Trovare i vicini più vicini tra due tabelle con posizioni dei punti in SpatiaLite?


10

Ho iniziato a giocare con SpatiaLite oggi e mi sono già imbattuto in un problema.

Per ogni posizione del punto memorizzata nella tabella Uno vorrei selezionare un punto più vicino (distanza lineare) dalla tabella Due.

Finora ho trovato una soluzione goffa che utilizza 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;

E poi:

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

sembra fare il lavoro.

Due domande:

C'è un modo per eseguire tale query senza creare una VISTA?

Esiste un altro modo per ottimizzare questa query per prestazioni migliori? In una tabella di scenari del mondo reale, One avrà centinaia di migliaia di record e una tabella di due - 1,3 milioni.


Posso darti un approccio che è più veloce di diversi ordini di grandezza, ma richiederebbe di usare l'indice postngesql 9 knngist invece di spazialite ...
Ragi Yaser Burhum

in realtà più veloce di GRASS, ArcGIS, QGIS, SQLServer e praticamente qualsiasi altro GIS spaziale db / desktop (non ho provato la funzionalità del vicino Oracle più vicino). Fammi solo sapere se è un'opzione.
Ragi Yaser Burhum,

@Ragi: sono consapevole che PostGIS sarebbe un modo molto più efficiente per risolvere questo problema. Tuttavia, l'obiettivo finale di questo esercizio sarebbe quello di creare una piccola app portatile e in questo caso SpatiaLite è un vincitore.
Radek,

Qual è la tua piattaforma di sviluppo per la tua app portatile?
Allan Adair,

@Allan: lavorando su entrambi: Windows Server 2008 e Ubuntu al momento.
radek,

Risposte:


5

Ho appena testato questo SQL e funziona:

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

Come puoi leggere qui "Il modo ingenuo di eseguire una query del vicino più vicino è ordinare la tabella dei candidati in base alla distanza dalla geometria della query, quindi prendere il record con la distanza minima".

I migliori saluti,

Andrea


Sto cercando di utilizzare questa query ma sto ottenendo risultati imprevisti: sto ottenendo una tabella risultante ma con ID per le linee che vedo non sono il vicino più vicino. Sto cercando di trovare la linea più vicina in un livello di stringa multilinea a ciascun punto di un altro livello. Sono nuovo con spatiaLite. Eventuali suggerimenti? Inoltre, alla fine voglio eseguire questo su 1 milione + punti
kflaw il

Inoltre, non sono sicuro di comprendere lo scopo di questa affermazione: WHERE g1.OGC_FID <> g2.OGC_FID
kflaw,

Inoltre, nel mio risultato sto ottenendo una distanza nulla. Ho giocato con questa linea: AND ST_Contains (ST_Expand (g1.geometry, 50), g2.geometry) così come l'ho rimosso e non ottengo ancora valori di distanza, anche se sto ottenendo un ID
kflaw

6

Se non si desidera calcolare le distanze tra tutte le combinazioni di punti, è possibile utilizzare un indice spaziale su una delle tabelle:

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

Ho provato a utilizzare la soluzione che hai pubblicato in quanto ho bisogno di utilizzare un indice spaziale, ma non restituisce valori? per la riga f_table_name = 'A', devo sostituire "A" con il nome effettivo della tabella (tabella uno)? Ho provato in entrambi i modi e non restituisce ancora nulla, perché potrebbe essere
kflaw

Hai ragione f_table_name = 'A'dovrebbe essere f_table_name = 'tableOne'. Si noti che questa richiesta presuppone spatialite> 4.x ( SpatialIndexviene utilizzata la tabella virtuale). Hai provato a regolare il search_framecaso d'uso? Nell'esempio sopra, si presume che i punti siano ad una distanza massima di 10000 metri.
Samuel,

Ho giocato con il valore del frame di ricerca, presumo che significa entro 10000 metri che dovrebbe funzionare per me. In realtà non so quale versione di spatialite, ho creato il database tramite qgis e sto usando la GUI in qgis. Fammi vedere se riesco a capirlo
kflaw il

È la versione 4.1.1 con sqlite versione 3.7.17, quindi dovrebbe funzionare allora? Mi chiedo che cosa c'è che non va. Lo
proverò

3

Dalla versione 4.4.0 SpatiaLite supporta un indice di tabella virtuale KNN per i problemi di vicinato più vicini. Ecco una query che trova la linea più vicina in una tabella lineare per ogni punto in una tabella di punti.

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

2

Puoi semplificare la tua query in questo modo.

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

Per una soluzione più generica, potrebbe valere la pena provare a convertire la funzione Prossimo più vicino PostGIS: http://blog.mackerron.com/2011/03/postgis-nearest-neighbour/


sfortunatamente il codice risulta in:SQL error: "misuse of aggregate: MIN()"
radek,

A partire da PostGIS ci sono anche alcuni esempi sul sito web BostonGIS, ma finora non sono riuscito a tradurli in SpatiaLite: /
radek,
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.