Як ефективно знайти найближчу точку над тушкою?


10

У мене є таблиця PostgreSQL 9.1 із сотнями тисяч POGIS POINT. Для кожного з них я хотів би знайти найближчу точку в іншій таблиці POINT. Бали у другій таблиці являють собою сітку у всьому світі, тому я знаю, що завжди буде матч у межах 1 градуса. Це запит, який я зараз використовую, який використовує індекси GIST, тому він досить швидкий (всього близько 30 секунд).

SELECT DISTINCT ON (p.id)
    p.id, ST_AsText(p.pos)
    , ST_AsText(first_value(g.location) OVER (PARTITION BY p.id ORDER BY ST_Distance(p.pos, g.location::geography)))
FROM point p
JOIN grid g ON ST_DWithin(p.pos::geometry, g.location, 1)

Єдина проблема - це дателін. Точки сітки мають лише широту 180, а не -180. При використанні версії геометрії ST_Distance це не повертає точки з іншого боку дателіни. Напр. якщо p.pos є POINT(-179.88056 -16.68833)найближчою точкою сітки POINT(180 -16.25), але вищезазначений запит не повертає її. Який найкращий спосіб це виправити?

Я не хочу мати двох координат для однієї точки сітки (-180 та +180). Я спробував додати свою функцію, яка перевіряє цей конкретний випадок, але потім запит не повертається через 5 хвилин, можливо, тому що він більше не може використовувати індекс. Я також спробував використовувати географічну версію ST_DWithin, і запит також не повернувся через 5 хвилин.


Хороше запитання (і розумний злом у вашій відповіді!). Потрібно дивуватися: якщо програмне забезпечення не в змозі визнати, що -180 = 180 для довготи, то воно, ймовірно, робить вигляд, що це прогнозовані координати і використовує алгоритми Евкліда для пошуку найближчих точок, що збирається створювати помилки (тонкі поряд екватора, величезних біля полюсів і меридіанів + -180). Я не знаю, чи це призводить до значних проблем у вашій програмі, але у багатьох інших це буде, і це не допоможе виправити помилки.
whuber

Добре, але в цьому випадку клієнтська програма не буде робити інших "найближчих" обчислень - вона просто отримає деякі дані, пов'язані з точкою сітки, повернутою з мого запиту.
EM0

Відповіді:


6

Гаразд, я нарешті з'ясував спосіб зламати це, що не тільки працює навколо випуску, але і швидше.

CREATE OR REPLACE FUNCTION nearest_grid_point(point geography(Point))
RETURNS integer
AS $BODY$
    SELECT pointid
    FROM
    (
            -- The normal case
        SELECT pointid, location
        FROM grid
        WHERE ST_DWithin($1::geometry, location, 1)

        UNION ALL

            -- The dateline hack
        SELECT pointid, location
        FROM grid
        WHERE (ST_X($1::geometry) < -178.75 AND longitude = 180)
    ) sub
    ORDER BY ST_Distance($1, location::geography)
    LIMIT 1;
$BODY$ LANGUAGE SQL STABLE;

SELECT p.id, ST_AsText(p.pos), g.pointid, ST_AsText(g.location)
FROM point p
JOIN grid g ON nearest_grid_point(p.pos) = g.pointid

Я був дуже здивований, побачивши, що ця функція, яка викликається для кожного ряду, швидша за вихідну віконну функцію, але вона - в 10 разів швидша. Продуктивність PostgreSQL - це справді чорне мистецтво!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.