日付変更線上の最も近い点を効率的に見つける方法は?


10

数十万のPostGIS POINTを持つPostgreSQL 9.1テーブルがあります。これらのそれぞれについて、POINTの別のテーブルで最も近いポイントを見つけたいと思います。2番目の表の点は、全世界のグリッドを表しています。そのため、常に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)が、上記のクエリはそれを返しません。これを修正する最良の方法は何ですか?

1つのグリッドポイントに2つの座標(-180と+180)を使いたくありません。この特定のケースをチェックする独自の関数を追加しようとしましたが、おそらくインデックスを使用できなくなったため、クエリが5分で返されません。ST_DWithinの地理バージョンも使用してみましたが、そのクエリも5分後に返さ​​れませんでした。


良い質問です(そして返信で巧妙なハックを!)。ただし、ソフトウェアが経度の-180 = 180を認識できない場合は、これらが投影座標であると偽っており、ユークリッドアルゴリズムを使用して最も近い点を検出しているため、エラーが発生します(微妙に近い)赤道、極と+ -180子午線の近くで巨大です)。それがアプリケーションに重大な問題を引き起こすかどうかはわかりませんが、他の多くのケースではそれが起こり、その回避策はエラーを解決しません。
whuber

良い点ですが、この場合、クライアントアプリケーションは他の「最も近い」計算を行いません。クエリから返されたグリッドポイントに関連付けられたデータを取得するだけです。
EM0

回答:


6

OK、ハッキングする方法を見つけました。日付変更の問題を回避するだけでなく、より高速です。

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のパフォーマンスは、本当に黒字です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.