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