Xây dựng sơ đồ Voronoi trong PostGIS


12

Tôi đang cố gắng xây dựng sơ đồ voronoi từ lưới các điểm bằng cách sử dụng mã sửa đổi từ đây . Đây là truy vấn SQL sau khi sửa đổi của tôi:

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

Dưới đây - kết quả của truy vấn của tôi. nhập mô tả hình ảnh ở đây

Như bạn có thể thấy tôi có sơ đồ voronoi "gần như" chính xác, ngoại trừ các điểm được tô sáng không có đa giác duy nhất. Dưới đây là những gì thuật toán QGIS tạo ra và những gì tôi muốn nhận được từ truy vấn. Bất kỳ đường nào là vấn đề với mã của tôi?

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


Có lẽ bạn cũng có thể so sánh kết quả của hàm SpatiaLite " VoronojDiagram " gaia-gis.it/gaia-sins/spatialite-sql-latest.html và xem mã nguồn trong gaia-gis.it/fossil/libspatite chỉ số .
dùng49584

Câu hỏi hay, tôi đã xem xét cùng một câu hỏi mà bạn tham khảo, với mục đích tăng tốc nó, nhưng tiếp tục hết thời gian. Tôi đã không nhận thức được vấn đề này với các điểm bên ngoài.
John Powell

5
Để biết giá trị của chúng tôi, ST_Voronoi sẽ xuất hiện trong PostGIS 2.3, Dan Baston sẽ sớm kiểm tra mã cho nó - trac.osgeo.org/postgis/ticket/2259 có vẻ như được thực hiện rất nhiều chỉ cần được kéo vào. thử nghiệm folks
LR1234567

Bạn có thể đăng lên tập hợp các điểm bạn đang sử dụng không? Tôi sẽ tự mình thực hiện một chút thử nghiệm về điều này
MickyT

@MickyT Đây là một liên kết đến dữ liệu của tôi. Dữ liệu SRID là 2180.
DamnBack

Câu trả lời:


6

Mặc dù điều này khắc phục sự cố ngay lập tức với truy vấn cho dữ liệu được đề cập, tôi không hài lòng với nó như là một giải pháp cho việc sử dụng chung và tôi sẽ xem lại câu hỏi này và câu trả lời trước khi tôi có thể.

Vấn đề là truy vấn ban đầu đã sử dụng vỏ lồi trên các cạnh voronoi để xác định cạnh bên ngoài cho đa giác voronoi. Điều này có nghĩa là một số cạnh voronoi không chạm tới bên ngoài khi chúng đáng lẽ phải có. Tôi đã xem xét việc sử dụng chức năng thân tàu lõm, nhưng nó không thực sự hoạt động như tôi muốn.
Để khắc phục nhanh, tôi đã thay đổi vỏ lồi được xây dựng xung quanh bộ cạnh voronoi kín cộng với bộ đệm xung quanh các cạnh ban đầu. Các cạnh voronoi không đóng được mở rộng ra một khoảng cách lớn để thử và đảm bảo rằng chúng giao nhau với bên ngoài và được sử dụng để xây dựng các đa giác. Bạn có thể muốn chơi xung quanh với các tham số bộ đệm.

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;

Cảm ơn đã giải thích và khắc phục nhanh chóng cho vấn đề! Nó hoạt động với dữ liệu của tôi (chậm hơn một chút do ST_Union(ST_Buffer(geom))), nhưng tôi sẽ tiếp tục thử nghiệm với các bộ điểm khác. Trong khi đó tôi sẽ chờ đợi như bạn đã nói - giải pháp tổng quát hơn. :)
DamnBack

Bạn có một hình ảnh bạn có thể đăng trên đầu ra cuối cùng của bạn?
Jeryl Cook

10

Sau một gợi ý bởi @ LR1234567 thử mới ST_Voronoi chức năng đã được phát triển bởi @dbaston, @MickyT 's câu trả lời tuyệt vời ban đầu (như đã nêu trong câu hỏi OP) và sử dụng các dữ liệu ban đầu bây giờ có thể được đơn giản hóa để:

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

dẫn đến kết quả đầu ra này, giống hệt với câu hỏi của OP.

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

Tuy nhiên, điều này gặp phải vấn đề tương tự, hiện đã được khắc phục trong câu trả lời của MickyT, rằng các điểm trên thân tàu lõm không có được một đa giác kèm theo (như mong đợi từ thuật toán). Tôi đã khắc phục vấn đề này bằng một truy vấn với một loạt các bước sau.

  1. Tính toán vỏ lõm của các điểm đầu vào - các điểm trên thân lõm là những điểm có đa giác không giới hạn trong sơ đồ Voronoi đầu ra.
  2. Tìm các điểm ban đầu trên thân tàu lõm (các điểm màu vàng trong sơ đồ 2 bên dưới).
  3. Bộ đệm vỏ lõm (khoảng cách bộ đệm là tùy ý và có lẽ có thể được tìm thấy tối ưu hơn từ dữ liệu đầu vào?).
  4. Tìm các điểm gần nhất trên bộ đệm của thân lõm gần nhất với các điểm trong bước 2. Chúng được hiển thị dưới dạng màu xanh lục trong sơ đồ bên dưới.
  5. Thêm các điểm này vào tập dữ liệu gốc
  6. Tính toán sơ đồ Voronoi của tập dữ liệu kết hợp này. Như có thể thấy trong sơ đồ thứ 3, các điểm trên thân tàu hiện có các đa giác kèm theo.

Sơ đồ 2 cho thấy các điểm trên thân tàu lõm (màu vàng) và các điểm gần nhất để đệm trên thân tàu (màu xanh lá cây). Sơ đồ 2.

Truy vấn rõ ràng có thể được đơn giản hóa / nén, nhưng tôi đã để lại nó dưới dạng một loạt các CTE, vì nó dễ dàng hơn để làm theo các bước theo cách đó. Truy vấn này chạy trên bộ dữ liệu gốc được tính bằng mili giây (trung bình 11ms trên máy chủ dev) trong khi câu trả lời của MickyT sử dụng ST_Delauney chạy trong 4800ms trên cùng một máy chủ. DBaston tuyên bố rằng một thứ tự tăng cường tốc độ khác có thể đạt được từ việc xây dựng dựa trên thân cây hiện tại của GEOS, 3.6dev, do những cải tiến trong thói quen tam giác.

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;

Sơ đồ 3 hiển thị tất cả các điểm được bao quanh trong một đa giác sơ đồ 3

Ghi chú: Hiện tại ST_Voronoi liên quan đến việc xây dựng Postgis từ nguồn (phiên bản 2.3 hoặc thân cây) và liên kết với GEOS 3.5 trở lên.

Biên tập: Tôi vừa xem Postgis 2.3 khi nó được cài đặt trên Amazon Web Services và có vẻ như tên hàm bây giờ là ST_VoronoiPolygons.

Không có nghi ngờ truy vấn / thuật toán này có thể được cải thiện. Gợi ý chào mừng.


@dbaston. Tự hỏi nếu bạn có bất kỳ ý kiến ​​về phương pháp này?
John Powell

1
tốt, tất cả các điểm đều có một đa giác kèm theo, chỉ là nó lớn không tương xứng. Nếu và làm thế nào những cái này nên được làm nhỏ hơn thì khá chủ quan, và không biết chính xác những gì mong muốn cho đa giác bên ngoài, thật khó để biết cách "tốt nhất" là gì. Có vẻ như một phương pháp tốt với tôi. Tôi đã sử dụng một phương pháp ít phức tạp tương tự như tinh thần của bạn, thả các điểm phụ dọc theo ranh giới vỏ lồi được đệm ở khoảng cách cố định được xác định bởi mật độ điểm avg.
dbaston

@dbaston. Cảm ơn, chỉ cần chắc chắn rằng tôi đã không thiếu thứ gì đó rõ ràng. Một thuật toán thu nhỏ các đa giác bên ngoài thành một cái gì đó phù hợp hơn với kích thước của các đa giác bên trong (trong khu vực mã bưu điện của tôi) là điều tôi sẽ phải suy nghĩ thêm.
John Powell

@ John Barça Cảm ơn vì một giải pháp tuyệt vời khác. Tốc độ tính toán là nhiều hơn thỏa mãn với phương pháp này. Thật không may, tôi muốn sử dụng thuật toán này bên trong plugin QGIS của mình và nó phải hoạt động với PostGIS 2.1+. Nhưng chắc chắn tôi sẽ sử dụng giải pháp này sau khi phát hành chính thức PostGIS 2.3. Dù sao cũng cảm ơn bạn cho câu trả lời toàn diện như vậy. :)
DamnBack

@DamnBack. Không có chi. Tôi cần điều này cho công việc và câu hỏi của bạn thực sự đã giúp tôi rất nhiều, vì tôi không biết gì về ST_Voronoi sắp ra mắt, và các giải pháp cũ chậm hơn nhiều (như bạn đã nhận thấy). Thật là vui khi tìm ra nó quá :-)
John Powell

3

Nếu bạn có quyền truy cập vào PostGIS 2.3, hãy thử chức năng ST_Voronoi mới, đã cam kết gần đây:

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

Có các bản dựng được biên dịch sẵn cho các cửa sổ - http://postgis.net/windows_doads/


Cảm ơn thông tin rằng có chức năng ST_Voronoi tích hợp sẵn - tôi sẽ kiểm tra xem. Thật không may, tôi cần giải pháp hoạt động trên các phiên bản PostGIS 2.1+, vì vậy truy vấn @MickyT là gần nhất với nhu cầu của tôi vào lúc này.
DamnBack

@ LR1234567. Điều này có yêu cầu bất kỳ phiên bản cụ thể của GEOS. Tôi có thời gian vào ngày mai để kiểm tra 2.3 và ST_Voronoi.
John Powell

Yêu cầu GEOS 3.5
LR1234567
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.