จะใช้ ST_DelaunayTriangles เพื่อสร้างไดอะแกรม Voronoi ได้อย่างไร


13

(แก้ไข 2019) ST_VoronoiPolygonsมีให้ตั้งแต่ PostGIS v2.3 !


ด้วย PostGIS 2.1+ เราสามารถใช้ST_DelaunayTriangles ()เพื่อสร้างDelaunay triangulationซึ่งเป็นกราฟคู่ของแผนภาพ Voronoiและในทางทฤษฎีพวกเขามีการแปลงที่แน่นอนและย้อนกลับได้

มีสคริปต์มาตรฐาน SQL ที่ปลอดภัย พร้อมอัลกอริธึมที่เหมาะสมสำหรับการแปลง PostGIS2 Delaunay-to-Voronoi นี้หรือไม่?


อ้างอิงอื่น ๆ : 1 , 2


คือgist.github.com/djq/4714788เรียงลำดับของสิ่งที่คุณมีอยู่หลังจากที่?
MickyT

ผมคิดว่าเขาต้องการการใช้งาน SQL หมดจดใช้ ST_DelaunayTriangles ()
ราฟาเอล

ดูคำตอบนี้จะติดตั้งST_DelaunayTrianglesใน Linux Debian Stable
Peter Krauss

! ST_VoronoiPolygonsมีให้ตั้งแต่ PostGIS 2.3
Peter Krauss

คำตอบ:


23

คำถามต่อไปนี้ดูเหมือนว่าจะทำชุดรูปหลายเหลี่ยมที่สมเหตุสมผลของ voronoi โดยเริ่มจากรูปสามเหลี่ยม Delaunay

ฉันไม่ใช่ผู้ใช้ 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

สร้าง Delaunay Triangles จากรูปทรงเรขาคณิตอินพุต

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 outputs ขอบแต่ละและ ID (เส้นทาง) ของรูปสามเหลี่ยมที่มันเป็น

ขั้นตอนที่ 4

'Outer Join' ตาราง 'ขอบ' กับตัวเองที่มีขอบเท่ากันสำหรับรูปสามเหลี่ยมที่แตกต่างกัน (ขอบด้านใน)

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 / Kenneth Sloa ... ทำไมไม่แก้ปัญหาโดยตรง
Peter Krauss

ฉันสวยมากที่ทำเส้นตั้งฉากตั้งฉากกับขอบด้านนอกโดยไม่ต้องใช้คณิตศาสตร์ทั้งหมด :) ฉันจะเพิ่มคำอธิบายของขั้นตอนที่ฉันทำเพื่อตอบ
MickyT

ภาพประกอบและคำอธิบายที่ดีสอนได้ดีมาก!   คุณโพสต์ทุกสิ่งที่ฉันต้องการ (!) แต่วันนี้ฉันไม่มี Postgis2.1 เพื่อทดสอบ ... ฉันสามารถตรวจสอบที่นี่ (ตามความคิดเห็น) คำถามที่ผู้ใดสามารถตอบได้โดยการทดสอบ?   1) ST_Polygonize "สร้าง GeometryCollection ที่มีรูปหลายเหลี่ยมที่เป็นไปได้" พวกมันคือเซลล์ Voronoi ทั้งหมดถูกต้องหรือไม่   2)เกี่ยวกับประสิทธิภาพคุณคิดว่าวิธีแก้ปัญหาเซนทรอยด์ของคุณมีเวลาซีพียูใกล้เคียงกับ "คณิตศาสตร์ทั้งหมดของการคำนวณเส้นแบ่งครึ่งตั้งฉาก"?
Peter Krauss

@PeterKrauss 1) ST_polygonize สร้างเซลล์ voronoi จากงานสาย เคล็ดลับก็คือเพื่อให้แน่ใจว่าทุกการทำงานของสายจะแบ่งที่โหนด 2) ฉันไม่คิดว่าจะมีความแตกต่างกันมากระหว่างการคำนวณเส้นแบ่งและการใช้ ST_Centroid ในบรรทัด แต่มันจะต้องมีการทดสอบ
MickyT

ดูคำตอบนี้จะติดตั้งST_DelaunayTrianglesใน Linux Debian Stable
Peter Krauss
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.