PostGIS에서 보로 노이 다이어그램 구성


12

here 에서 수정 된 코드를 사용하여 점 그리드에서 voronoi 다이어그램을 구성하려고합니다 . 이것은 내 수정 후의 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/ 의 소스 코드를 살펴볼 수도 있습니다. index .
user30184

좋은 질문입니다. 속도를 높이기 위해 참조하는 것과 동일한 질문을보고 있지만 시간이 계속 남아 있습니다. 나는 외부 문제와 관련 하여이 문제를 알지 못했습니다.
John Powell

5
우리가 가치있는 것을 위해 ST_Voronoi가 PostGIS 2.3에 출시되면 Dan Baston은 곧 코드를 확인할 것입니다. 사람들 테스트
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))하지만 다른 포인트 세트로 테스트를 계속합니다. 한편 나는 당신이 말한대로 더 일반적인 해결책을 기다릴 것입니다. :)
DamnBack

최종 출력에 게시 할 수있는 이미지가 있습니까?
Jeryl Cook

10

@ LR1234567의 제안에 따라 @dbaston 이 개발 한 새로운 ST_Voronoi 기능을 시험해 보면 @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. 이 결합 된 데이터 세트의 Voronoi 다이어그램을 계산하십시오. 세 번째 다이어그램에서 볼 수 있듯이 선체의 점은 이제 다각형을 둘러싸고 있습니다.

오목 선체의 지점 (노란색)과 선체의 버퍼에 가장 가까운 지점 (녹색)을 보여주는 다이어그램 2 다이어그램 2.

쿼리는 분명히 단순화 / 압축 될 수 있지만이 방법을 일련의 CTE로 남겨 두었습니다. 순차적으로 단계를 수행하는 것이 더 쉽기 때문입니다. 이 쿼리는 원래 데이터 세트에서 밀리 초 (dev 서버에서 평균 11ms)로 실행되는 반면 ST_Delauney를 사용한 MickyT의 답변은 동일한 서버에서 4800ms에서 실행됩니다. 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. 이 접근법에 대한 의견이 있는지 궁금하십니까?
John Powell

1
글쎄, 모든 점 들은 둘러싸는 다각형을 얻습니다. 그것은 단지 그것이 너무 크다는 것입니다. 이것들을 어떻게 작게 만들어야 하는가는 주관적이며, 외부 다각형에 필요한 것이 무엇인지 정확히 알지 못하면 "최상의"방법이 무엇인지 알기가 어렵습니다. 당신은 나에게 훌륭한 방법 인 것 같습니다. 나는 당신과 비슷한 방식으로 덜 정교한 방법을 사용하여 평균 포인트 밀도에 의해 결정된 고정 간격으로 버퍼링 된 볼록 껍질 선체를 따라 추가 포인트를 떨어 뜨립니다.
dbaston

@dbaston. 고마워, 내가 분명한 것을 놓치지 않았는지 확인하십시오. 외부 폴리곤을 내부 폴리곤 (내 경우에는 우편 번호 영역)의 크기와 일치하는 것으로 축소하는 알고리즘은 좀 더 생각해야 할 것입니다.
John Powell

@John Barça 또 다른 훌륭한 솔루션에 감사드립니다. 계산 속도는이 접근 방식을 만족시키는 것 이상입니다. 불행히도 QGIS 플러그인 내에서이 알고리즘을 사용하고 싶습니다. PostGIS 2.1 이상과 함께 즉시 작동해야합니다. 그러나 PostGIS 2.3 공식 릴리스 후에이 솔루션을 사용할 것입니다. 어쨌든 그러한 포괄적 인 답변에 감사드립니다. :)
DamnBack

@DamnBack. 천만에요 ST_Voronoi가 나오는 것에 대해 전혀 몰랐고 이전 솔루션이 훨씬 느리기 때문에 작업 에이 작업이 필요했고 실제로 귀하의 질문이 크게 도움이되었습니다. 그것을 알아내는 것도 재미있었습니다 :-)
John Powell

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 쿼리가 현재 내 요구에 가장 가깝습니다.
DamnBack

@ LR1234567. 특정 버전의 GEOS가 필요합니까? 내일 2.3과 ST_Voronoi를 테스트 할 시간이 있습니다.
존 파월

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.