使用Python检查点是否在多多边形内


13

我已尝试使用诸如shapefile,fiona和ogr之类的库的几个代码示例,以尝试检查点(x,y)是否落在用ArcMap创建的多面体的边界内(从而以shapefile格式)。但是,尽管这些示例对于常规的单个多边形shapefile也能很好地工作,但是它们都不能很好地与multipolygons配合使用。我尝试过的一些代码片段如下:

# First example using shapefile and shapely:
from shapely.geometry import Polygon, Point, MultiPolygon
import shapefile

polygon = shapefile.Reader('shapefile.shp') 
polygon = polygon.shapes()  
shpfilePoints = []
for shape in polygon:
    shpfilePoints = shape.points 
polygon = shpfilePoints 
poly = Polygon(poly)

point = Point(x, y)
# point in polygon test
if polygon.contains(point):
    print 'inside'
else:
    print 'OUT'


# Second example using ogr and shapely:
from shapely.geometry import Polygon, Point, MultiPolygon
from osgeo import ogr, gdal

driver = ogr.GetDriverByName('ESRI Shapefile')
dataset = driver.Open("shapefile.shp", 0)

layer = dataset.GetLayer()
for index in xrange(layer.GetFeatureCount()):
    feature = layer.GetFeature(index)
    geometry = feature.GetGeometryRef()

polygon = Polygon(geometry)
print 'polygon points =', polygon  # this prints 'multipoint' + all the points fine

point = Point(x, y)
# point in polygon test
if polygon.contains(point):
    print 'inside'
else:
    print 'OUT'

第一个示例一次只适用于一个多边形,但是当我在多多边形shapefile中的一个形状中输入一个点时,即使它确实落在许多部分中的一个中,它也会返回“ out”。

对于第二个示例,我得到一个错误“类型为'Geometry'的对象没有len()”,我认为这是因为几何字段不能作为常规的索引列表/数组读取。

我还尝试按照此处的建议替换多边形代码中的实际点,以确保不是那部分代码不起作用。而且,尽管该链接的示例可与简单的多边形shapefile正常工作,但我无法让我的复杂多多边形正确测试。

因此,我想不出任何其他方法来通过python测试点是否落在多多边形shapefile中...也许还有其他库我缺少了?


您的第二个示例似乎将多面体强制为多边形?它可能仅是根据多边形的第一部分来检查点。尝试将点移动到不同的部分,然后查看检查是否成功。
obrl_soil

@obrl_soil谢谢您的建议。但是,第二个示例由于我上面描述的错误消息而无法工作(无论我尝试使用MultiPolygon(几何)还是简单地使用Polygon(几何),类型为“ Geometry”的对象都没有len())”。第一个示例,只有主要多边形中的示例有效,希望此说明对您
有所

是的,我认为您需要polygon = Polygon(geometry)用某种try循环替换,polygon = MultiPolygon(geometry)如果发生该错误,它将切换到该try循环。
obrl_soil

第一个示例中的问题在第一个循环中。
xunilk

Answers:


24

Shapefile没有类型MultiPolygon(类型=多边形),但是无论如何它们都支持它们(所有环都存储在一个特征=多边形列表中,请看将巨大的多面多边形转换为多边形

问题

在此处输入图片说明

如果我打开MultiPolygon形状文件,则几何图形为“多边形”

multipolys = fiona.open("multipol.shp")
multipolys.schema
{'geometry': 'Polygon', 'properties': OrderedDict([(u'id', 'int:10')])}
len(multipolys)
1

解决方案1与Fiona

import fiona
from shapely.geometry import shape,mapping, Point, Polygon, MultiPolygon
multipol = fiona.open("multipol.shp")
multi= multipol.next() # only one feature in the shapefile
print multi
{'geometry': {'type': 'MultiPolygon', 'coordinates': [[[(-0.5275288092189501, 0.5569782330345711), (-0.11779769526248396, 0.29065300896286816), (-0.25608194622279135, 0.01920614596670933), (-0.709346991037132, -0.08834827144686286), (-0.8629961587708066, 0.18309859154929575), (-0.734955185659411, 0.39820742637644047), (-0.5275288092189501, 0.5569782330345711)]], [[(0.19974391805377723, 0.060179257362355965), (0.5480153649167734, 0.1293213828425096), (0.729833546734955, 0.03969270166453265), (0.8143405889884763, -0.13956466069142115), (0.701664532650448, -0.38540332906530095), (0.4763124199743918, -0.5006402048655569), (0.26888604353393086, -0.4238156209987196), (0.18950064020486557, -0.2291933418693981), (0.19974391805377723, 0.060179257362355965)]], [[(-0.3764404609475033, -0.295774647887324), (-0.11523687580025621, -0.3597951344430217), (-0.033290653008962945, -0.5800256081946222), (-0.11523687580025621, -0.7413572343149808), (-0.3072983354673495, -0.8591549295774648), (-0.58898847631242, -0.6927016645326505), (-0.6555697823303457, -0.4750320102432779), (-0.3764404609475033, -0.295774647887324)]]]}, 'type': 'Feature', 'id': '0', 'properties': OrderedDict([(u'id', 1)])}

Fiona将该功能解释为MultiPolygon,您可以在不使用QGIS,ArcGIS,PostGIS等的情况下使用Python中的“ 更高效的空间连接”中介绍的解决方案(1)

points= ([pt for pt  in fiona.open("points.shp")])
for i, pt in enumerate(points):
    point = shape(pt['geometry'])
    if point.within(shape(multi['geometry'])):
         print i, shape(points[i]['geometry'])
1 POINT (-0.58898847631242 0.17797695262484)
3 POINT (0.4993597951344431 -0.06017925736235585)
5 POINT (-0.3764404609475033 -0.4750320102432779)
6 POINT (-0.3098591549295775 -0.6312419974391805)

解决方案2使用pyshp(shapefile)geo_interface(类似GeoJSON)协议

这是对xulnik答案的补充。

import shapefile
pts = shapefile.Reader("points.shp")
polys = shapefile.Reader("multipol.shp")
points = [pt.shape.__geo_interface__ for pt in pts.shapeRecords()]
multi = shape(polys.shapeRecords()[0].shape.__geo_interface__) # 1 polygon
print multi
MULTIPOLYGON (((-0.5275288092189501 0.5569782330345711, -0.117797695262484 0.2906530089628682, -0.2560819462227913 0.01920614596670933, -0.7093469910371319 -0.08834827144686286, -0.8629961587708066 0.1830985915492958, -0.734955185659411 0.3982074263764405, -0.5275288092189501 0.5569782330345711)), ((0.1997439180537772 0.06017925736235596, 0.5480153649167734 0.1293213828425096, 0.729833546734955 0.03969270166453265, 0.8143405889884763 -0.1395646606914211, 0.701664532650448 -0.3854033290653009, 0.4763124199743918 -0.5006402048655569, 0.2688860435339309 -0.4238156209987196, 0.1895006402048656 -0.2291933418693981, 0.1997439180537772 0.06017925736235596)), ((-0.3764404609475033 -0.295774647887324, -0.1152368758002562 -0.3597951344430217, -0.03329065300896294 -0.5800256081946222, -0.1152368758002562 -0.7413572343149808, -0.3072983354673495 -0.8591549295774648, -0.58898847631242 -0.6927016645326505, -0.6555697823303457 -0.4750320102432779, -0.3764404609475033 -0.295774647887324)))
for i, pt in enumerate(points):
    point = shape(pt)
    if point.within(multi): 
        print i, shape(points[i])
1 POINT (-0.58898847631242 0.17797695262484)
3 POINT (0.4993597951344431 -0.06017925736235585)
5 POINT (-0.3764404609475033 -0.4750320102432779)
6 POINT (-0.3098591549295775 -0.6312419974391805)

使用ogr和geo_interface协议的解决方案3 (Python Geo_interface应用程序

from osgeo import ogr
import json
def records(file):  
    # generator 
    reader = ogr.Open(file)
    layer = reader.GetLayer(0)
    for i in range(layer.GetFeatureCount()):
        feature = layer.GetFeature(i)
        yield json.loads(feature.ExportToJson())

points  = [pt for pt in records("point_multi_contains.shp")]
multipol = records("multipol.shp")
multi = multipol.next() # 1 feature
for i, pt in enumerate(points):
     point = shape(pt['geometry'])
     if point.within(shape(multi['geometry'])):
          print i, shape(points[i]['geometry'])

1 POINT (-0.58898847631242 0.17797695262484)
3 POINT (0.499359795134443 -0.060179257362356)
5 POINT (-0.376440460947503 -0.475032010243278)
6 POINT (-0.309859154929577 -0.631241997439181)

在没有QGIS,ArcGIS,PostGIS等的Python中使用GeoPandas解决方案4,如Python中的高效空间连接(2)

import geopandas
point = geopandas.GeoDataFrame.from_file('points.shp') 
poly  = geopandas.GeoDataFrame.from_file('multipol.shp')
from geopandas.tools import sjoin
pointInPolys = sjoin(point, poly, how='left')
grouped = pointInPolys.groupby('index_right')
list(grouped)
[(0.0,      geometry                               id_left  index_right id_right  

1  POINT (-0.58898847631242 0.17797695262484)       None      0.0        1.0 
3  POINT (0.4993597951344431 -0.06017925736235585)  None      0.0        1.0
5  POINT (-0.3764404609475033 -0.4750320102432779)  None      0.0        1.0 
6  POINT (-0.3098591549295775 -0.6312419974391805)  None      0.0        1.0 ]
print grouped.groups
{0.0: [1, 3, 5, 6]} 

点1,3,5,6落在MultiPolygon的边界内


这里的线程略旧,但是如何multi = shape(polys.shapeRecords()[0].shape.__geo_interface__)在解决方案2中调用?我无法从调用shape()方法shapefile.py。我甚至尝试过shapefile.Shape(); 有一个类,但它不起作用。
pstatix '17

此外,您within()从哪里获得该方法?
pstatix '17

1
来自Shapely(from shapely.geometry import shape,mapping, Point, Polygon, MultiPolygon
基因

我使用解决方案4收到此错误:File "C:\WinPython\python-3.6.5.amd64\lib\site-packages\geopandas\tools\sjoin.py", line 43, in sjoin if left_df.crs != right_df.crs: AttributeError: 'MultiPolygon' object has no attribute 'crs'
Aaron Bramson

6

您的第一个示例中的问题在此循环中:

...
shpfilePoints = []
for shape in polygon:
    shpfilePoints = shape.points
...

它仅附加最后的特征点。我用这个shapefile尝试了我的方法:

在此处输入图片说明

我将您的代码修改为:

from shapely.geometry import Polygon, Point, MultiPolygon
import shapefile 

path = '/home/zeito/pyqgis_data/polygon8.shp'

polygon = shapefile.Reader(path) 

polygon = polygon.shapes() 

shpfilePoints = [ shape.points for shape in polygon ]

print shpfilePoints

polygons = shpfilePoints

for polygon in polygons:
    poly = Polygon(polygon)
    print poly

上面的代码在QGIS的Python控制台上运行,结果是:

在此处输入图片说明

它可以完美运行,现在,您可以检查点(x,y)是否落在每个要素的边界内。


0

如果要检查多边形内的经度和纬度点,请确保通过以下方法创建点对象:

from shapely.geometry.point import Point
Point(LONGITUDE, LATITUDE)
..
poly.within(point) # Returns true if the point within the 

点取经度,然后在参数中取纬度。首先不是纬度。您可以调用polygon_object.within函数来检查点是否在形状内。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.