如何使用ST_DelaunayTriangles构造Voronoi图?


13

(编辑2019)ST_VoronoiPolygons 自PostGIS v2.3 可用!


在PostGIS 2.1+中,我们可以使用ST_DelaunayTriangles()生成Delaunay三角剖分,这是其Voronoi图的偶图,并且从理论上讲,它们具有精确且可逆的转换。

是否有安全的 SQL标准的脚本优化的算法存在这个PostGIS2德劳内到维诺转换


其他裁判:12


gist.github.com/djq/4714788之类的事情,你是后?
MickyT 2014年

我认为他想要使用ST_DelaunayTriangles()的纯SQL实现
raphael

请参阅此答案ST_DelaunayTriangles在Linux Debian Stable中安装。
彼得·克劳斯

!自PostGIS 2.3 提供ST_VoronoiPolygons
Peter Krauss

Answers:


23

以下查询似乎是从Delaunay三角形开始的一组合理的voronoi多边形。

我不是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三角形

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

在此处输入图片说明

第三步

为每个三角形建立外接圆并找到质心

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))))))

在此处输入图片说明


很好的线索,也许是一个解决方案(!)。我需要测试,但现在不能测试...分析:您使用ref1 / Kenneth Sloa建议的直接算法中的“垂直平分线” ST_ConvexHullST_Centroid而不是...为何不使用直接解决方案?
彼得·克劳斯

我几乎在做所有边缘的垂直平分线,只是没有做所有的数学运算:)我将添加对回答步骤的解释
MickyT 2014年

好的插图和解释,很有说服力!   您发布了我需要的所有内容(!),但是现在我没有Postgis2.1可以测试...我可以在此处(作为评论)检查一些人可以通过测试回答的问题吗?   1) ST_Polygonize“创建一个包含可能多边形的GeometryCollection”,它们都是Voronoi单元格,对吗?   2)关于性能,您认为基于质心的解决方案具有比“垂直二等分线计算的所有数学方法”相似的CPU时间?
彼得·克劳斯

@PeterKrauss 1)ST_p​​olygonize确实从线路工作中创建了voronoi细胞。诀窍是确保所有线路工作在节点处均被拆分。2)我认为在计算二等分和在行上使用ST_Centroid之间不会有很多区别。但这需要进行测试。
MickyT

请参阅此答案ST_DelaunayTriangles在Linux Debian Stable中安装。
彼得·克劳斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.