Làm cách nào để sử dụng ST_DelaunayTriangles để xây dựng sơ đồ Voronoi?


12

(chỉnh sửa 2019) ST_VoronoiPolygons có sẵn kể từ PostGIS v2.3 !


Với PostGIS 2.1+, chúng ta có thể sử dụng ST_DelaunayTriangles () để tạo ra tam giác Delaunay , đó là một biểu đồ kép của sơ đồ Voronoi của nó và theo lý thuyết, chúng có một chuyển đổi chính xác và có thể đảo ngược.

Có bất kỳ tập lệnh tiêu chuẩn SQL an toàn nào với thuật toán tối ưu hóa tồn tại cho chuyển đổi PostGIS2 Delaunay-to-Voronoi này không?


Các giới thiệu khác: 1 , 2


Gist.github.com/djq/4714788 có phải là thứ bạn đang theo đuổi không?
MickyT

Tôi nghĩ rằng anh ấy muốn triển khai SQL hoàn toàn bằng ST_DelaunayTriangles ()
raphael

Xem câu trả lời này để cài đặt ST_DelaunayTrianglestrong Linux Debian Stable .
Peter Krauss

! ST_VoronoiPolygons có sẵn kể từ PostGIS 2.3
Peter Krauss

Câu trả lời:


22

Các truy vấn sau đây dường như thực hiện một tập hợp đa giác voronoi hợp lý bắt đầu từ Tam giác Delaunay.

Tôi không phải là người dùng Postgres lớn, vì vậy nó có thể được cải thiện khá nhiều.

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;

Điều này tạo ra tập hợp đa giác sau cho các điểm mẫu có trong truy vấn nhập mô tả hình ảnh ở đây

Giải thích truy vấn

Bước 1

Tạo Tam giác Delaunay từ hình học đầu vào

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

Bước 2

Phân tách các nút tam giác và làm cho các cạnh có thể được thực hiện. Tôi nghĩ rằng nên có một cách tốt hơn để có được các cạnh, nhưng tôi đã không tìm thấy một.

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

nhập mô tả hình ảnh ở đây

Bước 3

Xây dựng các vòng tròn được đăng ký cho mỗi tam giác và tìm tâm

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

nhập mô tả hình ảnh ở đây

Các EdgesCTE kết quả đầu ra mỗi cạnh và id (đường dẫn) của tam giác nó thuộc về.

Bước 4

'Tham gia bên ngoài' bảng 'Cạnh' với chính nó, nơi có các cạnh bằng nhau cho các hình tam giác khác nhau (các cạnh bên trong).

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)

Trường hợp có một cạnh chung vẽ một đường giữa các tâm tương ứng

nhập mô tả hình ảnh ở đây

Trường hợp cạnh không được nối (bên ngoài) vẽ một đường thẳng từ tâm qua tâm của cạnh. Chỉ làm điều này nếu tâm của hình tròn nằm trong tập hợp các hình tam giác.

nhập mô tả hình ảnh ở đây

Bước 5

Lấy thân tàu lồi cho các đường được vẽ dưới dạng đường. Liên minh và hợp nhất tất cả các dòng. Nút nút dòng để chúng ta có một bộ tô pô có thể được đa giác hóa.

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

nhập mô tả hình ảnh ở đây


Đầu mối tốt, có lẽ là một giải pháp (!). Tôi cần kiểm tra, nhưng bây giờ không thể ... Phân tích: bạn sử dụng ST_ConvexHullST_Centroidthay vào đó là "chia đôi vuông góc" như trong thuật toán trực tiếp được đề xuất bởi ref1 / Kenneth Sloa của tôi ... Tại sao không phải là giải pháp trực tiếp?
Peter Krauss

Tôi gần như đang thực hiện các phép chia vuông góc cho các cạnh bên ngoài, chỉ cần không có tất cả các phép toán :) Tôi sẽ thêm một lời giải thích về các bước tôi đã thực hiện để trả lời
MickyT

Minh họa tốt và giải thích, rất mô phạm!   Bạn đã đăng tất cả những gì tôi cần (!), Nhưng ngày nay tôi không có Postgis2.1 để kiểm tra ... Tôi có thể kiểm tra ở đây (dưới dạng nhận xét) một số câu hỏi mà bất kỳ ai cũng có thể trả lời bằng cách kiểm tra không?   1) ST_Polygonize "tạo GeometryCollection chứa các đa giác có thể", tất cả chúng đều là các ô Voronoi, đúng không?   2) về hiệu suất, bạn nghĩ giải pháp dựa trên centroid của bạn có thời gian CPU tương tự như "tất cả các phép tính của phép tính chia vuông góc"?
Peter Krauss

@PeterKrauss 1) ST_polygonize không tạo các ô voronoi từ dòng hoạt động. Thủ thuật với nó là đảm bảo tất cả các công việc dòng được phân chia tại các nút. 2) Tôi không nghĩ sẽ có nhiều sự khác biệt giữa việc tính toán chia đôi và sử dụng ST_Centroid trên dòng. Nhưng nó sẽ cần phải được thử nghiệm.
MickyT

Xem câu trả lời này để cài đặt ST_DelaunayTrianglestrong Linux Debian Stable .
Peter Krauss
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.