PostGIS'de Voronoi diyagramının oluşturulması


12

Burada değiştirilmiş kodu kullanarak nokta kılavuzundan voronoi diyagramı inşa etmeye çalışıyorum . Bu benim değişiklikler sonra SQL sorgusu:

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

Aşağıda - sorgumun sonucu. resim açıklamasını buraya girin

Gördüğünüz gibi, "neredeyse" doğru voronoi diyagramı alıyorum, benzersiz çokgen içermeyen vurgulanan noktalar hariç. QGIS algoritması üretmek ve sorgudan ne elde etmek istiyorum aşağıdadır. Herhangi bir önerim benim kod ile sorun nerede?

resim açıklamasını buraya girin


Belki de SpatiaLite fonksiyonu "VoronojDiagram" gaia-gis.it/gaia-sins/spatialite-sql-latest.html sonucunu karşılaştırabilir ve kaynak koduna gaia-gis.it/fossil/libspatialite/ endeksi .
user30184

Güzel bir soru, hızlandırmak için başvurduğunuz aynı soruya bakıyorum, ancak zaman tükeniyor. Dış noktalar ile bu sorunun farkında değildim.
John Powell

5
STGV 2.3'e gelen ST_Voronoi'ye değdiğimiz şey için, Dan Baston yakında kodunu kontrol edecek - trac.osgeo.org/postgis/ticket/2259 neredeyse tamamlanmış görünüyor sadece çekilmesi gerekiyor. millet test
LR1234567

Kullandığınız puan kümesini kaydedebilir misiniz? Bu kendimi test biraz zihin olur
MickyT

@MickyT İşte verilerime bir bağlantı . Veri SRID
2180'dir.

Yanıtlar:


6

Bu, söz konusu verilerin sorgusu ile ilgili acil sorunu giderirken, genel kullanım için bir çözüm olarak bundan memnun değilim ve bunu yapabildiğimde ve önceki cevabı tekrar ziyaret edeceğim.

Sorun, orijinal sorgunun voronoi poligonunun dış kenarını belirlemek için voronoi kenarlarında dışbükey bir gövde kullanılmasıydı. Bu, bazı voronoi kenarlarının olması gerektiği zaman dış cepheye ulaşmadığı anlamına geliyordu. İçbükey gövde işlevselliğini kullanmaya baktım, ama istediğim gibi çalışmadı.
Hızlı bir düzeltme olarak, kapalı voronoi kenarları seti ve orijinal kenarların etrafında bir tampon etrafında inşa edilecek dışbükey gövdeyi değiştirdim. Kapatılmayan voronoi kenarları, dış tarafla kesiştiklerinden ve çokgenler oluşturmak için kullanıldıklarından emin olmak için geniş bir mesafe uzatılır. Arabellek parametreleriyle oynamak isteyebilirsiniz.

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;

Sorun için hızlı açıklama ve açıklama için teşekkürler! Verilerimle (nedeniyle biraz daha yavaş ST_Union(ST_Buffer(geom))) çalışır, ancak diğer puan kümeleri ile test etmeye devam edeceğim. Bu arada dediğin gibi bekleyeceğim - daha genel bir çözüm. :)
DamnBack

Son çıktınıza gönderebileceğiniz bir resminiz var mı?
Jeryl Cook

10

@Dbaston tarafından geliştirilen yeni ST_Voronoi işlevselliğini denemek için @ LR1234567 tarafından yapılan bir öneriden sonra, @MickyT'nin orijinal şaşırtıcı cevabı (OP'nin sorusunda belirtildiği gibi) ve orijinal verileri kullanmak artık basitleştirilebilir:

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

bu da OP'nin sorusuyla özdeş olan bu çıktıyla sonuçlanır.

resim açıklamasını buraya girin

Bununla birlikte, bu, MickyT'nin cevabında düzeltilen, içbükey gövdedeki noktaların (çoktan algoritmadan bekleneceği gibi) kapalı bir çokgen elde etmediği aynı sorundan muzdariptir. Ben aşağıdaki adımları dizi bir sorgu ile bu sorunu düzeltti.

  1. Giriş noktalarının içbükey gövdesini hesaplayın - içbükey gövdedeki noktalar, çıkış Voronoi diyagramında sınırsız çokgenlere sahip olanlardır.
  2. İçbükey gövdedeki orijinal noktaları bulun (aşağıdaki şema 2'de sarı noktalar).
  3. İçbükey gövdeyi tamponlayın (tampon mesafesi isteğe bağlıdır ve giriş verilerinden belki de en uygun şekilde bulunabilir mi?).
  4. Adım 2'deki noktalara en yakın içbükey gövdenin tamponundaki en yakın noktaları bulun. Bunlar aşağıdaki şemada yeşil olarak gösterilmiştir.
  5. Bu noktaları orijinal veri kümesine ekle
  6. Bu birleşik veri kümesinin Voronoi diyagramını hesaplayın. 3. diyagramda görülebileceği gibi, gövdedeki noktalar artık çokgenlere sahiptir.

Şekil 2, içbükey gövdede (sarı) noktaları ve gövdede (yeşil) arabelleğe en yakın noktaları gösterir. Şekil 2.

Sorgu açıkça basitleştirilebilir / sıkıştırılmış olabilir, ancak adımları bu şekilde sırayla takip etmek daha kolay olduğu için bu formu bir dizi CTE olarak bıraktım. Bu sorgu milisaniye (bir dev sunucusunda ortalama 11 ms) kümesinde orijinal veri üzerinde çalışır, oysa MickyT'nin ST_Delauney kullanarak yanıtı 4800 ms aynı sunucuda çalışır. DBaston, üçgenleme rutinlerindeki iyileştirmeler nedeniyle, GEOS, 3.6dev'in mevcut gövdesine karşı binadan başka bir büyüklük hızı geliştirme sırası kazanılabileceğini iddia ediyor.

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;

Şimdi çokgen içine alınmış tüm noktaları gösteren diyagram 3 diyagram 3

Not: Şu anda ST_Voronoi, Postgis'i kaynaktan (sürüm 2.3 veya gövde) oluşturmayı ve GEOS 3.5 veya üstüne bağlantı kurmayı içerir.

Düzenleme: Ben sadece Amazon Web Services yüklü olarak Postgis 2.3 baktım ve fonksiyon adı şimdi ST_VoronoiPolygons görünüyor.

Şüphesiz bu sorgu / algoritma geliştirilebilir. Öneriler hoş geldiniz.


@dbaston. Bu yaklaşım hakkında yorumlarınız mı var?
John Powell

1
kuyu, tüm noktaları yok o orantısız iri # sadece var, bir kapatan poligonu olsun. Bunların daha küçük yapılması ve nasıl yapılması gerektiği oldukça öznelse ve dış çokgenler için tam olarak ne istediğini bilmeden "en iyi" yolun ne olduğunu bilmek zor. Seninki benim için iyi bir yöntem gibi görünüyor. Ortama nokta yoğunluğu tarafından belirlenen sabit bir aralıkta tamponlu dışbükey gövde sınırı boyunca fazladan noktalar bırakarak, sizinkine benzer daha az karmaşık bir yöntem kullandım.
dbaston

@dbaston. Teşekkürler, sadece bariz bir şeyi kaçırmamaya dikkat ederek. Dış çokgenleri iç boyutların boyutuna (benim durumumda posta kodu alanlarına) göre daha fazla küçültmek için bir algoritma, biraz daha düşünmek zorunda kalacağım bir şey.
John Powell

@John Barça Başka bir harika çözüm için teşekkürler. Hesaplama hızı bu yaklaşımla tatmin edici değildir. Ne yazık ki bu algoritmayı QGIS eklentimde kullanmak istiyorum ve PostGIS 2.1+ ile birlikte çalışmak zorunda. Ama kesinlikle bu çözümü PostGIS 2.3 resmi sürümünden sonra kullanacağım. Neyse böyle kapsamlı cevaplar için teşekkür ederim. :)
DamnBack

@DamnBack. Hoşgeldiniz. Bu iş için gerekli ve ST_Voronoi çıkan hakkında hiçbir fikrim yoktu gibi soru gerçekten bana çok yardımcı oldu ve (daha önce fark ettiğiniz gibi) eski çözümler çok daha yavaş. Çok eğlenceli anladım :-)
John Powell

3

PostGIS 2.3'e erişiminiz varsa, yeni ST_Voronoi işlevine yakın zamanda taahhüt edilen bir deneme yapın:

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

Pencereler için önceden derlenmiş yapılar vardır - http://postgis.net/windows_downloads/


Orada yerleşik ST_Voronoi fonksiyonu upcomming bilgi için teşekkürler - Ben kontrol edecektir. Ne yazık ki PostGIS 2.1+ sürümlerinde çalışan bir çözüme ihtiyacım var, bu yüzden @MickyT sorgusu şu anda ihtiyaçlarıma en yakın.
DamnBack

@ LR1234567. Bunun için GEOS'un belirli bir sürümü gerekiyor mu? Yarın 2.3 ve ST_Voronoi'yi test etmek için zamanım var.
John Powell

GEOS 3.5
LR1234567
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.