¿Encontrar vecinos más cercanos entre dos tablas con ubicaciones de puntos en SpatiaLite?

10

Hoy comencé a jugar con SpatiaLite y ya me topé con un problema.

Para cada ubicación de punto almacenada en tableOne, me gustaría seleccionar un punto más cercano (distancia lineal) de tableTwo.

Hasta ahora se me ocurrió una solución torpe que utiliza VIEW:

CREATE VIEW testview AS 
SELECT 
A.id , 
B.myValue, 
Distance(A.Geometry, B.Geometry) AS distance
FROM tableOne AS A, tableTwo AS B
WHERE distance < 10000
ORDER BY A.Id, distance;

Y entonces:

SELECT * FROM testview
WHERE distance = (SELECT MIN(distance) FROM testview AS t WHERE t.id = testview.id)

Parece hacer el trabajo.

Dos preguntas:

¿Hay alguna manera de realizar dicha consulta sin crear una VISTA?

¿Hay alguna otra forma de optimizar esta consulta para un mejor rendimiento? En un escenario del mundo real, tableOne tendrá cientos de miles de registros, y tableTwo - 1.3 millones.

radek
fuente
Puedo darle un enfoque que es varios órdenes de magnitud más rápido, pero requeriría que use el índice postgresql 9 knngist en lugar de spaceialite ...
Ragi Yaser Burhum
en realidad más rápido que GRASS, ArcGIS, QGIS, SQLServer y prácticamente cualquier otro db espacial / GIS de escritorio (aunque no he probado la funcionalidad de vecino más cercano de Oracle). Solo hágamelo saber si es una opción.
Ragi Yaser Burhum
@Ragi: Soy consciente de que PostGIS sería una forma mucho más eficiente de trabajar con este problema. Sin embargo, el objetivo final de este ejercicio sería hacer una pequeña aplicación portátil y en este caso SpatiaLite es un ganador.
radek
¿Cuál es su plataforma de desarrollo para su aplicación portátil?
Allan Adair
@Allan: trabajando en ambos: Windows Server 2008 y Ubuntu en este momento.
radek

Respuestas:

5

Acabo de probar este SQL y funciona:

SELECT g1.OGC_FID As id1, g2.OGC_FID As id2, MIN(ST_Distance(g1.GEOMETRY,g2.GEOMETRY)) AS DIST
FROM table_01 As g1, table_02 As g2   
WHERE g1.OGC_FID <> g2.OGC_FID
AND ST_Contains(ST_Expand(g1.geometry,50),g2.geometry)
GROUP BY id1
ORDER BY id1

Como puede leer aquí "La manera ingenua de llevar a cabo una consulta vecina más cercana es ordenar la tabla de candidatos por distancia desde la geometría de la consulta, y luego tomar el registro con la distancia más pequeña".

Atentamente,

Andrea

aborruso
fuente
Estoy tratando de usar esta consulta pero obtengo resultados inesperados: estoy obteniendo una tabla resultante pero con ID para las líneas que puedo ver no son las vecinas más cercanas. Estoy tratando de encontrar la línea más cercana en una capa de cadena multilínea a cada punto en otra capa. Soy nuevo en spatiaLite. ¿Alguna sugerencia? Además, en última instancia, quiero ejecutar esto con más de 1 millón de puntos
kflaw
Tampoco estoy seguro de entender el propósito de esta declaración: WHERE g1.OGC_FID <> g2.OGC_FID
kflaw
Además, en mi resultado estoy obteniendo una distancia nula. He jugado con esta línea: Y ST_Contains (ST_Expand (g1.geometry, 50), g2.geometry), así como lo eliminé y todavía no obtengo valores de distancia, a pesar de que
obtengo
6

Si no desea calcular las distancias entre todas las combinaciones de puntos, puede usar un índice espacial en una de las tablas:

SELECT 
  A.id , 
  B.myValue, 
  MIN(Distance(A.Geometry, B.Geometry)) AS distance
FROM tableOne AS A, tableTwo AS B
WHERE A.ROWID IN (
  SELECT ROWID
  FROM SpatialIndex WHERE
    f_table_name = 'A' 
    AND search_frame = BuildCircleMbr(ST_X(B.Geometry), ST_Y(B.Geometry), 10000))
GROUP BY A.id, B.myValue
Samuel
fuente
Intenté usar la solución que publicaste, ya que necesito usar un índice espacial, pero ¿no devuelve ningún valor? para la línea f_table_name = 'A', ¿necesito reemplazar 'A' con el nombre real de la tabla (tabla uno)? Lo he intentado de cualquier manera y todavía no devuelve nada, ¿por qué podría ser esto
Kflaw
Tienes razón f_table_name = 'A'debería ser f_table_name = 'tableOne'. Tenga en cuenta que esta solicitud asume spaceialite> 4.x ( SpatialIndexse utiliza la tabla virtual). ¿Intentó ajustar el search_framepara su caso de uso? En el ejemplo anterior, se supone que los puntos están a una distancia máxima de 10000 metros.
Samuel
Jugué con el valor del cuadro de búsqueda, supongo que eso significa que dentro de 10000 metros debería funcionar para mí. En realidad no sé qué versión de spaceialite, creé la base de datos a través de qgis y estoy usando la interfaz gráfica de usuario en qgis. Déjame ver si puedo resolver eso
kflaw
Es la versión 4.1.1 con sqlite versión 3.7.17, ¿entonces debería funcionar? Me pregunto qué pasa, lo
probaré un
3

Desde la versión 4.4.0, SpatiaLite admite un índice de tabla virtual KNN para los problemas de vecinos más cercanos. Aquí hay una consulta que encuentra la línea más cercana en una tabla de cadenas lineales a cada punto en una tabla de puntos.

SELECT k.* FROM knn k, points p
WHERE f_table_name = 'linestrings' 
AND ref_geometry = p.geometry
AND max_items = 1;
Greg Cracovia
fuente
2

Puede simplificar su consulta de esta manera.

SELECT 
   A.id , 
   B.myValue, 
   MIN(Distance(A.Geometry, B.Geometry)) AS distance
FROM tableOne AS A, tableTwo AS B
GROUP BY A.id, B.myValue

Para una solución más genérica, podría valer la pena intentar convertir esta función de vecino más cercano de PostGIS: http://blog.mackerron.com/2011/03/postgis-nearest-neighbour/

bajo oscuro
fuente
desafortunadamente el código da como resultado:SQL error: "misuse of aggregate: MIN()"
radek
A partir de PostGIS también hay algunos ejemplos en el sitio web de BostonGIS, pero hasta ahora no tuve éxito en traducirlos a SpatiaLite: /
radek