Esecuzione di una query spaziale in un ciclo in PyQGIS


9

Quello che sto cercando di fare: ciclo attraverso un punto di shapefile e selezionare ogni punto che cade in un poligono.

Il codice seguente è ispirato a un esempio di query spaziale che ho trovato in un libro:

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

Funziona e seleziona i set di dati, ma il problema è che seleziona i limiti , quindi ovviamente restituendo i punti che non mi interessano:

inserisci qui la descrizione dell'immagine

Come potrei fare solo per restituire punti all'interno del poligono senza usare qgis: selectbylocation ?

Ho provato a usare i metodi Within () e intersects () , ma poiché non li stavo facendo funzionare, ho fatto ricorso al codice sopra. Ma forse sono la chiave dopo tutto.

Risposte:


10

Non hai bisogno di una funzione speciale (come "Ray Casting"), tutto è in PyQGIS ( contiene () 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" 

o in una riga

 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)
 ...

Puoi anche usare direttamente

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

Il problema qui è che è necessario scorrere tutte le geometrie (poligoni e punti). È più interessante utilizzare un indice spaziale delimitante: si esegue l'iterazione solo attraverso le geometrie che hanno la possibilità di intersecarsi con la geometria corrente ('filtro', vedere Come accedere in modo efficiente alle funzionalità restituite da QgsSpatialIndex? )



5

È possibile utilizzare l' algoritmo "Ray Casting" che ho leggermente adattato per l'utilizzo con PyQGIS:

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

Applicato a questa situazione:

inserisci qui la descrizione dell'immagine

il risultato, sulla console Python, era:

[2, 2]

Ha funzionato.

Nota di modifica:

Codice con la proposta più concisa di gene :

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

Ottimo riferimento e ottima risposta! Segnerò quello che ho appena pubblicato come soluzione, poiché è un po 'più facile da implementare. Dovresti essere ricompensato con molti voti però. +1 da me, di sicuro.
BritishSteel,

Non è necessario specificare if geo_pol.contains(geo_point) == True:perché è implicito if geo_pol.contains(geo_point)(sempre vero)
gene

3

Con alcuni consigli di un compagno di lavoro, finalmente sono riuscito a farlo usando in ().

Logica generale

  1. ottieni le caratteristiche dei poligoni
  2. ottenere funzionalità di punti
  3. scorrere ogni funzione dal file poligonale e per ciascuno:
    • ottenere la geometria
    • scorrere tutti i punti
      • ottenere la geometria del singolo punto
      • verifica se la geometria è all'interno della geometria del poligono

Ecco il codice:

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

Ciò funzionerebbe anche con intersects () anziché entro () . Quando si utilizzano i punti, non importa quale si utilizzerà, poiché entrambi restituiranno lo stesso risultato. Quando si controllano le linee / i poligoni, tuttavia, può fare una differenza importante: Within () restituisce oggetti che sono interamente all'interno, mentre intersechi () riavvia gli oggetti che sono interamente all'interno e che sono parzialmente all'interno (ovvero che si intersecano con la funzione, come il nome indica).

inserisci qui la descrizione dell'immagine


Ho provato la tua soluzione. Funziona solo se hai un poligono, altrimenti saranno selezionati solo i punti all'interno del primo poligono
ilFonta
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.