PostGISでのボロノイ図の作成


12

ここからの修正コードを使用して、点のグリッドからボロノイ図を作成しようとしています。これは私の変更後のSQLクエリです。

DROP TABLE IF EXISTS example.voronoi;
WITH 
    -- Sample set of points to work with
    Sample AS (SELECT ST_SetSRID(ST_Union(geom), 0) geom FROM example."MeshPoints2d"),
    -- 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_SetSRID((ST_Dump(ST_Polygonize(ST_Node(ST_LineMerge(ST_Union(v, ST_ExteriorRing(ST_ConvexHull(v)))))))).geom, 2180)
INTO example.voronoi
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

以下-私のクエリの結果。 ここに画像の説明を入力してください

ご覧のとおり、一意のポリゴンを持たないハイライトされたポイントを除き、「ほぼ」正しいボロノイ図が得られます。以下は、QGISアルゴリズムが生成するものと、クエリから取得したいものです。私のコードのどこに問題があるのでしょうか?

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


おそらく、SpatiaLite関数 "VoronojDiagram" gaia-gis.it/gaia-sins/spatialite-sql-latest.htmlの結果を比較して、gaia-gis.it / fossil / libspatialite /のソースコードを確認することもできます。インデックス
user30184

いい質問です。私はあなたが参照するのと同じ質問を、それをスピードアップする目的で見てきましたが、時間を使い果たし続けています。私は外側の点でこの問題を知りませんでした。
ジョンパウエル

5
ST_VoronoiのPostGIS 2.3での価値は、Dan Bastonがすぐにコードをチェックインすることになります。trac.osgeo.org/ postgis / ticket / 2259 はほとんど必要ないようです。人々テスト
LR1234567

使用しているポイントのセットを投稿できますか?私はこの上で自分自身をテストするためのビットをやって気になり
MickyT

@MickyTここ に私のデータへのリンクがあります。データSRIDは2180です
DamnBack

回答:


6

これで問題のデータのクエリに関する当面の問題は修正されますが、一般的な使用法のソリューションとしては満足できません。可能な場合は、これと以前の回答を再検討します。

問題は、元のクエリがボロノイエッジの凸包を使用して、ボロノイポリゴンの外部エッジを決定することでした。これは、ボロノイのエッジの一部が本来あるべきときに外部に到達していないことを意味していました。凹型船体の機能を使用することを検討しましたが、期待どおりに機能しませんでした。
簡単な修正として、ボロノイエッジの閉じたセットと元のエッジの周りのバッファの周りに構築されるように凸包を変更しました。閉じていないボロノイエッジは、それらが外部と交差し、ポリゴンを構築するために使用されることを確認するために、長い距離に拡張されます。バッファパラメータをいろいろ試してみてください。

WITH 
    -- Sample set of points to work with
    Sample AS (SELECT ST_SetSRID(ST_Union(geom), 0) geom FROM MeshPoints2d),
    -- 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_SetSRID((ST_Dump(ST_Polygonize(ST_Node(ST_LineMerge(ST_Union(v, (SELECT ST_ExteriorRing(ST_ConvexHull(ST_Union(ST_Union(ST_Buffer(edge,20),ct)))) FROM Edges))))))).geom, 2180) geom
INTO voronoi
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)) * 200),ST_Y(x.ct) + ((ST_Y(ST_Centroid(x.edge)) - ST_Y(x.ct)) * 200))
            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;

問題の説明と迅速な修正をありがとう!それは私のデータで動作します(のために少し遅くなりますST_Union(ST_Buffer(geom)))が、他のポイントのセットでテストを続けます。一方、私はあなたが言ったように待っています-より一般的な解決策。:)
ダムバック

最終出力に投稿できる画像はありますか?
ジェリルクック

10

@dbastonによって開発された新しいST_Voronoi機能を試すための@ LR1234567の提案に従い、@ MickyT の元の驚くべき答え(OPの質問で述べられているように)と元のデータの使用を次のように簡略化できるようになりました:

WITH voronoi (vor) AS 
     (SELECT ST_Dump(ST_Voronoi(ST_Collect(geom))) FROM meshpoints)
SELECT (vor).path, (vor).geom FROM voronoi;

これは、OPの質問と同じこの出力になります。

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

ただし、これには同じ問題があり、MickyTの答えで修正されました。凹型の船体上のポイントは囲んでいるポリゴンを取得しません(アルゴリズムから予想されるように)。次の一連の手順を使用したクエリでこの問題を修正しました。

  1. 入力ポイントの凹包を計算します-凹包上の点は、出力ボロノイ図に無制限のポリゴンがある点です。
  2. 凹型ハル上の元のポイントを見つけます(下の図2の黄色のポイント)。
  3. 凹包をバッファリングします(バッファ距離は任意であり、おそらく入力データからより最適に見つけることができますか?)。
  4. ステップ2のポイントに最も近い凹包のバッファー上の最も近いポイントを見つけます。これらは、下の図で緑色で示されています。
  5. これらのポイントを元のデータセットに追加します
  6. この結合データセットのボロノイ図を計算します。3番目の図に見られるように、船体上の点は多角形を囲むようになりました。

凹型船体上のポイント(黄色)と船体上のバッファに最も近いポイント(緑色)を示す図2。 図2。

クエリは明らかに単純化/圧縮できますが、この方法は一連のCTEであるため、この方法で順次ステップを実行する方が簡単です。ST_Delauneyを使用したMickyTの回答は同じサーバー上で4800ミリ秒で実行されますが、このクエリは元のデータセットでミリ秒単位で実行されます(開発サーバーでは平均11ミリ秒)。DBastonは、三角測量ルーチンの強化により、GEOSの現在のトランク3.6devに対して構築することで、さらに桁違いの速度向上が得られると主張しています。

WITH 
  conv_hull(geom) AS 
        (SELECT ST_Concavehull(ST_Union(geom), 1) FROM meshpoints), 
  edge_points(points) AS 
        (SELECT mp.geom FROM meshpoints mp, conv_hull ch 
        WHERE ST_Touches(ch.geom, mp.geom)), 
  buffered_points(geom) AS
        (SELECT ST_Buffer(geom, 100) as geom FROM conv_hull),
  closest_points(points) AS
        (SELECT 
              ST_Closestpoint(
                   ST_Exteriorring(bp.geom), ep.points) as points,
             ep.points as epoints 
         FROM buffered_points bp, edge_points ep),
  combined_points(points) AS
        (SELECT points FROM closest_points 
        UNION SELECT geom FROM meshpoints),
  voronoi (vor) AS 
       (SELECT 
            ST_Dump(
                  ST_Voronoi(
                    ST_Collect(points))) as geom 
        FROM combined_points)
 SELECT 
     (vor).path[1] as id, 
     (vor).geom 
 FROM voronoi;

ポリゴンで囲まれたすべてのポイントを示す図3 図3

注:現在、ST_Voronoiには、ソース(バージョン2.3またはトランク)からPostgisをビルドし、GEOS 3.5以降に対してリンクすることが含まれます。

編集: Amazon Web ServicesにインストールされているPostgis 2.3を調べたところ、関数名がST_VoronoiPolygonsになったようです。

このクエリ/アルゴリズムが改善されることは間違いありません。提案を歓迎します。


@dbaston。このアプローチについて何かコメントがありますか?
ジョンパウエル

1
まあ、すべてのポイント囲んでいるポリゴンを取得しますが、それは単に不均衡に大きいだけです。これらを小さくする方法が主観的であり、外側のポリゴンに何が望ましいかを正確に知らなければ、「最良の」方法が何であるかを知ることは困難です。あなたの方法は私には良い方法のようです。私はあなたの精神に似たそれほど洗練されていない方法を使用し、平均点密度によって決定される固定間隔でバッファー付き凸包の境界に沿って余分な点をドロップしました。
dbaston

@dbaston。ありがとう、明らかな何かを見逃していないことを確認してください。外側のポリゴンを内側のサイズ(私の場合は郵便番号領域)のサイズに合わせて縮小するアルゴリズムは、もう少し考えなければなりません。
ジョンパウエル

@JohnBarça別の素晴らしいソリューションをありがとう。計算速度は、このアプローチで満たす以上のものです。残念ながら、QGISプラグイン内でこのアルゴリズムを使用したいので、PostGIS 2.1以降ですぐに動作する必要があります。しかし、PostGIS 2.3の公式リリース後にこのソリューションを使用することは確かです。とにかく、このような包括的な答えをありがとう。:)
ダムバック

@DamnBack。どういたしまして。ST_Voronoiがリリースされることを知らなかったので、仕事のためにこれが必要でしたが、あなたの質問は実際に非常に役立ちました。それを理解するのも楽しかったです:
ジョンパウエル

3

PostGIS 2.3にアクセスできる場合は、最近コミットした新しいST_Voronoi関数を試してください。

http://postgis.net/docs/manual-dev/ST_Voronoi.html

Windows用のプリコンパイルビルドがあります-http ://postgis.net/windows_downloads/


最新の組み込みST_Voronoi関数があるという情報をありがとう-私はそれをチェックします。残念ながら、PostGIS 2.1以降のバージョンで動作するソリューションが必要なので、@ MickyTクエリは現時点で私のニーズに最も近いものです。
ダムバック

@ LR1234567。これにはGEOSの特定のバージョンが必要ですか?明日、2.3とST_Voronoiをテストする時間があります。
ジョンパウエル

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