Як використовувати ST_DelaunayTriangles для побудови діаграми Вороного?


13

(редагувати 2019 р.) ST_VoronoiPolygons доступні з PostGIS v2.3 !


За допомогою PostGIS 2.1+ і ми можемо використовувати ST_DelaunayTriangles () для створення триангуляції Делоне , тобто подвійний графік його діаграми Вороного , і, теоретично, вони мають точне та оборотне перетворення.

Чи існує який-небудь безпечний сценарій стандартного SQL з оптимізованим алгоритмом для цієї конверсії PostGIS2 Delaunay-to-Voronoi ?


Інші відповіді: 1 , 2


Чи є gist.github.com/djq/4714788 те, що ви шукаєте?
MickyT

Я думаю, що він хоче чисто реалізацію SQL, використовуючи ST_DelaunayTriangles ()
raphael

Дивіться цю відповідь, щоб встановити ST_DelaunayTrianglesв Linux Debian Stable .
Пітер Краусс

! ST_VoronoiPolygons доступні з PostGIS 2.3
Пітер Краус

Відповіді:


23

Наступний запит представляє собою розумний набір багатоугольників 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

Створіть з вхідних геометрій трикутники Делоне

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

введіть тут опис зображення

Крок 3

Побудуйте описані кола для кожного трикутника та знайдіть центроїд

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

введіть тут опис зображення

EdgesКТР виводить кожне ребро і ідентифікатор (шлях) трикутника він належить.

Крок 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))))))

введіть тут опис зображення


Хороший ключ, можливо, рішення (!). Мені потрібно протестувати, але зараз не можу ... Аналізуючи: ви використовуєте ST_ConvexHullі ST_Centroidнатомість "перпендикулярні бісектриси", як у прямому алгоритмі, запропонованому моїм ref1 / Кеннет Слоа ... Чому б не прямого рішення?
Пітер Краусс

Я майже роблю перпендикулярні бісектриси для зовнішніх країв, просто без усієї математики :) Я додам пояснення кроків, які я зробив до відповіді
MickyT

Гарні ілюстрації та пояснення, дуже дидактичні!   Ви розмістили все, що мені потрібно (!), Але в цей день у мене немає Postgis2.1 для тестування ... Чи можу я перевірити тут (як коментар) кілька питань, на які хтось може відповісти тестуванням?   1) ST_Polygonize "створює GeometryCollection, що містить можливі багатокутники", вони всі клітини Вороного, правда?   2) щодо продуктивності, на вашу думку, у вашому рішенні, що базується на центроїдах, є аналогічний час процесора, ніж "вся математика розрахунку перпендикулярних бісектрис"?
Пітер Краусс

@PeterKrauss 1) ST_polygonize створює клітини voronoi з лінії роботи. Хитрість у цьому полягає в тому, щоб переконатися, що вся робота лінії розбита на вузли. 2) Я не думаю, що між розрахунком бісекції та використанням ST_Centroid на лінії буде велика різниця. Але це потрібно було б протестувати.
MickyT

Дивіться цю відповідь, щоб встановити ST_DelaunayTrianglesв Linux Debian Stable .
Пітер Краусс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.