La forma más rápida de unir espacialmente un punto CSV con un archivo de formas poligonales

19

Tengo un archivo CSV de mil millones de puntos y un archivo de forma con alrededor de 5,000 polígonos. ¿Cuál sería la forma más rápida de unir puntos y polígonos espacialmente? Para cada punto, necesito obtener la identificación del polígono que contiene. (Los polígonos no se superponen).

Por lo general, cargaría ambos conjuntos de datos en PostGIS. ¿Hay una forma más rápida de hacer el trabajo?

Estoy buscando una solución de código abierto.

bajo oscuro
fuente

Respuestas:

16

Si "más rápido" incluye la cantidad de su tiempo que se gasta, la solución dependerá de lo que el software que se sienta cómodo y se puede utilizar de manera expedita. En consecuencia, las siguientes observaciones se centran en ideas para lograr los tiempos informáticos más rápidos posibles .

Si usa un programa enlatado, casi seguramente lo mejor que puede hacer es preprocesar los polígonos para configurar una estructura de datos de punto en el polígono, como un árbol KD o un árbol cuádruple, cuyo rendimiento generalmente será O (log (V ) * (N + V)) donde V es el número total de vértices en los polígonos y N es el número de puntos, porque la estructura de datos tomará al menos un esfuerzo de O (log (V) * V) para crear y luego tiene que sondearse para cada punto a un costo por punto O (log (V)).

Puede hacerlo sustancialmente mejor primero cuadriculando los polígonos, explotando el supuesto de que no hay superposiciones. Cada celda de la cuadrícula está completamente en el interior de un polígono (incluido el interior del "polígono universal"), en cuyo caso etiquete la celda con la identificación del polígono, o bien contiene uno o más bordes de polígono. El costo de esta rasterización, igual al número de celdas de la cuadrícula referenciadas mientras rasteriza todos los bordes, es O (V / c) donde c es el tamaño de una celda, pero la constante implícita en la notación O grande es pequeña.

(Una ventaja de este enfoque es que puede explotar las rutinas gráficas estándar. Por ejemplo, si tiene un sistema que (a) dibujará los polígonos en una pantalla virtual usando (b) un color distinto para cada polígono y (c) permite Si lees el color de cualquier píxel que quieras abordar, lo tienes hecho).

Con esta cuadrícula en su lugar, realice una prueba previa de los puntos calculando la celda que contiene cada punto (una operación O (1) que requiere solo unos pocos relojes). A menos que los puntos se agrupen alrededor de los límites del polígono, esto generalmente dejará solo unos puntos O (c) con resultados ambiguos. Por lo tanto, el costo total de construir la cuadrícula y la selección previa es O (V / c + 1 / c ^ 2) + O (N). Debe usar algún otro método (como cualquiera de los recomendados hasta ahora) para procesar los puntos restantes (es decir, aquellos que están cerca de los límites de los polígonos), a un costo de O (log (V) * N * c) .

A medida que c se hace más pequeña, cada vez habrá menos puntos en la misma celda de la cuadrícula con un borde y, por lo tanto, cada vez se requerirá menos procesamiento O (log (V)). Actuar contra esto es la necesidad de almacenar celdas de cuadrícula O (1 / c ^ 2) y gastar tiempo O (V / c + 1 / c ^ 2) rasterizando los polígonos. Por lo tanto, habrá un tamaño de cuadrícula óptimo c. Al usarlo, el costo computacional total es O (log (V) * N) pero la constante implícita es típicamente mucho más pequeña que el uso de los procedimientos enlatados, debido a la velocidad O (N) de la preselección.

Hace 20 años probé este enfoque (usando puntos espaciados uniformemente en toda Inglaterra y en alta mar y explotando una cuadrícula relativamente cruda de alrededor de 400K celdas ofrecidas por las memorias intermedias de video de la época) y obtuve dos órdenes de aceleración de magnitud en comparación con el algoritmo mejor publicado que pude encontrar. Incluso cuando los polígonos son pequeños y simples (como los triángulos), está prácticamente seguro de un orden de aceleración de magnitud.

En mi experiencia, el cálculo fue tan rápido que toda la operación estuvo limitada por las velocidades de E / S de datos, no por la CPU. Anticipando que I / O podría ser el cuello de botella, lograría los resultados más rápidos almacenando los puntos en un formato tan comprimido como sea posible para minimizar los tiempos de lectura de datos. También piense cómo deben almacenarse los resultados, de modo que pueda limitar las escrituras en disco.

whuber
fuente
66
Muy buen momento en el tiempo dedicado a darse cuenta de la solución frente al tiempo de computación. Tomar mucho tiempo para llegar a una solución óptima solo es beneficioso si se da cuenta de esos ahorros a través de la optimización (especialmente desde el punto de vista del empleador).
Sasa Ivetic
5

Por mi parte, probablemente cargaría datos CSV en un archivo shp y luego escribiría un script python usando shapefile y shapely para obtener la identificación del polígono que contiene y actualizar el valor del campo.

No sé si los geotools y JTS son más rápidos que shapefile / shapely ... ¡No tengo tiempo para probarlo!

editar : Por cierto, la conversión de CSV a formato shapefile probablemente no sea necesaria, ya que los valores podrían formatearse fácilmente para ser probados con objetos espaciales de su shapefile poligonal.

simo
fuente
44
Cargaría directamente los datos usando un lector csv y llenaría un índice espacial Rtree . La combinación de Rtree y Shapely tiene un rendimiento impresionante (mucho mejor que PostGIS; no puedo compararme con JTS porque no conozco Java).
Mike T
2
Buena idea siempre que no necesite almacenar todos los puntos 1b en la memoria a la vez. Con un mínimo de 16 bytes por punto (X / Y), está viendo 16GB de datos. Si Rtree construirá el índice en el almacenamiento local, definitivamente mejorará el rendimiento. Importar puntos 1b a un solo archivo shape tampoco funcionará. Las especificaciones de OGR indican que los archivos de forma están limitados a 8 GB (se recomiendan 4 GB). Una forma de un solo punto usa 20 bytes.
Sasa Ivetic
4

Terminé convirtiendo los polígonos en un ráster y probándolo en las posiciones de los puntos. Como mis polígonos no se superponían y no era necesaria una alta precisión (los polígonos representaban clases de uso de la tierra y sus bordes se consideraban bastante inciertos de todos modos), esta fue la solución más eficiente en tiempo que pude encontrar.

bajo oscuro
fuente
3

Escribiría rápidamente un pequeño programa java basado en el lector de archivos de forma de geotools y la operación contiene de JTS . No sé qué tan rápido puede ser ...

julien
fuente
1
Si tiene los datos en PostGIS, entonces GeoTools puede hacer uso de índices esenciales, etc.
Ian Turton
3

Usa Spatialite .

Descargue la GUI. Puede abrir tanto el Shapefile como el CSV como tablas virtuales. Esto significa que en realidad no los importa a la base de datos, sino que aparecen como tablas y puede unirse rápidamente y consultarlos de la manera que desee.

Sean
fuente
3

Puede hacerlo bastante rápido usando OGR en C / C ++ / Python (Python debería ser el más lento de los 3). Recorre todos los polígonos y establece un filtro en los puntos, recorre los puntos filtrados y sabrás que cada uno de los puntos por los que perteneces pertenecerá al polígono actual. Aquí hay un código de muestra en Python usando OGR que recorrerá los polígonos y filtrará los puntos en consecuencia. El código C / C ++ se verá bastante similar a esto, y me imagino que obtendrá un aumento significativo de la velocidad en comparación con python. Tendrá que agregar algunas líneas de código para actualizar el CSV a medida que avanza:

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

Archivo 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>

Archivo CSV (busstops.csv):

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

Archivo CSVT (busstops.csvt, OGR lo necesita para identificar los tipos de columna, de lo contrario no realizará el filtro espacial):

Integer,Real,Real,String
Sasa Ivetic
fuente
2
¿Eso no pasa por mil millones de puntos 5000 veces (una vez por cada polígono)?
oscuro
Un índice espacial es una necesidad absoluta . ¡Mencioné a Rtree antes, y lo mencionaré nuevamente!
Mike T
-1

podría intentar csv2shp csv2shp

¿Tienes curiosidad por saber en qué industria está el CSV de mil millones de puntos?

sirgeo
fuente
1
se hizo una pregunta similar en el tablero stackoverflow re mysql 10 mil millones de filas stackoverflow.com/questions/5735447/…
sirgeo