Finden Sie die nächsten Nachbarn zwischen zwei Tabellen mit Punktpositionen in SpatiaLite?

10

Ich habe heute angefangen, mit SpatiaLite zu spielen und bin bereits auf ein Problem gestoßen.

Für jede in tableOne gespeicherte Punktposition möchte ich einen nächstgelegenen (linearen Abstand) Punkt aus tableTwo auswählen.

Bisher habe ich eine ungeschickte Lösung gefunden, die VIEW verwendet:

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;

Und dann:

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

scheint den Job zu machen.

Zwei Fragen:

Gibt es eine Möglichkeit, eine solche Abfrage durchzuführen, ohne eine ANSICHT zu erstellen?

Gibt es eine andere Möglichkeit, diese Abfrage für eine bessere Leistung zu optimieren? In einem realen Szenario wird tableOne Hunderttausende von Datensätzen und tableTwo - 1,3 Millionen haben.

Radek
quelle
Ich kann Ihnen einen Ansatz geben, der mehrere Größenordnungen schneller ist, aber Sie müssten den Postgresql 9 Knngist Index anstelle von Spatialite verwenden ...
Ragi Yaser Burhum
Tatsächlich schneller als GRASS, ArcGIS, QGIS, SQLServer und so ziemlich jedes andere räumliche Datenbank- / Desktop-GIS (habe jedoch die Oracle-Funktion für den nächsten Nachbarn nicht ausprobiert). Lassen Sie mich nur wissen, ob dies eine Option ist.
Ragi Yaser Burhum
@Ragi: Mir ist bewusst, dass PostGIS eine viel effizientere Möglichkeit wäre, mit solchen Problemen zu arbeiten. Das ultimative Ziel dieser Übung wäre es jedoch, eine kleine tragbare App zu erstellen. In diesem Fall ist SpatiaLite ein Gewinner.
Radek
Was ist Ihre Entwicklungsplattform für Ihre tragbare App?
Allan Adair
@Allan: Arbeiten an beiden: Windows Server 2008 und Ubuntu im Moment.
Radek

Antworten:

5

Ich habe gerade dieses SQL getestet und es funktioniert:

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

Wie Sie hier lesen können "Die naive Möglichkeit, eine Abfrage zum nächsten Nachbarn durchzuführen, besteht darin, die Kandidatentabelle nach Entfernung von der Abfragegeometrie zu ordnen und dann den Datensatz mit der geringsten Entfernung zu nehmen".

Freundliche Grüße,

Andrea

aborruso
quelle
Ich versuche, diese Abfrage zu verwenden, erhalte jedoch unerwartete Ergebnisse. Ich erhalte eine resultierende Tabelle, aber mit IDs für Zeilen, die ich sehen kann, sind sie nicht der nächste Nachbar. Ich versuche, die nächstgelegene Linie in einer mehrzeiligen Zeichenfolge zu jedem Punkt in einer anderen Ebene zu finden. Ich bin neu bei SpatiaLite. Irgendwelche Vorschläge? Außerdem möchte ich dies letztendlich mit über 1 Million Punkten
ausführen
Ich bin mir auch nicht sicher, ob ich den Zweck dieser Aussage verstehe: WHERE g1.OGC_FID <> g2.OGC_FID
kflaw
Außerdem bekomme ich in meinem Ergebnis keine Distanz. Ich habe mit dieser Zeile herumgespielt: AND ST_Contains (ST_Expand (g1.geometry, 50), g2.geometry) sowie entfernt und erhalte immer noch keine Abstandswerte, obwohl ich eine ID
bekomme
6

Wenn Sie die Abstände zwischen allen Punktkombinationen nicht berechnen möchten, können Sie einen räumlichen Index für eine der Tabellen verwenden:

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
quelle
Ich habe versucht, die von Ihnen veröffentlichte Lösung zu verwenden, da ich einen räumlichen Index verwenden muss, aber er gibt keine Werte zurück. f_table_name = 'A'Muss ich für die Zeile 'A' durch den tatsächlichen Tabellennamen (Tabelle 1) ersetzen? Ich habe es so oder so versucht und es gibt immer noch nichts zurück, warum könnte das sein
kflaw
Du hast recht f_table_name = 'A'sollte sein f_table_name = 'tableOne'. Beachten Sie, dass diese Anforderung Spatialite> 4.x voraussetzt ( SpatialIndexvirtuelle Tabelle wird verwendet). Haben Sie versucht, das search_framefür Ihren Anwendungsfall anzupassen ? Im obigen Beispiel wird angenommen, dass sich die Punkte in einer maximalen Entfernung von 10000 Metern befinden.
Samuel
Ich habe mit dem Suchrahmenwert herumgespielt, ich gehe davon aus, dass dies innerhalb von 10000 Metern bedeutet, was für mich funktionieren sollte. Ich weiß eigentlich nicht, welche Version von Spatialite ich habe die Datenbank über qgis erstellt und verwende die GUI in qgis. Lassen Sie mich sehen, ob ich das herausfinden kann
kflaw
Es ist Version 4.1.1 mit SQLite Version 3.7.17, also sollte es dann funktionieren? Ich frage mich, was los ist. Ich werde es noch einmal testen
kflaw
3

Seit Version 4.4.0 unterstützt SpatiaLite einen virtuellen KNN-Tabellenindex für Probleme mit dem nächsten Nachbarn. Hier ist eine Abfrage, die die nächstgelegene Linie in einer Linestring-Tabelle zu jedem Punkt in einer Punktetabelle findet.

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

Sie können Ihre Abfrage so vereinfachen.

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

Für eine allgemeinere Lösung kann es sich lohnen, diese PostGIS Nearest Neighbor-Funktion zu konvertieren: http://blog.mackerron.com/2011/03/postgis-nearest-neighbour/

Unterdunkel
quelle
Leider führt der Code zu:SQL error: "misuse of aggregate: MIN()"
Radek
Ab PostGIS gibt es auch einige Beispiele auf der BostonGIS-Website, aber bisher war es mir nicht gelungen, sie in SpatiaLite zu übersetzen: /
radek