Durchführen einer räumlichen Abfrage in einer Schleife in PyQGIS


9

Was ich versuche: Durchlaufen Sie ein Punkt-Shapefile und wählen Sie jeden Punkt aus, der in ein Polygon fällt .

Der folgende Code ist von einem Beispiel für räumliche Abfragen inspiriert, das ich in einem Buch gefunden habe:

mitte_path = r"D:\PythonTesting\SelectByLocation\mitte.shp"
punkte_path = r"D:\PythonTesting\SelectByLocation\punkte.shp"

polygon = QgsVectorLayer(mitte_path, 'Mitte', 'ogr')
points = QgsVectorLayer(punkte_path, 'Berlin Punkte', 'ogr')

QgsMapLayerRegistry.instance().addMapLayer(polygon)
QgsMapLayerRegistry.instance().addMapLayer(points)

polyFeatures = polygon.getFeatures()

pointsCount = 0

for poly_feat in polyFeatures:
    polyGeom = poly_feat.geometry()
    pointFeatures = points.getFeatures(QgsFeatureRequest().setFilterRect(polyGeom.boundingBox()))
    for point_feat in pointFeatures:
        points.select(point_feat.id())
        pointsCount += 1

print 'Total:',pointsCount

Dies funktioniert und es werden Datensätze ausgewählt, aber das Problem ist, dass es durch einen Begrenzungsrahmen ausgewählt wird , wodurch offensichtlich Punkte zurückgegeben werden, an denen ich nicht interessiert bin:

Geben Sie hier die Bildbeschreibung ein

Wie kann ich nur Punkte innerhalb des Polygons zurückgeben, ohne qgis: selectbylocation zu verwenden ?

Ich habe versucht, die Methoden inside () und intersects () zu verwenden, aber da ich sie nicht zum Laufen gebracht habe, habe ich auf den obigen Code zurückgegriffen. Aber vielleicht sind sie doch der Schlüssel.

Antworten:


10

Sie benötigen keine spezielle Funktion (wie "Ray Casting"), alles ist in PyQGIS ( enthält () in PyQGIS Geometry Handling )

polygons = [feature for feature in polygons.getFeatures()]
points = [feature for feature in points.getFeatures()]
for pt in points: 
     point = pt.geometry() # only and not pt.geometry().asPolygon() 
     for pol in polygons:
        poly = pol.geometry()
        if poly.contains(point):
             print "ok" 

oder in einer Zeile

 polygons = [feature for feature in polygons.getFeatures()]
 points = [feature for feature in points.getFeatures()]
 resulting = [pt for pt in points for poly in polygons if poly.geometry().contains(pt.geometry())]
 print len(resulting)
 ...

Sie können auch direkt verwenden

[pt.geometry().asPoint() for pt in points for poly in polygons if poly.geometry().contains(pt.geometry())]

Das Problem hierbei ist, dass Sie alle Geometrien (Polygone und Punkte) durchlaufen müssen. Es ist interessanter, einen begrenzten räumlichen Index zu verwenden: Sie durchlaufen nur die Geometrien, die sich möglicherweise mit Ihrer aktuellen Geometrie überschneiden ('Filter', siehe So greifen Sie effizient auf die von QgsSpatialIndex zurückgegebenen Features zu? ).



5

Sie können den "Ray Casting" -Algorithmus verwenden , den ich für die Verwendung mit PyQGIS leicht angepasst habe:

def point_in_poly(point,poly):
    x = point.x()
    y = point.y()

    n = len(poly)
    inside = False

    p1x,p1y = poly[0]
    for i in range(n+1):
        p2x,p2y = poly[i % n]
        if y > min(p1y,p2y):
            if y <= max(p1y,p2y):
                if x <= max(p1x,p2x):
                    if p1y != p2y:
                        xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
                    if p1x == p2x or x <= xints:
                        inside = not inside
        p1x,p1y = p2x,p2y

    return inside

## Test
mapcanvas = iface.mapCanvas()

layers = mapcanvas.layers()

#For polygon 
polygon = [feature.geometry().asPolygon() 
            for feature in layers[1].getFeatures()]

points = [feat.geometry().asPoint() 
           for feat in layers[0].getFeatures()]

## Call the function with the points and the polygon
count = [0]*(layers[1].featureCount())

for point in points:
    i = 0
    for feat in polygon:
        if point_in_poly(point, feat[0]) == True:
            count[i] += 1
        i += 1

print count

Auf diese Situation angewendet:

Geben Sie hier die Bildbeschreibung ein

Das Ergebnis an der Python-Konsole war:

[2, 2]

Es funktionierte.

Bearbeitungshinweis:

Code mit dem prägnanteren Vorschlag des Gens :

mapcanvas = iface.mapCanvas()

layers = mapcanvas.layers()

count = [0]*(layers[1].featureCount())

polygon = [feature
           for feature in layers[1].getFeatures()]

points = [feature
          for feature in layers[0].getFeatures()]

for point in points:

    i = 0

    geo_point = point.geometry()

    for pol in polygon:
        geo_pol = pol.geometry()

        if geo_pol.contains(geo_point):
            count[i] += 1
        i += 1

print count

Tolle Referenz und tolle Antwort! Ich werde die soeben veröffentlichte jedoch als Lösung markieren, da sie etwas einfacher zu implementieren ist. Sie sollten jedoch mit vielen positiven Stimmen belohnt werden. +1 von mir sicher.
BritishSteel

Sie müssen nicht angeben, if geo_pol.contains(geo_point) == True:da es in if geo_pol.contains(geo_point)(immer True)
Gen

3

Mit einigen Ratschlägen eines Arbeitskollegen habe ich es endlich geschafft, mit inside () zu arbeiten.

Allgemeine Logik

  1. Funktionen von Polygonen abrufen
  2. Merkmale von Punkten erhalten
  3. Durchlaufen Sie jedes Feature aus der Polygondatei und für jedes:
    • Geometrie erhalten
    • Schleife durch alle Punkte
      • Geometrie des Einzelpunkts abrufen
      • Testen Sie, ob die Geometrie innerhalb der Geometrie des Polygons liegt

Hier ist der Code:

mitte_path = r"D:\PythonTesting\SelectByLocation\mitte.shp"
punkte_path = r"D:\PythonTesting\SelectByLocation\punkte.shp"

poly = QgsVectorLayer(mitte_path, 'Mitte', 'ogr')
points = QgsVectorLayer(punkte_path, 'Berlin Punkte', 'ogr')

QgsMapLayerRegistry.instance().addMapLayer(poly)
QgsMapLayerRegistry.instance().addMapLayer(points)

polyFeatures = poly.getFeatures()
pointFeatures = points.getFeatures()

pointCounter = 0

for polyfeat in polyFeatures:
    polyGeom = polyfeat.geometry()
    for pointFeat in pointFeatures:
        pointGeom = pointFeat.geometry()
        if pointGeom.within(polyGeom):
            pointCounter += 1
            points.select(pointFeat.id())

print 'Total',pointCounter

Dies würde auch mit intersects () anstelle von inside () funktionieren . Bei der Verwendung von Punkten spielt es keine Rolle, welche Sie verwenden würden, da beide das gleiche Ergebnis zurückgeben. Wenn für die Zeilen / Polygonen Überprüfung kann es jedoch einen wichtigen Unterschied: innerhalb von Objekten () zurückkehrt, die vollständig innerhalb, während intersects () Objekte Retuns die innerhalb vollständig sind und , die teilweise innerhalb (dh , dass sich schneiden mit dem Merkmal, als der Name gibt an).

Geben Sie hier die Bildbeschreibung ein


Ich habe deine Lösung ausprobiert. Es funktioniert nur, wenn Sie ein Polygon haben, sonst werden nur die Punkte innerhalb des ersten Polygons ausgewählt
ilFonta
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.