Bir Voronoi diyagramı oluşturmak için ST_DelaunayTriangles nasıl kullanılır?


13

(düzenleme 2019) ST_VoronoiPolygons mevcut PostGIS v2.3 beri !


PostGIS ile 2.1 + kullanabiliriz ST_DelaunayTriangles () meydana getirmek Delaunay üçgenleştirmesinin a,, çift grafik barındırmayan Voronoi diyagramı teorik olarak, ve, bunlar tam ve geri dönüşüm vardır.

Bu PostGIS2 Delaunay-Voronoi dönüşümü için optimize edilmiş bir algoritmaya sahip güvenli bir SQL standardı komut dosyası var mı?


Diğer referanslar: 1 , 2


gist.github.com/djq/4714788 sonra olan türden bir şey?
MickyT

Sanırım ST_DelaunayTriangles () kullanarak tamamen SQL uygulaması istiyor
raphael

ST_DelaunayTrianglesLinux Debian Stable'a kurmak için bu cevaba bakınız .
Peter Krauss

! PostGIS 2.3'ten beri ST_VoronoiPolygons
Peter Krauss

Yanıtlar:


23

Aşağıdaki sorgu, Delaunay Üçgenleri'nden başlayarak makul bir voronoi çokgenleri kümesi gibi görünüyor.

Ben büyük bir Postgres kullanıcısı değilim, bu yüzden muhtemelen biraz geliştirilebilir.

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;

Bu, sorguya dahil edilen örnek noktalar için aşağıdaki çokgen kümesini üretir resim açıklamasını buraya girin

Sorgu Açıklaması

Aşama 1

Giriş geometrilerinden Delaunay Üçgenleri oluşturun

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

Adım 2

Üçgen düğümleri ayrıştırın ve kenarlar yapılabilir. Kenarları almanın daha iyi bir yolu olması gerektiğini düşünüyorum, ama bir tane bulamadım.

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

resim açıklamasını buraya girin

Aşama 3

Her üçgen için sınırlandırılmış daireler oluşturun ve sentroidi bulun

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

resim açıklamasını buraya girin

EdgesCTE ait olduğu üçgenin her bir kenarının ve kimliği (yol) verir.

4. Adım

'Dış birleşimler' Kenarlar 'tablosunu kendisine farklı üçgenler (iç kenarlar) için eşit kenarların olduğu yerde birleştirin.

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)

Ortak bir kenar olduğunda, ilgili centroidler arasında bir çizgi çizin

resim açıklamasını buraya girin

Kenarın birleştirilmediği (dış), merkezden kenarın ortasından bir çizgi çizin. Bunu sadece dairenin sentroidi üçgen kümesinin içindeyse yapın.

resim açıklamasını buraya girin

Adım 5

Çizilmiş çizgiler için dışbükey gövdeyi bir çizgi olarak alın. Birleştirin ve tüm hatları birleştirin. Çokgenleştirilebilen bir topolojik setimiz olacak şekilde çizgi setini düğümleyin.

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

resim açıklamasını buraya girin


İyi ipucu, belki bir çözüm (!). Testten gerek, ama şimdi ... analiz edemez: Kullanmak ST_ConvexHullve ST_Centroiddoğrudan algoritmasında olarak yerine "dik bisectors" Benim önerdiği REF1 / Kenneth Slöa ... Neden direkt çözüm?
Peter Krauss

Hemen hemen tüm matematik olmadan dış kenarlar için dik açıortaylar yapıyorum :)
Cevaba attığım

İyi çizimler ve açıklama, çok didaktik!   İhtiyacım olan her şeyi (!) Gönderdiniz, ancak bu gün test etmek için Postgis2.1'e sahip değilim ... Burada test ederek cevaplayabileceği bazı soruları (yorum olarak) kontrol edebilir miyim?   1) ST_Polygonize "olası çokgenleri içeren bir GeometryCollection oluşturur", hepsi Voronoi hücreleri, değil mi?   2) performans hakkında, centroid tabanlı çözümünüzün "dikey diktatör hesaplamasının tüm matematiği" ile benzer CPU zamanı olduğunu düşünüyor musunuz?
Peter Krauss

@PeterKrauss 1) ST_polygonize hat çalışmasından voronoi hücrelerini oluşturur. Onunla ilgili hile, tüm hat çalışmasının düğümlerde bölünmesini sağlamaktır. 2) İkiye ayırmayı hesaplamak ve hatta ST_Centroid kullanmak arasında çok fazla fark olacağını düşünmüyorum. Ancak test edilmesi gerekir.
MickyT

ST_DelaunayTrianglesLinux Debian Stable'a kurmak için bu cevaba bakınız .
Peter Krauss
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.