检查具有纬度和经度的地理位置是否在shapefile中

时间:2011-10-22 17:10:40

标签: python geolocation geocoding geospatial shapefile

如何检查某个地理位置是否在给定shapefile的区域内?

我设法在python中加载一个shapefile,但是无法再进一步了。

7 个答案:

答案 0 :(得分:30)

另一种选择是使用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):
    ...

最后,请记住,加载和解析大型/不规则的shapefile需要一些时间(不幸的是,这些类型的多边形通常也很难保存在内存中)。

答案 1 :(得分:16)

这是对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)

This sitethis sitethis site对投影检查很有帮助。 EPSG:4326

答案 2 :(得分:8)

以下是基于pyshpshapely的简单解决方案。

让我们假设你的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)

答案 3 :(得分:2)

我使用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))

答案 4 :(得分:1)

执行此操作的一种方法是使用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.orghttp://www.vividsolutions.com/products.aspjts.jar}。您只需要一个文本命令 文件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/

答案 5 :(得分:1)

答案 6 :(得分:0)

如果你想找出哪个多边形(来自一个完整的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))