Il modo più veloce per unire spazialmente un punto CSV con un poligono Shapefile


19

Ho un file CSV da 1 miliardo di punti e un file di forma con circa 5.000 poligoni. Quale sarebbe il modo più veloce per unire spazialmente punti e poligoni? Per ogni punto, devo ottenere l'id poligonale contenente. (I poligoni non si sovrappongono.)

Di solito, caricavo entrambi i set di dati in PostGIS. C'è un modo più veloce per portare a termine il lavoro?

Sto cercando una soluzione open source.

Risposte:


16

Se "più veloce" include la quantità di vostro tempo impiegato, la soluzione dipenderà da ciò che il software che hai dimestichezza con e può utilizzare rapidamente. Di conseguenza, le seguenti osservazioni si concentrano su idee per ottenere i tempi di elaborazione più rapidi possibili .

Se usi un programma fisso, quasi sicuramente il meglio che puoi fare è pre-elaborare i poligoni per impostare una struttura di dati punto-in-poligono, come un albero KD o quadtree, le cui prestazioni saranno in genere O (log (V ) * (N + V)) dove V è il numero totale di vertici nei poligoni e N è il numero di punti, poiché la struttura dei dati richiederà almeno O (log (V) * V) per creare e quindi deve essere analizzato per ogni punto con un costo O (log (V)) per punto.

Puoi fare sostanzialmente di meglio gridando prima i poligoni, sfruttando l'assunto di non sovrapposizioni. Ogni cella della griglia è interamente all'interno di un poligono interno (incluso l'interno del "poligono universale"), nel qual caso etichetta la cella con l'id del poligono, oppure contiene uno o più bordi poligonali. Il costo di questa rasterizzazione, pari al numero di celle della griglia referenziate durante la rasterizzazione di tutti i bordi, è O (V / c) dove c è la dimensione di una cella, ma la costante implicita nella notazione big-O è piccola.

(Una bellezza di questo approccio è che puoi sfruttare le routine grafiche standard. Ad esempio, se hai un sistema che (a) disegna i poligoni su uno schermo virtuale usando (b) un colore distinto per ogni poligono e (c) consente di leggere il colore di qualsiasi pixel a cui tieni, lo hai fatto.)

Con questa griglia in atto, pre-selezionare i punti calcolando la cella contenente ciascun punto (un'operazione O (1) che richiede solo pochi orologi). A meno che i punti non siano raggruppati attorno ai confini del poligono, questo in genere lascia solo circa i punti O (c) con risultati ambigui. Il costo totale di costruzione della griglia e di pre-screening è quindi O (V / c + 1 / c ^ 2) + O (N). Devi usare qualche altro metodo (come uno di quelli consigliati finora) per elaborare i punti rimanenti (cioè quelli che sono vicini ai confini poligonali), al costo di O (log (V) * N * c) .

Man mano che c diventa più piccolo, sempre meno punti saranno nella stessa cella della griglia con un bordo e quindi sempre meno richiederà la successiva elaborazione O (log (V)). Agire contro questo è la necessità di memorizzare le celle della griglia O (1 / c ^ 2) e di passare il tempo O (V / c + 1 / c ^ 2) a rasterizzare i poligoni. Pertanto ci sarà una dimensione della griglia ottimale c. Usandolo, il costo computazionale totale è O (log (V) * N) ma la costante implicita è in genere molto più piccola rispetto all'utilizzo delle procedure fisse, a causa della velocità O (N) del pre-screening.

20 anni fa ho testato questo approccio (usando punti uniformemente distanziati in tutta l'Inghilterra e al largo e sfruttando una griglia relativamente grezza di circa 400.000 celle offerte dai buffer video dell'epoca) e ottenuto due ordini di accelerazione di grandezza rispetto al miglior algoritmo pubblicato che ho potuto trova. Anche quando i poligoni sono piccoli e semplici (come i triangoli), sei praticamente sicuro di un ordine di accelerazione di grandezza.

Nella mia esperienza il calcolo è stato così rapido che l'intera operazione è stata limitata dalle velocità di I / O dei dati, non dalla CPU. Anticipando che l'I / O potrebbe essere il collo di bottiglia, si otterrebbero i risultati più veloci memorizzando i punti in un formato il più compresso possibile per ridurre al minimo i tempi di lettura dei dati. Rifletti anche su come archiviare i risultati, in modo da poter limitare le scritture su disco.


6
Ottimo tempo dedicato alla realizzazione della soluzione rispetto al tempo di elaborazione. Impiegare molto tempo per arrivare a una soluzione ottimale è vantaggioso solo se si realizzano quei risparmi attraverso l'ottimizzazione (specialmente dal punto di vista di un datore di lavoro).
Sasa Ivetic,

5

Da parte mia, avrei probabilmente caricare i dati CSV in uno shp file e poi scrivere uno script Python usando shapefile e formosa per ottenere il poligono contenente id e aggiornare il valore del campo.

Non so se geotools e JTS siano più veloci di shapefile / shapely ... Non ho tempo di provarlo!

modifica : A proposito, la conversione CSV in formato shapefile probabilmente non è richiesta, poiché i valori potrebbero essere facilmente formattati per essere testati con oggetti spaziali dal tuo shapefile poligonale.


4
Caricherei direttamente i dati utilizzando un lettore CSV e popolare un indice spaziale Rtree . La combinazione di Rtree e Shapely ha prestazioni impressionanti (molto meglio di PostGIS; non posso confrontarmi con JTS perché non conosco Java).
Mike T,

2
Buona idea a condizione che non sia necessario memorizzare tutti i punti 1b in memoria contemporaneamente. Con un minimo di 16 byte per punto (X / Y), stai cercando dati da 16 GB. Se Rtree costruirà l'indice sull'archiviazione locale, migliorerà sicuramente le prestazioni. Anche l'importazione di 1b punti in un singolo file di forma non funzionerà. Gli shapefile dello stato delle specifiche OGR sono limitati a 8 GB (4 GB consigliati). Una forma a punto singolo utilizza 20 byte.
Sasa Ivetic,

4

Ho finito per convertire i poligoni in un raster e campionarlo nelle posizioni dei punti. Poiché i miei poligoni non si sovrapponevano e non era necessaria un'elevata precisione (i poligoni rappresentavano classi di uso del suolo e i loro confini erano comunque considerati piuttosto incerti) questa era la soluzione più efficiente in termini di tempo che potevo trovare.



3

Usa Spatialite .

Scarica la GUI. Puoi aprire sia Shapefile che CSV come tabelle virtuali. Ciò significa che in realtà non li importi nel database ma vengono visualizzati come tabelle e puoi unirti rapidamente e interrogarli come preferisci.


3

Puoi farlo abbastanza rapidamente usando OGR in C / C ++ / Python (Python dovrebbe essere il più lento dei 3). Passa attraverso tutti i poligoni e imposta un filtro sui punti, passa attraverso i punti filtrati e saprai che ciascuno dei punti che attraversi apparterrà al poligono corrente. Ecco il codice di esempio in Python che utilizza OGR che eseguirà il ciclo attraverso i poligoni e i punti filtro di conseguenza. Il codice C / C ++ sarà abbastanza simile a questo e immagino che otterrai un aumento di velocità significativo rispetto a Python. Dovrai aggiungere alcune righe di codice per aggiornare il CSV mentre procedi:

from osgeo import ogr
from osgeo.gdalconst import *

inPolyDS = ogr.Open("winnipeg.shp", GA_ReadOnly)
inPolyLayer = inPolyDS.GetLayer(0)
inPointDS = ogr.Open("busstops.vrt", GA_ReadOnly)   
inPointLayer = inPointDS.GetLayerByName("busstops")

inPolyFeat = inPolyLayer.GetNextFeature()
while inPolyFeat is not None:
  inPtFeat = inPointLayer.GetNextFeature()
  while inPtFeat is not None:
    ptGeom = inPtFeat.GetGeometryRef()
    # Do work here...

    inPtFeat = inPointLayer.GetNextFeature()

  inPolyFeat = inPolyLayer.GetNextFeature()

File VRT (busstops.vrt):

<OGRVRTDataSource>
  <OGRVRTLayer name="busstops">
    <SrcDataSource>busstops.csv</SrcDataSource>
    <GeometryType>wkbPoint</GeometryType>
    <LayerSRS>WGS84</LayerSRS>
    <GeometryField encoding="PointFromColumns" x="X" y="Y" reportSrcColumn="FALSE" />
  </OGRVRTLayer>
</OGRVRTDataSource>

File CSV (busstops.csv):

FID,X,Y,stop_name
1,-97.1394781371062,49.8712241633646,Southbound Osborne at Mulvey

File CSVT (busstops.csvt, OGR ne ha bisogno per identificare i tipi di colonna, altrimenti non eseguirà il filtro spaziale):

Integer,Real,Real,String

2
Questo non attraversa 1 bn di punti 5000 volte (una volta per ogni poligono)?
underdark

Un indice spaziale è un must assoluto . Ho già parlato di Rtree e lo citerò di nuovo!
Mike T,

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.