如何有效地找到日期线上的最近点?


10

我有一个带有成千上万个PostGIS POINT的PostgreSQL 9.1表。对于这些,我想在另一个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子午线附近巨大)。我不知道这是否会在您的应用程序中导致严重的问题,但是在其他许多情况下却会导致问题,并且解决方法无法解决错误。
ub

很好,但是在这种情况下,客户端应用程序将不会执行其他“最接近”的计算-它只会获取与查询返回的网格点相关的一些数据。
EM0

Answers:


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

令我惊讶的是,每行都要调用的该函数比原始窗口函数要快,但是却快了十倍。PostgreSQL的性能确实是一门妖术!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.