Znaleźć najbliższych sąsiadów między dwiema tabelami z lokalizacjami punktów w SpatiaLite?

10

Dzisiaj zacząłem grać ze SpatiaLite i już natknąłem się na problem.

Dla każdego położenia punktu zapisanego w tableOne chciałbym wybrać jeden, najbliższy (odległość liniowa) punkt z tableTwo.

Do tej pory wymyśliłem niezdarne rozwiązanie, które wykorzystuje 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;

I wtedy:

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

zdaje się wykonywać pracę.

Dwa pytania:

Czy istnieje sposób na wykonanie takiego zapytania bez tworzenia WIDOKU?

Czy istnieje inny sposób optymalizacji tego zapytania w celu uzyskania lepszej wydajności? W rzeczywistym scenariuszu tableOne będzie miało setki par tysięcy rekordów, a tableTwo - 1,3 miliona.

radek
źródło
Mogę dać ci podejście, które jest kilka rzędów wielkości szybsze, ale wymagałoby to użycia indeksu kngist postgresql 9 zamiast spatialite ...
Ragi Yaser Burhum
faktycznie szybszy niż GRASS, ArcGIS, QGIS, SQLServer i praktycznie każdy inny przestrzenny system GIS db / Desktop (chociaż nie wypróbowałem jeszcze funkcji Oracle najbliższego sąsiada). Po prostu daj mi znać, czy jest to opcja.
Ragi Yaser Burhum
@Ragi: Zdaję sobie sprawę, że PostGIS byłby znacznie wydajniejszym sposobem pracy z takim problemem. Jednak ostatecznym celem tego ćwiczenia byłoby stworzenie małej przenośnej aplikacji, w tym przypadku SpatiaLite jest zwycięzcą.
radek
Jaka jest Twoja platforma programistyczna dla Twojej przenośnej aplikacji?
Allan Adair
@Allan: Obecnie działa na obu systemach: Windows Server 2008 i Ubuntu.
radek

Odpowiedzi:

5

Właśnie przetestowałem ten SQL i działa:

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

Jak można przeczytać tutaj: „Naiwnym sposobem wykonania zapytania najbliższego sąsiada jest uporządkowanie tabeli kandydatów na odległość od geometrii zapytania, a następnie zapisanie rekordu z najmniejszą odległością”.

Z poważaniem,

Andrea

aborruso
źródło
Próbuję użyć tego zapytania, ale otrzymuję nieoczekiwane wyniki - otrzymuję wynikową tabelę, ale z identyfikatorami linii, które widzę, nie są najbliższym sąsiadem. Próbuję znaleźć najbliższą linię w wielowierszowej warstwie strunowej do każdego punktu na innej warstwie. Jestem nowy w SpatiaLite. Jakieś sugestie? Ponadto ostatecznie chcę uruchomić to na 1 milion + punktów
kflaw
Nie jestem również pewien, czy rozumiem cel tego stwierdzenia: GDZIE g1.OGC_FID <> g2.OGC_FID
kflaw
Ponadto w moim wyniku uzyskuję zerowy dystans. Grałem około z tej linii: I ST_Contains (ST_Expand (g1.geometry, 50), g2.geometry), jak również usuwa go i wciąż żadnych wartości odległości, choć jestem coraz identyfikator
kflaw
6

Jeśli nie chcesz obliczać odległości między wszystkimi kombinacjami punktów, możesz użyć indeksu przestrzennego na jednej z tabel:

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
źródło
Próbowałem użyć opublikowanego rozwiązania, ponieważ muszę użyć indeksu przestrzennego, ale nie zwraca on żadnych wartości? w przypadku wiersza f_table_name = 'A', czy muszę zastąpić „A” rzeczywistą nazwą tabeli (tabela pierwsza)? Próbowałem w obu kierunkach i nadal nic nie zwraca, dlaczego tak może być
kflaw
Masz rację, f_table_name = 'A'powinno być f_table_name = 'tableOne'. Zauważ, że to żądanie zakłada spatialite> 4.x ( SpatialIndexużywana jest wirtualna tabela). Czy próbowałeś dostosować search_frameswój przypadek użycia? W powyższym przykładzie zakłada się, że punkty znajdują się w maksymalnej odległości 10000 metrów.
Samuel
Bawiłem się z wartością ramki wyszukiwania, zakładam, że oznacza to w odległości 10000 metrów, co powinno dla mnie działać. Tak naprawdę nie wiem, która wersja spatialite, stworzyłem bazę danych za pomocą qgis i używam GUI w qgis.
Zobaczę,
Jest to wersja 4.1.1 z sqlite w wersji 3.7.17, więc powinno działać? Zastanawiam się, co jest nie tak, przetestuję to jeszcze bardziej
kflaw
3

Od wersji 4.4.0 SpatiaLite obsługuje indeks tabeli wirtualnej KNN dla problemów z najbliższym sąsiadem. Oto zapytanie, które znajduje najbliższą linię w tabeli linii do każdego punktu w tabeli punktów.

SELECT k.* FROM knn k, points p
WHERE f_table_name = 'linestrings' 
AND ref_geometry = p.geometry
AND max_items = 1;
Greg Kraków
źródło
2

Możesz uprościć swoje zapytanie w ten sposób.

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

Aby uzyskać bardziej ogólne rozwiązanie, warto spróbować przekonwertować tę funkcję PostGIS Nearest Neighbor: http://blog.mackerron.com/2011/03/postgis-nearest-neighbour/

podmrok
źródło
niestety kod skutkuje:SQL error: "misuse of aggregate: MIN()"
radek
W PostGIS jest też kilka przykładów na stronie BostonGIS, ale jak dotąd nie udało mi się przetłumaczyć ich na SpatiaLite: /
radek,