Überprüfen Sie mit Python, ob ein Punkt in ein Multipolygon fällt

13

Ich habe mehrere Codebeispiele mit Bibliotheken wie Shapefile, Fiona und Ogr ausprobiert, um zu überprüfen, ob ein Punkt (x, y) innerhalb der Grenzen eines mit ArcMap erstellten Multipolygons (und damit im Shapefile-Format) liegt. Keines der Beispiele funktioniert jedoch gut mit Multipolygonen, obwohl sie mit regulären Einzelpolygon-Shapefiles gut funktionieren. Einige Ausschnitte, die ich ausprobiert habe, sind unten aufgeführt:

# 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'

Das erste Beispiel funktioniert gut mit jeweils einem einzelnen Polygon. Wenn ich jedoch einen Punkt innerhalb einer der Formen in meinem Multipolygon-Shapefile eingebe, wird "out" zurückgegeben, obwohl er in einen der vielen Teile fällt.

Für das zweite Beispiel erhalte ich den Fehler "Objekt vom Typ 'Geometrie' hat kein len ()", von dem ich annehme, dass das Geometriefeld nicht als normale, indizierte Liste / Array gelesen werden kann.

Ich habe außerdem versucht, den tatsächlichen Punkt im Polygoncode wie hier vorgeschlagen zu ersetzen , um sicherzustellen, dass nicht dieser Teil des Codes nicht funktioniert hat. Und während die Beispiele dieses Links mit einfachen Polygon-Shapefiles gut funktionieren, kann ich mein komplexes Multipolygon nicht richtig testen.

Ich kann mir also keine andere Möglichkeit vorstellen, um zu testen, ob ein Punkt über Python in ein Multipolygon-Shapefile fällt ... Vielleicht fehlen mir noch andere Bibliotheken?

spartmar
quelle
Ihr zweites Beispiel sieht so aus, als würde es Multipolygon zu Polygon zwingen? Möglicherweise wird der Punkt nur mit dem ersten Teil des Multipolygons verglichen. Versuchen Sie, den Punkt auf verschiedene Teile zu verschieben, und prüfen Sie, ob die Prüfung jemals erfolgreich war.
obrl_soil
@obrl_soil Vielen Dank für Ihren Vorschlag. Das zweite Beispiel funktioniert jedoch nie aufgrund der oben beschriebenen Fehlermeldung (Objekt vom Typ 'Geometrie' hat kein len ()) "ob ich MultiPolygon (Geometrie) oder einfach Polygon (Geometrie) versuche. Ich habe viele Punkte in der erstes Beispiel und nur diejenigen innerhalb des Hauptpolygons arbeiten. Hoffe, diese Klarstellung hilft.
Spartmar
Ja, ich denke, Sie müssen durch polygon = Polygon(geometry)eine Art Try-Schleife ersetzen, in die umgeschaltet wird, polygon = MultiPolygon(geometry)wenn dieser Fehler auftritt.
obrl_soil
Das Problem in Ihrem ersten Beispiel liegt in der ersten Schleife.
Xunilk

Antworten:

24

Shapefiles haben keinen Typ MultiPolygon (Typ = Polygon), aber sie unterstützen sie trotzdem (alle Ringe werden in einem Feature gespeichert = Liste der Polygone, siehe Konvertieren eines großen Multipolygons in Polygone )

Das Problem

Geben Sie hier die Bildbeschreibung ein

Wenn ich ein MultiPolygon-Shapefile öffne, lautet die Geometrie "Polygon".

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

Lösung 1 mit 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 interpretiert die Funktion als MultiPolygon und Sie können die in Python für effizientere räumliche Verknüpfung vorgestellte Lösung ohne QGIS, ArcGIS, PostGIS usw. anwenden. (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)

Lösung 2 mit pyshp (Shapefile) und dem Protokoll geo_interface (GeoJSON like)

Dies ist eine Ergänzung zur Antwort von 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)

Lösung 3 mit ogr und dem geo_interface- Protokoll ( Python Geo_interface-Anwendungen )

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)

Lösung 4 mit GeoPandas wie in Efficient Spatial Join in Python ohne QGIS, ArcGIS, PostGIS usw. (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]} 

Die Punkte 1,3,5,6 liegen innerhalb der Grenzen des MultiPolygons

Gen
quelle
Etwas alter Thread hier, aber wie ruft man multi = shape(polys.shapeRecords()[0].shape.__geo_interface__)Lösung 2 auf? Ich kann keine Aufrufe einer shape () -Methode von erhalten shapefile.py. Ich habe es sogar versucht shapefile.Shape(); Es gibt eine Klasse dafür, aber es funktioniert nicht.
Pstatix
Woher bekommen Sie die within()Methode?
Pstatix
1
von Shapely ( from shapely.geometry import shape,mapping, Point, Polygon, MultiPolygon)
Gen
Ich erhalte diesen Fehler mit Lösung 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

Das Problem in Ihrem ersten Beispiel liegt in dieser Schleife:

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

Es werden nur die letzten Feature-Punkte angehängt. Ich habe meinen Ansatz mit diesem Shapefile ausprobiert:

Geben Sie hier die Bildbeschreibung ein

Ich habe Ihren Code geändert in:

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

Der obige Code wurde in der Python-Konsole von QGIS ausgeführt und das Ergebnis war:

Geben Sie hier die Bildbeschreibung ein

Es funktioniert perfekt und jetzt können Sie überprüfen, ob ein Punkt (x, y) innerhalb der Grenzen jedes Features liegt.

xunilk
quelle