Come accedere in modo efficiente alle funzionalità restituite da QgsSpatialIndex?


9

Il ricettario PyQGIS spiega come impostare l'indice spaziale ma spiega solo la metà del suo utilizzo:

crea indice spaziale: il codice seguente crea un indice vuoto

index = QgsSpatialIndex()

aggiungi funzionalità all'indice: l'indice accetta l'oggetto QgsFeature e lo aggiunge alla struttura dati interna. È possibile creare manualmente l'oggetto o utilizzarne uno dalla chiamata precedente alla funzione successiva () del provider

index.insertFeature(feat)

una volta riempito l'indice spaziale con alcuni valori, è possibile eseguire alcune query

# returns array of feature IDs of five nearest features
nearest = index.nearestNeighbor(QgsPoint(25.4, 12.7), 5)

Qual è il passaggio più efficace per ottenere le funzionalità effettive che appartengono agli ID funzionalità restituiti?

Risposte:


12
    # assume a list of feature ids returned from index and a QgsVectorLayer 'lyr'
    fids = [1, 2, 4]
    request = QgsFeatureRequest()
    request.setFilterFids(fids)

    features = lyr.getFeatures(request)
    # can now iterate and do fun stuff:
    for feature in features:
        print feature.id(), feature

    1 <qgis._core.QgsFeature object at 0x000000000E987510>
    2 <qgis._core.QgsFeature object at 0x000000000E987400>
    4 <qgis._core.QgsFeature object at 0x000000000E987510>

Grazie! Snorfalorpagus ha affermato che setFilterFids sarebbe notevolmente più lento della soluzione che ha pubblicato. Lo confermi?
underdark

Non l'ho usato su grandi set di risultati, quindi non posso confermare.
gsherman,

1
Confermo e, nel mio caso, rtree è ancora più veloce di QgsSpatialIndex () (per la costruzione di grafici planari da strati di polilinee molto grandi, trasposizione del modulo PlanarGraph con Shapely in PyQGIS. Ma la soluzione con Fiona, Shapely e rtree è ancora la più veloce)
gene

1
Credo che la domanda riguardi l'ottenimento delle funzionalità effettive dagli ID delle funzionalità restituite anziché la velocità di vari metodi di indicizzazione.
gsherman,

7

In un post sul blog sull'argomento , Nathan Woodrow fornisce il seguente codice:

layer = qgis.utils.iface.activeLayer()

# Select all features along with their attributes
allAttrs = layer.pendingAllAttributesList()
layer.select(allAttrs)
# Get all the features to start
allfeatures = {feature.id(): feature for (feature) in layer}

def noindex():
    for feature in allfeatures.values():
        for f in allfeatures.values():
            touches = f.geometry().touches(feature.geometry())
            # It doesn't matter if we don't return anything it's just an example

def withindex():
    # Build the spatial index for faster lookup.
    index = QgsSpatialIndex()
    map(index.insertFeature, allfeatures.values())

    # Loop each feature in the layer again and get only the features that are going to touch.
    for feature in allfeatures.values():
        ids = index.intersects(feature.geometry().boundingBox())
        for id in ids:
            f = allfeatures[id]
            touches = f.geometry().touches(feature.geometry())
            # It doesn't matter if we don't return anything it's just an example

import timeit
print "With Index: %s seconds " % timeit.timeit(withindex,number=1)
print "Without Index: %s seconds " % timeit.timeit(noindex,number=1)

Questo crea un dizionario che consente di cercare rapidamente una funzionalità Qgs utilizzando il suo FID.

Ho scoperto che per strati molto grandi questo non è particolarmente pratico in quanto richiede molta memoria. Tuttavia, l'utilizzo alternativo (accesso casuale della funzione desiderata) layer.getFeatures(QgsFeatureRequest().setFilterFid(fid))sembra essere relativamente lento. Non sono sicuro del perché, poiché la chiamata equivalente che utilizza i binding SWIG OGR layer.GetFeature(fid)sembra molto più veloce di così.


1
L'uso di un dizionario è stato molto più veloce di layer.getFeatures(QgsFeatureRequest().setFilterFid(fid)). Stavo lavorando su un livello con funzionalità 140k e il tempo totale per le ricerche 140k è passato da molti minuti a secondi.
Håvard Tveite,

5

Per i confronti, guarda Join spaziale più efficiente in Python senza QGIS, ArcGIS, PostGIS, ecc . La soluzione presentata utilizza i moduli Python Fiona , Shapely e rtree (Spatial Index).

Con PyQGIS e lo stesso esempio due livelli pointe polygon:

inserisci qui la descrizione dell'immagine

1) Senza un indice spaziale:

polygons = [feature for feature in polygon.getFeatures()]
points = [feature for feature in point.getFeatures()]
for pt in points: 
    point = pt.geometry()
    for pl  in polygons:
        poly = pl.geometry()
        if poly.contains(point):
            print point.asPoint(), poly.asPolygon()
(184127,122472) [[(183372,123361), (184078,123130), (184516,122631),   (184516,122265), (183676,122144), (183067,122570), (183128,123105), (183372,123361)]]
(183457,122850) [[(183372,123361), (184078,123130), (184516,122631), (184516,122265), (183676,122144), (183067,122570), (183128,123105), (183372,123361)]]
(184723,124043) [[(184200,124737), (185368,124372), (185466,124055), (185515,123714), (184955,123580), (184675,123471), (184139,123787), (184200,124737)]]
(182179,124067) [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]

2) Con l' indice spaziale R-Tree PyQGIS:

# build the spatial index with all the polygons and not only a bounding box
index = QgsSpatialIndex()
for poly in polygons:
     index.insertFeature(poly)

# intersections with the index 
# indices of the index for the intersections
for pt in points:
    point = pt.geometry()
    for id in index.intersects(point.boundingBox()):
    print id
0
0
1
2

Cosa significano questi indici?

for i, pt in enumerate(points):
     point = pt.geometry()
     for id in index.intersects(point.boundingBox()):
        print "Point ", i, points[i].geometry().asPoint(), "is in Polygon ", id, polygons[id].geometry().asPolygon()
Point  1 (184127,122472) is in Polygon  0 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]
Point  2 (183457,122850) is in Polygon  0 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]
Point  4 (184723,124043) is in Polygon  1 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]
Point  6 (182179,124067) is in Polygon  2 [[(182520,125175), (183348,124286), (182605,123714), (182252,123544), (181753,123799), (181740,124627), (182520,125175)]]

Stesse conclusioni dell'adesione spaziale più efficiente in Python senza QGIS, ArcGIS, PostGIS, ecc .:

  • Senza e indicizzare, è necessario scorrere tutte le geometrie (poligoni e punti).
  • Con un indice spaziale delimitante (QgsSpatialIndex ()), si esegue l'iterazione solo attraverso le geometrie che hanno la possibilità di intersecarsi con la geometria corrente ('filtro' che può risparmiare una notevole quantità di calcoli e tempo ...).
  • È inoltre possibile utilizzare altri moduli Python indice spaziale ( R-tree , Pyrtree o quadtree ) con PyQGIS come in Utilizzo di un QGIS indice spaziale per velocizzare il codice (con QgsSpatialIndex () e R-tree )
  • ma un indice spaziale non è una bacchetta magica. Quando è necessario recuperare gran parte del set di dati, un indice spaziale non può offrire alcun vantaggio in termini di velocità.

Altro esempio in GIS: Come trovare la linea più vicina a un punto in QGIS? [duplicare]


Grazie per tutte le spiegazioni aggiuntive. Fondamentalmente, la tua soluzione utilizza un elenco anziché un dict come fece Snorfalorpagus. Quindi sembra davvero che non ci sia alcuna funzione layer.getFeatures ([ids]) ...
underdark

Lo scopo di questa spiegazione è puramente geometrico ed è molto semplice aggiungere un layer.getFeatures ([ids]) funziona come in Join spaziale più efficiente in Python senza QGIS, ArcGIS, PostGIS, ecc.
gene

0

Apparentemente l'unico metodo per ottenere buone prestazioni è evitare o raggruppare le chiamate a layer.getFeatures (), anche se il filtro è semplice come un fid.

Ora, ecco la trappola: chiamare getFeatures è costoso. Se lo si chiama su un livello vettoriale, verrà richiesto QGIS per impostare una nuova connessione all'archivio dati (il fornitore del livello), creare una query per restituire i dati e analizzare ogni risultato man mano che viene restituito dal fornitore. Questo può essere lento, specialmente se stai lavorando con un qualche tipo di livello remoto, come una tabella PostGIS su una connessione VPN.

fonte: http://nyalldawson.net/2016/10/speeding-up-your-pyqgis-scripts/

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.