K-запрос ближайшего соседа в PostGIS
Я использую следующий запрос ближайшего соседа в PostGIS :
SELECT g1.gid g2.gid FROM points as g1, polygons g2
WHERE g1.gid <> g2.gid
ORDER BY g1.gid, ST_Distance(g1.the_geom,g2.the_geom)
LIMIT k;
Теперь, когда я создал индексы на the_geom, а также столбец gid на обеих таблицах, этот запрос занимает гораздо больше времени, чем другие пространственные запросы, связанные с пространственными соединениями b/w двух таблиц.
есть ли лучший способ, чтобы найти k ближайших соседей? Я использую PostGIS.
и еще один запрос, который занимает необычно много времени, несмотря на создание индексов в геометрии колонка:
select g1.gid , g2.gid from polygons as g1 , polygons as g2
where st_area(g1.the_geom) > st_area(g2.the_geom) ;
Я считаю, что эти запросы не выиграли от индексов gist, но почему?
тогда как этот запрос:
select a.polyid , sum(length(b.the_geom)) from polygon as a , roads as b
where st_intersects(a.the_geom , b.the_geom);
возвращает результат через некоторое время, несмотря на то, что таблица "дороги" намного больше, чем таблица полигонов или точек, а также включает более сложные пространственные операторы.
5 ответов
всего несколько мыслей о вашей проблеме:
st_distance, а также st_area не могут использовать индексы. Это связано с тем, что обе функции не могут быть сведены к таким вопросам, как "является ли a внутри b?"или" совпадают ли a и b?". Еще более конкретно: GIST-индексы могут работать только на ограничивающих коробках двух объектов.
для получения дополнительной информации об этом вы просто можете посмотреть в руководство postgis указано, что пример с st_distance и как запрос может быть улучшено, чтобы работать лучше.
однако это не решает проблему K-ближайшего соседа. Для этого, прямо сейчас у меня нет хорошей идеи, как улучшить производительность запроса. Единственный шанс, который я вижу, - это предположить, что k ближайших соседей всегда находятся на расстоянии ниже x метров. Затем вы можете использовать аналогичный подход, как это сделано в руководстве postgis.
ваш второй запрос может быть немного ускорен. В настоящее время вы вычисляете площадь для каждого объекта в Таблица 1 так часто, как таблица имеет строки-стратегия сначала присоединиться к данным, а затем выбрать на основе этой функции. Вы можете значительно уменьшить количество вычислений области, предварительно вычисляя область:
WITH polygonareas AS (
SELECT gid, the_geom, st_area(the_geom) AS area
FROM polygons
)
SELECT g1.gid, g2.gid
FROM polygonareas as g1 , polygonareas as g2
WHERE g1.area > g2.area;
ваш третий запрос может быть значительно оптимизирован с помощью ограничивающих полей: когда ограничивающие поля двух объектов не перекрываются, нет никакого способа сделать объекты. Это позволяет использовать данный индекс и, следовательно, огромный прирост производительности.
С конец сентября 2011, PostGIS поддерживает индексированные запросы ближайшего соседа через специальный оператор(ы), используемый в предложении ORDER BY:
SELECT name, gid
FROM geonames
ORDER BY geom <-> st_setsrid(st_makepoint(-90,40),4326)
LIMIT 10;
...вернет 10 объектов, чьи geom
ближе -90,40
масштабируемым способом. Еще несколько деталей (варианты и советы) в этом объявлении в должности и использовать и у операторы также теперь задокументирован в официальном PostGIS 2.0 ссылка. (Основное различие между ними в том, что <->
сравнивает центроиды формы и <#>
сравнивает свои границы - нет разницы для точек, другие фигуры выбирают то, что подходит для ваших запросов.)
Вам может понадобиться индекс KNN, который, надеюсь, скоро будет доступен в PostGIS 2.x и PostgreSQL 9.1: см. http://blog.opengeo.org/tag/knn/
предполагая, что у вас есть полигоны P point и g, ваш исходный запрос:
SELECT g1.gid, g2.gid FROM points as g1, polygons g2
WHERE g1.gid <> g2.gid
ORDER BY g1.gid, ST_Distance(g1.the_geom,g2.the_geom)
LIMIT k;
возвращает k ближайших соседей в наборе p x G. Запрос может использовать индексы, но он все равно должен упорядочить весь набор p x g, чтобы найти k строк с наименьшим расстоянием. Вместо этого вы хотите следующее:
SELECT g1.gid,
(SELECT g2.gid FROM polygons g2
--prevents you from finding every nearest neighbour twice
WHERE g1.gid < g2.gid
--ORDER BY gid is erroneous if you want to limit by the distance
ORDER BY ST_Distance(g1.the_geom,g2.the_geom)
LIMIT k)
FROM points as g1;
вы можете сделать это с индексом KNN и боковым соединением.
SELECT v.gid, v2.gid,st_distance(v.the_geom, v2.the_geom)
FROM geonames v,
lateral(select *
from geonames v2
where v2.id<>v.id
ORDER BY v.the_geom <-> v2.the_geom LIMIT 10) v2
where v.gid in (...) - or other filtering condition