Мне нужно получить по каждому элементу на одной таблице ближайшую точку другой таблицы. Первая таблица содержит дорожные знаки, а вторая - входные залы города. Дело в том, что я не могу использовать функцию ST_ClosestPoint, и мне нужно использовать функцию ST_Distance и получить запись min (ST_distance), но я довольно застрял при построении запроса.
CREATE TABLE traffic_signs
(
id numeric(8,0) ),
"GEOMETRY" geometry,
CONSTRAINT traffic_signs_pkey PRIMARY KEY (id),
CONSTRAINT traffic_signs_id_key UNIQUE (id)
)
WITH (
OIDS=TRUE
);
CREATE TABLE entrance_halls
(
id numeric(8,0) ),
"GEOMETRY" geometry,
CONSTRAINT entrance_halls_pkey PRIMARY KEY (id),
CONSTRAINT entrance_halls_id_key UNIQUE (id)
)
WITH (
OIDS=TRUE
);
Мне нужно получить идентификатор ближайшего entrnce_hall каждого трафика_sign.
Мой запрос до сих пор:
SELECT senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY") as dist
FROM traffic_signs As senal, entrance_halls As port
ORDER BY senal.id,port.id,ST_Distance(port."GEOMETRY",senal."GEOMETRY")
При этом я получаю расстояние от каждого traffic_sign до каждого entry_hall. Но как я могу получить только минимальное расстояние?
С Уважением,
Ответы:
Вы почти там. Есть небольшая хитрость, которая заключается в использовании отдельного оператора Postgres , который будет возвращать первое совпадение каждой комбинации - так как вы упорядочиваете по ST_Distance, эффективно он будет возвращать ближайшую точку от каждого сенала к каждому порту.
Если вы знаете, что минимальное расстояние в каждом случае составляет не более некоторой величины x (и у вас есть пространственный индекс в ваших таблицах), вы можете ускорить это, поставив
WHERE ST_DWithin(port."GEOMETRY", senal."GEOMETRY", distance)
, например, если все минимальные расстояния известны как не более 10км, тогда:Очевидно, что это нужно использовать с осторожностью, так как, если минимальное расстояние больше, вы просто не получите строки для этой комбинации сенала и порта.
Примечание: порядок за порядком должен совпадать с отличным по порядку, что имеет смысл, так как отличным является выбор первой отличной группы на основе некоторого упорядочения.
Предполагается, что у вас есть пространственный индекс в обеих таблицах.
РЕДАКТИРОВАТЬ 1 . Есть еще одна опция, которая заключается в использовании операторов Postgres <-> и <#> (вычисления расстояния от центральной точки и ограничивающей рамки соответственно), которые более эффективно используют пространственный индекс и не требуют взлома ST_DWithin, чтобы избежать n ^ 2 сравнения. Есть хорошая статья в блоге, объясняющая, как они работают. Общее, на что следует обратить внимание, это то, что эти два оператора работают в предложении ORDER BY.
РЕДАКТИРОВАТЬ 2 . Поскольку этому вопросу уделяется много внимания, и k-ближайшие соседи (kNN), как правило, представляют собой сложную проблему (с точки зрения алгоритмического времени выполнения) в ГИС, представляется целесообразным несколько расширить исходную область этого вопроса.
Стандартный способ найти x ближайших соседей одного объекта - использовать LATERAL JOIN (концептуально аналогично a для каждого цикла). Заимствуя бесстыдно из ответа dbaston , вы бы сделали что-то вроде:
Итак, если вы хотите найти ближайшие 10 портов, упорядоченные по расстоянию, вам просто нужно изменить предложение LIMIT в боковом подзапросе. Это гораздо сложнее обойтись без LATERAL JOINS и включает в себя использование логики типа ARRAY. Хотя этот подход работает хорошо, его можно значительно ускорить, если вы знаете, что вам нужно искать только на определенном расстоянии. В этом случае вы можете использовать ST_DWithin (signs.geom, ports.geom, 1000) в подзапросе, который из-за способа индексации работает с оператором <-> - одна из геометрий должна быть константой, а не ссылка на столбец - может быть намного быстрее. Так, например, чтобы получить 3 ближайших порта, в пределах 10 км, вы могли бы написать что-то вроде следующего.
Как всегда, использование будет зависеть от вашего распределения данных и запросов, поэтому EXPLAIN - ваш лучший друг.
Наконец, есть небольшая ошибка, если вы используете LEFT вместо CROSS JOIN LATERAL в том смысле, что вы должны добавить ON TRUE после псевдонима боковых запросов, например,
источник
Это можно сделать с помощью
LATERAL JOIN
PostgreSQL 9.3+:источник
Подход с перекрестным соединением не использует индексы и требует много памяти. Таким образом, у вас есть два варианта. До 9.3 вы использовали коррелированный подзапрос. 9.3+ вы можете использовать
LATERAL JOIN
.KNN GIST с боковым поворотом Скоро появится в базе данных рядом с вами
(точные запросы, чтобы следовать скоро)
источник
ST_DISTANCE()
найти ближайший полигон, а перекрестное соединение приводит к нехватке памяти на сервере. Ближайший запрос полигонов по-прежнему не решен AFAIK.@ Джон Барса
ЗАКАЗАТЬ ПО неверно!
Правильно
в противном случае он вернет не ближайший, а только тот, у которого маленький идентификатор порта
источник
SELECT DISTINCT ON (points.id) points.id, lines.id, ST_Distance(lines.geom, points.geom) as dist FROM development.passed_entries As points, development."de_muc_rawSections_cleaned" As lines ORDER BY points.id, ST_Distance(lines.geom, points.geom),lines.id;