【问题标题】:Check if a geopoint with latitude and longitude is within a shapefile检查具有纬度和经度的地理点是否在 shapefile 中
【发布时间】:2011-12-13 06:08:27
【问题描述】:

如何检查地理点是否在给定 shapefile 的区域内?

我设法在 python 中加载了一个 shapefile,但无法继续。

【问题讨论】:

    标签: python geolocation geocoding geospatial shapefile


    【解决方案1】:

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

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

    【讨论】:

    • 我有很多点和很多多边形(每个 2000 个)。找到每个点的每个多边形的快速方法是什么?我在开发代码时遇到问题。
    • 很好的答案,使用shapely / fiona 比使用ogr 容易得多。我发现还有一件事很有用:如果您在单个 shapefile 中使用多个图层/功能,您可以迭代集合中的元素,使用 asShape 方法并返回正确的功能(如果它包含您的观点)。这也可用于保留特征上可能存在的任何属性(如名称和代码)。
    【解决方案2】:

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

    【讨论】:

    • 感谢您改进答案。我记得我当时只是从我的工作中复制代码,并且留下了一些肮脏的东西,就像我为其他目的所做的那样。我试图清理它以便在此处发布,但是当您在编辑中选择时,这也引入了一些错误。
    • 没问题,@yosukesabai,非常感谢您指路。似乎缺少这些软件包的文档和示例。你对this question有什么想法吗?
    • gdal 的数据集对象似乎具有 GetProjection() 方法,该方法似乎为投影返回众所周知的文本 (WKT)。我想你可以在 osr 中以某种方式将其转换为投影对象。
    • WGS84 是 4326 所以这应该是 point_ref.ImportFromEPSG(4326),这样写的代码会导致形状文件读取不正确。
    • 感谢您编辑它,@thagorn。这是一个愚蠢的错误! 4236是“虎子山1950”投影。 4326 绝对正确。
    【解决方案3】:

    这是一个基于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)
    

    【讨论】:

    • Fiona 也是 pyshp 的一个很好的替代品,并且可以轻松解析 shapefile 并获得不同的多边形。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

      一种方法是使用 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/

      【讨论】:

        【解决方案6】:
        【解决方案7】:

        如果你想找出哪个多边形(从一个充满它们的 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))
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-26
        • 2011-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-08
        • 2020-08-27
        • 2017-01-07
        相关资源
        最近更新 更多