ST_DelaunayTrianglesを使用してボロノイ図を作成する方法


12

(編集2019)PostGIS v2.3 以降で使用可能なST_VoronoiPolygons


PostGIS 2.1+では、ST_DelaunayTriangles()を使用してDelaunay三角形分割を生成できます。これは、そのボロノイ線図の双対グラフであり、理論的には、正確かつ可逆的な変換を行います。

このPostGIS2 DelaunayからVoronoiへの変換のために、最適化されたアルゴリズムを備え安全な SQL標準スクリプトありますか?


その他の引用文献:12


あるgist.github.com/djq/4714788あなたが後にしているもののようなもの?
MickyT 14

私は彼がST_DelaunayTrianglesを使用して純粋なSQLの実装を望んでいると思います()
ラファエル

ST_DelaunayTrianglesLinux Debian Stableにインストールするには、この回答参照してください
ピータークラウス14年

ST_VoronoiPolygonsはPostGIS 2.3以降で利用可能
Peter Krauss

回答:


22

次のクエリは、ドローネ三角形から始まる妥当なボロノイポリゴンのセットを実行するようです。

私はPostgresの大ユーザーではないので、おそらくかなり改善できるでしょう。

WITH 
    -- Sample set of points to work with
    Sample AS (SELECT ST_GeomFromText('MULTIPOINT (12 5, 5 7, 2 5, 19 6, 19 13, 15 18, 10 20, 4 18, 0 13, 0 6, 4 1, 10 0, 15 1, 19 6)') geom),
    -- Build edges and circumscribe points to generate a centroid
    Edges AS (
    SELECT id,
        UNNEST(ARRAY['e1','e2','e3']) EdgeName,
        UNNEST(ARRAY[
            ST_MakeLine(p1,p2) ,
            ST_MakeLine(p2,p3) ,
            ST_MakeLine(p3,p1)]) Edge,
        ST_Centroid(ST_ConvexHull(ST_Union(-- Done this way due to issues I had with LineToCurve
            ST_CurveToLine(REPLACE(ST_AsText(ST_LineMerge(ST_Union(ST_MakeLine(p1,p2),ST_MakeLine(p2,p3)))),'LINE','CIRCULAR'),15),
            ST_CurveToLine(REPLACE(ST_AsText(ST_LineMerge(ST_Union(ST_MakeLine(p2,p3),ST_MakeLine(p3,p1)))),'LINE','CIRCULAR'),15)
        ))) ct      
    FROM    (
        -- Decompose to points
        SELECT id,
            ST_PointN(g,1) p1,
            ST_PointN(g,2) p2,
            ST_PointN(g,3) p3
        FROM    (
            SELECT (gd).Path id, ST_ExteriorRing((gd).Geom) g -- ID andmake triangle a linestring
            FROM (SELECT (ST_Dump(ST_DelaunayTriangles(geom))) gd FROM Sample) a -- Get Delaunay Triangles
            )b
        ) c
    )
SELECT ST_Polygonize(ST_Node(ST_LineMerge(ST_Union(v, ST_ExteriorRing(ST_ConvexHull(v))))))
FROM (
    SELECT  -- Create voronoi edges and reduce to a multilinestring
        ST_LineMerge(ST_Union(ST_MakeLine(
        x.ct,
        CASE 
        WHEN y.id IS NULL THEN
            CASE WHEN ST_Within(
                x.ct,
                (SELECT ST_ConvexHull(geom) FROM sample)) THEN -- Don't draw lines back towards the original set
                -- Project line out twice the distance from convex hull
                ST_MakePoint(ST_X(x.ct) + ((ST_X(ST_Centroid(x.edge)) - ST_X(x.ct)) * 2),ST_Y(x.ct) + ((ST_Y(ST_Centroid(x.edge)) - ST_Y(x.ct)) * 2))
            END
        ELSE 
            y.ct
        END
        ))) v
    FROM    Edges x 
        LEFT OUTER JOIN -- Self Join based on edges
        Edges y ON x.id <> y.id AND ST_Equals(x.edge,y.edge)
    ) z;

これにより、クエリに含まれるサンプルポイントに対して次のポリゴンセットが生成されます。 ここに画像の説明を入力してください

クエリの説明

ステップ1

入力ジオメトリからドロネー三角形を作成します

SELECT (gd).Path id, ST_ExteriorRing((gd).Geom) g -- ID and make triangle a linestring
FROM (SELECT (ST_Dump(ST_DelaunayTriangles(geom))) gd FROM Sample) a -- Get Delaunay Triangles

ステップ2

三角形ノードを分解し、エッジを作成できるようにします。エッジを取得するより良い方法があるはずだと思いますが、見つけられませんでした。

SELECT ...
        ST_MakeLine(p1,p2) ,
        ST_MakeLine(p2,p3) ,
        ST_MakeLine(p3,p1)
        ...
FROM    (
    -- Decompose to points
    SELECT id,
        ST_PointN(g,1) p1,
        ST_PointN(g,2) p2,
        ST_PointN(g,3) p3
    FROM    (
        ... Step 1...
        )b
    ) c

ここに画像の説明を入力してください

ステップ3

各三角形の外接円を作成し、重心を見つけます

SELECT ... Step 2 ...
    ST_Centroid(ST_ConvexHull(ST_Union(-- Done this way due to issues I had with LineToCurve
        ST_CurveToLine(REPLACE(ST_AsText(ST_LineMerge(ST_Union(ST_MakeLine(p1,p2),ST_MakeLine(p2,p3)))),'LINE','CIRCULAR'),15),
        ST_CurveToLine(REPLACE(ST_AsText(ST_LineMerge(ST_Union(ST_MakeLine(p2,p3),ST_MakeLine(p3,p1)))),'LINE','CIRCULAR'),15)
    ))) ct      
FROM    (
    -- Decompose to points
    SELECT id,
        ST_PointN(g,1) p1,
        ST_PointN(g,2) p2,
        ST_PointN(g,3) p3
    FROM    (
        ... Step 1...
        )b
    ) c

ここに画像の説明を入力してください

EdgesCTEは、それが属する三角形の各辺とID(パス)を出力します。

ステップ4

異なる三角形の等しいエッジ(内部エッジ)が存在する「エッジ」テーブルをそれ自体に「外部結合」します。

SELECT  
    ...
    ST_MakeLine(
    x.ct, -- Circumscribed Circle centroid
    CASE 
    WHEN y.id IS NULL THEN
        CASE WHEN ST_Within( -- Don't draw lines back towards the original set
            x.ct,
            (SELECT ST_ConvexHull(geom) FROM sample)) THEN
            -- Project line out twice the distance from convex hull
            ST_MakePoint(
                ST_X(x.ct) + ((ST_X(ST_Centroid(x.edge)) - ST_X(x.ct)) * 2),
                T_Y(x.ct) + ((ST_Y(ST_Centroid(x.edge)) - ST_Y(x.ct)) * 2)
            )
        END
    ELSE 
        y.ct -- Centroid of triangle with common edge
    END
    ))) v
FROM    Edges x 
    LEFT OUTER JOIN -- Self Join based on edges
    Edges y ON x.id <> y.id AND ST_Equals(x.edge,y.edge)

共通のエッジがある場合、それぞれの重心の間に線を引きます

ここに画像の説明を入力してください

エッジが結合されていない場合(外側)、重心からエッジの中心を通る線を引きます。これは、円の重心が三角形のセット内にある場合にのみ行います。

ここに画像の説明を入力してください

ステップ5

描画された線の凸包を線として取得します。結合してすべての行をマージします。ポリゴン化できるトポロジセットが得られるように、ラインセットをノード化します。

SELECT ST_Polygonize(ST_Node(ST_LineMerge(ST_Union(v, ST_ExteriorRing(ST_ConvexHull(v))))))

ここに画像の説明を入力してください


良い手がかり、おそらく解決策(!)。私はテストする必要がありますが、今...分析することはできません:あなたは使用ST_ConvexHullしてST_Centroid直接アルゴリズムのように、代わりに「垂直二等分線は、」私が提案しref1の/ケネスSloa ...なぜ直接解?
ピータークラウス14

私はかなりちょうどすべての数学なしで、外縁のための垂直二等分線をやっている:)私は答えにかかった手順の説明を追加します
MickyT

良いイラストと説明、非常に教訓的!   必要なものはすべて投稿しましたが(!)、今日はテストするPostgis2.1がありません...テストで答えられる質問を(コメントとして)ここで確認できますか?   1) ST_Polygonizeは「可能なポリゴンを含むGeometryCollectionを作成します」、それらはすべてボロノイセルです、正しいですか?   2)パフォーマンスについては、重心ベースのソリューションのCPU時間は「垂直二等分線計算のすべての計算」よりも似ていると思いますか?
ピータークラウス14年

@PeterKrauss 1)ST_p​​olygonizeは、ラインワークからボロノイセルを作成します。これを使用するコツは、すべてのライン作業がノードで分割されるようにすることです。2)二分法の計算とラインでのST_Centroidの使用との間に大きな違いはないと思います。ただし、テストする必要があります。
MickyT 14年

ST_DelaunayTrianglesLinux Debian Stableにインストールするには、この回答参照してください
ピータークラウス14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.