【发布时间】:2011-12-13 06:08:27
【问题描述】:
如何检查地理点是否在给定 shapefile 的区域内?
我设法在 python 中加载了一个 shapefile,但无法继续。
【问题讨论】:
标签: python geolocation geocoding geospatial shapefile
如何检查地理点是否在给定 shapefile 的区域内?
我设法在 python 中加载了一个 shapefile,但无法继续。
【问题讨论】:
标签: python geolocation geocoding geospatial shapefile
另一种选择是使用 Shapely(基于 GEOS 的 Python 库,PostGIS 的引擎)和 Fiona(主要用于读取/写入文件):
import fiona
import shapely
with fiona.open("path/to/shapefile.shp") as fiona_collection:
# In this case, we'll assume the shapefile only has one record/layer (e.g., the shapefile
# is just for the borders of a single country, etc.).
shapefile_record = fiona_collection.next()
# Use Shapely to create the polygon
shape = shapely.geometry.asShape( shapefile_record['geometry'] )
point = shapely.geometry.Point(32.398516, -39.754028) # longitude, latitude
# Alternative: if point.within(shape)
if shape.contains(point):
print "Found shape for point."
请注意,如果多边形很大/很复杂(例如,某些海岸线极不规则的国家/地区的 shapefile),则进行多边形内点测试可能会很昂贵。在某些情况下,它可以帮助在进行更密集的测试之前使用边界框快速排除问题:
minx, miny, maxx, maxy = shape.bounds
bounding_box = shapely.geometry.box(minx, miny, maxx, maxy)
if bounding_box.contains(point):
...
最后,请记住,加载和解析大型/不规则形状文件需要一些时间(不幸的是,这些类型的多边形在内存中的保存成本通常也很高)。
【讨论】:
shapely / fiona 比使用ogr 容易得多。我发现还有一件事很有用:如果您在单个 shapefile 中使用多个图层/功能,您可以迭代集合中的元素,使用 asShape 方法并返回正确的功能(如果它包含您的观点)。这也可用于保留特征上可能存在的任何属性(如名称和代码)。
这是对 yosukesabai 回答的改编。
我想确保我正在搜索的点与 shapefile 在同一个投影系统中,所以我为此添加了代码。
我不明白他为什么要对 ply = feat_in.GetGeometryRef() 进行包含测试(在我的测试中,没有它似乎也能正常工作),所以我删除了它。
我还改进了评论以更好地解释正在发生的事情(据我了解)。
#!/usr/bin/python
import ogr
from IPython import embed
import sys
drv = ogr.GetDriverByName('ESRI Shapefile') #We will load a shape file
ds_in = drv.Open("MN.shp") #Get the contents of the shape file
lyr_in = ds_in.GetLayer(0) #Get the shape file's first layer
#Put the title of the field you are interested in here
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("P_Loc_Nm")
#If the latitude/longitude we're going to use is not in the projection
#of the shapefile, then we will get erroneous results.
#The following assumes that the latitude longitude is in WGS84
#This is identified by the number "4326", as in "EPSG:4326"
#We will create a transformation between this and the shapefile's
#project, whatever it may be
geo_ref = lyr_in.GetSpatialRef()
point_ref=ogr.osr.SpatialReference()
point_ref.ImportFromEPSG(4326)
ctran=ogr.osr.CoordinateTransformation(point_ref,geo_ref)
def check(lon, lat):
#Transform incoming longitude/latitude to the shapefile's projection
[lon,lat,z]=ctran.TransformPoint(lon,lat)
#Create a point
pt = ogr.Geometry(ogr.wkbPoint)
pt.SetPoint_2D(0, lon, lat)
#Set up a spatial filter such that the only features we see when we
#loop through "lyr_in" are those which overlap the point defined above
lyr_in.SetSpatialFilter(pt)
#Loop through the overlapped features and display the field of interest
for feat_in in lyr_in:
print lon, lat, feat_in.GetFieldAsString(idx_reg)
#Take command-line input and do all this
check(float(sys.argv[1]),float(sys.argv[2]))
#check(-95,47)
【讨论】:
假设您的 shapefile 仅包含一个多边形(但您可以轻松适应多个多边形):
import shapefile
from shapely.geometry import shape, Point
# read your shapefile
r = shapefile.Reader("your_shapefile.shp")
# get the shapes
shapes = r.shapes()
# build a shapely polygon from your shape
polygon = shape(shapes[0])
def check(lon, lat):
# build a shapely point from your geopoint
point = Point(lon, lat)
# the contains function does exactly what you want
return polygon.contains(point)
【讨论】:
我使用 gdal 的 ogr 和 python 绑定几乎完成了你昨天所做的事情。看起来像这样。
import ogr
# load the shape file as a layer
drv = ogr.GetDriverByName('ESRI Shapefile')
ds_in = drv.Open("./shp_reg/satreg_etx12_wgs84.shp")
lyr_in = ds_in.GetLayer(0)
# field index for which i want the data extracted
# ("satreg2" was what i was looking for)
idx_reg = lyr_in.GetLayerDefn().GetFieldIndex("satreg2")
def check(lon, lat):
# create point geometry
pt = ogr.Geometry(ogr.wkbPoint)
pt.SetPoint_2D(0, lon, lat)
lyr_in.SetSpatialFilter(pt)
# go over all the polygons in the layer see if one include the point
for feat_in in lyr_in:
# roughly subsets features, instead of go over everything
ply = feat_in.GetGeometryRef()
# test
if ply.Contains(pt):
# TODO do what you need to do here
print(lon, lat, feat_in.GetFieldAsString(idx_reg))
【讨论】:
一种方法是使用 OGR 读取 ESRI Shape 文件 库http://www.gdal.org/ogr 然后使用 GEOS 几何 库http://trac.osgeo.org/geos/ 进行多边形点测试。 这需要一些 C/C++ 编程。
http://sgillies.net/blog/14/python-geos-module/ 也有一个与 GEOS 的 python 接口(我从未使用过)。也许这就是你想要的?
另一个解决方案是使用http://geotools.org/ 库。 那是在 Java 中。
我也有自己的 Java 软件来执行此操作(您可以下载
来自http://www.mapyrus.org 加上jts.jar 来自http://www.vividsolutions.com/products.asp)。您只需要一个文本命令
文件inside.mapyrus 包含
以下几行检查一个点是否位于
ESRI Shape 文件中的第一个多边形:
dataset "shapefile", "us_states.shp"
fetch
print contains(GEOMETRY, -120, 46)
然后运行:
java -cp mapyrus.jar:jts-1.8.jar org.mapyrus.Mapyrus inside.mapyrus
如果点在内部,则打印 1,否则打印 0。
如果您将这个问题发布到 https://gis.stackexchange.com/
【讨论】:
如果你想找出哪个多边形(从一个充满它们的 shapefile 中)包含一个给定的点(你也有一堆点),最快的方法是使用 postgis。我实际上实现了一个基于 fiona 的版本,使用这里的答案,但它非常慢(我使用的是多处理并首先检查边界框)。 400 分钟的处理时间 = 50k 点。使用 postgis,不到 10 秒。 B树索引很高效!
shp2pgsql -s 4326 shapes.shp > shapes.sql
这将使用 shapefile 中的信息生成一个 sql 文件,创建一个支持 postgis 的数据库并运行该 sql。在 geom 列上创建一个 gist 索引。然后,找到多边形的名称:
sql="SELECT name FROM shapes WHERE ST_Contains(geom,ST_SetSRID(ST_MakePoint(%s,%s),4326));"
cur.execute(sql,(x,y))
【讨论】: