Comprensione dell'uso degli indici spaziali con RTree?


13

Ho difficoltà a comprendere l'uso degli indici spaziali con RTree.

Esempio: ho 300 punti bufferizzati e ho bisogno di conoscere l'area di intersezione di ciascun buffer con un file di forma poligonale. Il file di forma poligonale ha> 20.000 poligoni. Mi è stato suggerito di utilizzare indici spaziali per accelerare il processo.

Quindi ... Se creo un indice spaziale per il mio shapefile del poligono, sarà "allegato" al file in qualche modo o l'indice rimarrà da solo? Cioè, dopo averlo creato, posso semplicemente eseguire la mia funzione di intersezione sul file poligonale e ottenere risultati più veloci? L'intersezione "vedrà" l'esistenza di indici spaziali e saprà cosa fare? Oppure, devo eseguirlo sull'indice, quindi ricollegare quei risultati al mio file poligonale originale tramite FID o alcuni di questi?

La documentazione di RTree non mi aiuta molto (probabilmente perché sto solo imparando la programmazione). Mostrano come creare un indice leggendo i punti creati manualmente e quindi eseguendo una query su altri punti creati manualmente, che restituisce gli ID contenuti nella finestra. Ha senso. Ma non spiegano come ciò si ricollegherebbe a qualche file originale da cui sarebbe arrivato l'indice.

Sto pensando che debba andare qualcosa del genere:

  1. Estrai le bbox per ogni feature poligonale dal mio shapefile del poligono e inseriscile in un indice spaziale, dando loro un id uguale al loro id nel file shape.
  2. Interroga quell'indice per ottenere gli ID che si intersecano.
  3. Quindi rieseguire il mio incrocio solo sulle funzionalità del mio shapefile originale che sono state identificate interrogando il mio indice (non sono sicuro di come farei quest'ultima parte).

Ho l'idea giusta? Mi sto perdendo qualcosa?


In questo momento sto cercando di far funzionare questo codice su un file di forma punto che contiene solo una funzione punto e un file di forma poligono che contiene> 20.000 funzioni poligono.

Sto importando gli shapefile usando Fiona, aggiungendo l'indice spaziale usando RTree e provando a fare l'intersezione usando Shapely.

Il mio codice di test è simile al seguente:

#point shapefile representing location of desired focal statistic
traps = fiona.open('single_pt_speed_test.shp', 'r') 

#polygon shapefile representing land cover of interest 
gl = MultiPolygon([shape(pol['geometry']) for pol in fiona.open('class3_aa.shp', 'r')]) 

#search area
areaKM2 = 20

#create empty spatial index
idx = index.Index()

#set initial search radius for buffer
areaM2 = areaKM2 * 1000000
r = (math.sqrt(areaM2/math.pi))

#create spatial index from gl
for i, shape in enumerate(gl):
    idx.insert(i, shape.bounds)

#query index for ids that intersect with buffer (will eventually have multiple points)
for point in traps:
        pt_buffer = shape(point['geometry']).buffer(r)
        intersect_ids = pt_buffer.intersection(idx)

Ma continuo a ricevere TypeError: l'oggetto 'Polygon' non è richiamabile


1
Un indice spaziale è integrale e trasparente al set di dati (contenuto, non una singola entità dal punto di vista dell'utente) Il software che esegue le intersezioni è a conoscenza e utilizzerà gli indici spaziali per creare un breve elenco con cui eseguire l'intersezione reale informando rapidamente il software che le caratteristiche dovrebbero essere prese in considerazione per un'ispezione più ravvicinata e che non sono chiaramente in nessun punto vicino all'intersezione. Il modo in cui ne crei uno dipende dal tuo software e dal tuo tipo di dati ... ti preghiamo di fornire maggiori informazioni su questi punti per un aiuto più specifico. Per un file di forma è il file .shx.
Michael Stimson,

4
.shx non è un indice spaziale. È solo il file offset di accesso dinamico a larghezza variabile. .sbn / .sbx è la coppia di indici spaziali del file di forma ArcGIS, sebbene le specifiche per quelle non siano state rilasciate.
Vince il

1
Inoltre .qixè l' indice Mapterver / GDAL / OGR / SpatiaLite quadtree
Mike T

La tua idea è perfettamente giusta per Spatialite che non ha un vero indice spaziale. La maggior parte degli altri formati, se supportano affatto gli indici spaziali, lo fanno in modo trasparente.
user30184,

2
Continui a ottenere TypeError: 'Polygon' object is not callablecon il tuo esempio di aggiornamento perché sovrascrivi la shapefunzione che hai importato da shapely con un oggetto Polygon che crei con questa riga:for i, shape in enumerate(gl):
user2856

Risposte:


12

Questo è il senso. L'albero a R consente di effettuare un primo passaggio molto veloce e offre una serie di risultati che avranno "falsi positivi" (i riquadri di delimitazione potrebbero intersecarsi quando le geometrie non lo fanno). Quindi si passa al set di candidati (recuperandoli dallo shapefile in base al loro indice) e si esegue un test di intersezione matematicamente preciso utilizzando, ad esempio, Shapely. Questa è la stessa strategia utilizzata nei database spaziali come PostGIS.


1
Bel gioco di parole (GiST)! GiST è normalmente descritto come una variante B-Tree ma Postgresql ha un'implementazione GiST di R-Tree. Sebbene wiki non sia necessariamente il miglior riferimento per citare, ha un bel diagramma per spiegare le ricerche nel riquadro di selezione.
MappaGnosis,

Può valere la pena imparare un modo manuale per utilizzare l'indice R-tree come nei passaggi 2 e 3. Questo blog su OGC GeoPackage che supporta anche R-tree poiché tabelle di database separate mostrano alcuni SQL e acquisizioni di schermate openjump.blogspot.fi / 2014/02 /… .
user30184

9

Hai quasi capito, ma hai fatto un piccolo errore. È necessario utilizzare il intersectionmetodo sull'indice spaziale, piuttosto che passare l'indice al intersectionmetodo sul punto bufferizzato. Una volta trovato un elenco di funzioni in cui le caselle di delimitazione si sovrappongono, è necessario verificare se il punto bufferizzato interseca effettivamente le geometrie.

import fiona
from shapely.geometry import mapping
import rtree
import math

areaM2 = areaKM2 * 1000000
r = (math.sqrt(areaM2/math.pi))

# open both layers
with fiona.open('single_pt_speed_test.shp', 'r') as layer_pnt:
    with fiona.open('class3_aa.shp', 'r') as layer_land:

        # create an empty spatial index object
        index = rtree.index.Index()

        # populate the spatial index
        for fid, feature in layer_land.items():
            geometry = shape(feature['geometry'])
            idx.insert(fid, geometry.bounds)

        for feature in layer_pnt:
            # buffer the point
            geometry = shape(feature['geometry'])
            geometry_buffered = geometry.buffer(r)

            # get list of fids where bounding boxes intersect
            fids = [int(i) for i in index.intersection(geometry_buffered.bounds)]

            # access the features that those fids reference
            for fid in fids:
                feature_land = layer_land[fid]
                geometry_land = shape(feature_land['geometry'])

                # check the geometries intersect, not just their bboxs
                if geometry.intersects(geometry_land):
                    print('Found an intersection!')  # do something useful here

Se sei interessato a trovare punti che si trovano a una distanza minima dalla tua classe di terra, puoi distanceinvece utilizzare il metodo (scambia la sezione appropriata dalla precedente).

for feature in layer_pnt:
    geometry = shape(feature['geometry'])

    # expand bounds by r in all directions
    bounds = [a+b*r for a,b in zip(geometry.bounds, [-1, -1, 1, 1])]

    # get list of fids where bounding boxes intersect
    fids = [int(i) for i in index.intersection(geometry_buffered.bounds)]

    for fid in fids:
        feature_land = layer_land[fid]
        geometry_land = shape(feature_land['geometry'])

        # check the geometries are within r metres
        if geometry.distance(geometry_land) <= r:
            print('Found a match!')

Se ci vuole molto tempo per costruire il tuo indice spaziale e lo farai più di qualche volta, dovresti cercare di serializzare l'indice in un file. La documentazione descrive come eseguire questa operazione: http://toblerity.org/rtree/tutorial.html#serializing-your-index-to-a-file

Puoi anche esaminare il caricamento in blocco delle scatole di delimitazione nell'rtree usando un generatore, in questo modo:

def gen(collection):
    for fid, feature in collection.items():
        geometry = shape(feature['geometry'])
        yield((fid, geometry.bounds, None))
index = rtree.index.Index(gen(layer_land))

2

Sì, questa è l'idea. Ecco un estratto da questo tutorial sull'uso di un indice spaziale r-tree in Python , usando shapely, Fiona e geopandas:

Un albero a r rappresenta i singoli oggetti e i loro riquadri di delimitazione (la "r" sta per "rettangolo") come il livello più basso dell'indice spaziale. Quindi aggrega gli oggetti vicini e li rappresenta con il loro riquadro di delimitazione aggregato nel successivo livello superiore dell'indice. A livelli ancora più alti, l'albero r aggrega i riquadri di delimitazione e li rappresenta in base al loro riquadro di selezione, iterativamente, fino a quando tutto non viene annidato in un riquadro di selezione di livello superiore. Per effettuare la ricerca, l'albero a forma di r prende una casella di query e, a partire dal livello più alto, vede quali (se presenti) caselle di delimitazione la intersecano. Quindi espande ogni riquadro di delimitazione intersecante e vede quale dei box di delimitazione figlio al suo interno interseca il riquadro di query. Questo procede in modo ricorsivo fino a quando tutte le caselle che si intersecano non vengono cercate fino al livello più basso e restituisce gli oggetti corrispondenti dal livello più basso.

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.