Найти ближайших соседей между двумя таблицами с точечными местоположениями в SpatiaLite?

10

Я начал играть со SpatiaLite сегодня и уже наткнулся на проблему.

Для каждой точки, сохраненной в tableOne, я бы хотел выбрать одну, ближайшую (линейное расстояние) точку из tableTwo.

До сих пор я придумал неуклюжее решение, которое использует 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;

А потом:

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

кажется, делает работу.

Два вопроса:

Есть ли способ выполнить такой запрос без создания VIEW?

Есть ли другой способ оптимизировать этот запрос для повышения производительности? В реальном сценарии tableOne будет иметь сотни-пару тысяч записей, а tableTwo - 1,3 миллиона.

Радек
источник
Я могу дать вам подход, который на несколько порядков быстрее, но он потребует от вас использовать индекс kngist postgresql 9 вместо пространственного ...
Ragi Yaser Burhum
на самом деле быстрее, чем GRASS, ArcGIS, QGIS, SQLServer и в значительной степени любая другая пространственная ГБ db / Desktop (хотя я и не пробовал использовать функциональность ближайшего соседа Oracle). Просто дайте мне знать, если это вариант.
Раги Язер Бурхум
@Ragi: Я знаю, что PostGIS будет гораздо более эффективным способом решения этой проблемы. Однако конечной целью этого упражнения будет создание небольшого портативного приложения, и в этом случае SpatiaLite станет победителем.
Радек
Какая у вас платформа для разработки вашего портативного приложения?
Аллан Адэйр
@Allan: В данный момент работает над Windows Server 2008 и Ubuntu.
Радек

Ответы:

5

Я только что проверил этот SQL, и он работает:

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

Как вы можете прочитать здесь: «Наивный способ выполнить запрос ближайшего соседа - упорядочить таблицу кандидатов по расстоянию от геометрии запроса, а затем взять запись с наименьшим расстоянием».

С наилучшими пожеланиями,

Andrea

aborruso
источник
Я пытаюсь использовать этот запрос, но получаю неожиданные результаты - я получаю результирующую таблицу, но с идентификаторами для строк, которые я вижу, я не являюсь ближайшим соседом. Я пытаюсь найти ближайшую линию в многострочном строковом слое к каждой точке в другом слое. Я новичок в spatiaLite. Какие-либо предложения? Кроме того, я в конечном счете хочу запустить это на 1 миллион + очков
kflaw
Я также не уверен, что понимаю цель этого утверждения: ГДЕ g1.OGC_FID <> g2.OGC_FID
kflaw
Кроме того, в моем результате я получаю нулевое расстояние. Я поиграл с этой строкой: AND ST_Contains (ST_Expand (g1.geometry, 50), g2.geometry), а также удалил ее и до сих пор не получаю значения расстояния, даже если я получаю идентификатор
kflaw
6

Если вы не хотите вычислять расстояния между всеми комбинациями точек, вы можете использовать пространственный индекс для одной из таблиц:

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
Самуил
источник
Я пытался использовать опубликованное вами решение, так как мне нужно использовать пространственный индекс, но он не возвращает значений? для строки f_table_name = 'A', мне нужно заменить 'A' на фактическое имя таблицы (таблица одна)? Я пытался в любом случае, и он до сих пор ничего не возвращает, почему это может быть
KFlaw
Вы правы f_table_name = 'A'должны быть f_table_name = 'tableOne'. Обратите внимание, что этот запрос предполагает пространственный размер> 4.x (используется SpatialIndexвиртуальная таблица). Вы пытались настроить search_frameдля вашего случая использования? В приведенном выше примере предполагается, что точки находятся на максимальном расстоянии 10000 метров.
Самуил
Я поэкспериментировал со значением кадра поиска, я предполагаю, что это означает, что в пределах 10000 метров это должно работать для меня. Я на самом деле не знаю, какая версия пространственного объекта, я создал базу данных через qgis и использую графический интерфейс в qgis. Дайте мне посмотреть, смогу ли я это выяснить
kflaw
Это версия 4.1.1 с sqlite версии 3.7.17, так что должно работать? Интересно, что не так, я
проверю
3

Начиная с версии 4.4.0 SpatiaLite поддерживает индекс виртуальной таблицы KNN для проблем ближайшего соседа. Вот запрос, который находит ближайшую строку в таблице линейных линий для каждой точки в таблице точек.

SELECT k.* FROM knn k, points p
WHERE f_table_name = 'linestrings' 
AND ref_geometry = p.geometry
AND max_items = 1;
Грег Краков
источник
2

Вы можете упростить ваш запрос следующим образом.

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

Для более общего решения, возможно, стоит попытаться преобразовать эту функцию ближайшего соседа PostGIS: http://blog.mackerron.com/2011/03/postgis-nearest-neighbour/

Подземье
источник
к сожалению код приводит к:SQL error: "misuse of aggregate: MIN()"
radek
Что касается PostGIS, на сайте BostonGIS также есть несколько примеров , но до сих пор мне не удалось перевести их на SpatiaLite: /
radek