Побудова діаграми Вороного в PostGIS


12

Я намагаюся побудувати діаграми Вороного з сітки точок з використанням модифікованого коду від тут . Це запит SQL після моїх модифікацій:

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

Нижче - результат мого запиту. введіть тут опис зображення

Як ви бачите, я отримую "майже" правильну діаграму voronoi, за винятком виділених точок, у яких немає унікального багатокутника. Нижче наведено, що алгоритм QGIS виробляє і що я хотів би отримати за запитом. Будь-які судження, де проблема з моїм кодом?

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


Можливо, ви також можете порівняти результат функції SpatiaLite " VoronojDiagram " gaia-gis.it/gaia-sins/spatialite-sql-latest.html та ознайомитись із вихідним кодом у gaia-gis.it/fossil/libspatialite/ покажчик .
user30184

Приємне запитання, я розглядав те саме питання, на яке ви посилаєтесь, з метою прискорити його, але не вистачає часу. Мені не було відомо про це питання із зовнішніх питань.
Джон Пауелл

5
Для чого варто, щоб ST_Voronoi прийшов у PostGIS 2.3, Дан Бастон незабаром перевірить код для цього - trac.osgeo.org/postgis/ticket/2259 виглядає дуже багато, що потрібно зробити. тестування людей
LR1234567

Чи можете ви розмістити набір точок, які ви використовуєте? Я був би проти зробити трохи тестування на цьому
MickyT

@MickyT Ось посилання на мої дані. Дані SRID - це 2180.
DamnBack

Відповіді:


6

Хоча це виправляє негайну проблему із запитом щодо цих даних, я не задоволений ним як рішенням для загального використання, і я перегляну цю та попередню відповідь, коли зможу.

Проблема полягала в тому, що оригінальний запит використовував опуклий корпус на краях вороного для визначення зовнішнього краю для багатокутника Вороного. Це означало, що деякі з країв voronoi не доходили до зовнішності, коли вони мали бути. Я дивився на використання функцій увігнутого корпусу, але він не дуже працював так, як я хотів.
Як швидке виправлення, я змінив опуклий корпус, який має бути побудований навколо закритого набору країв voronoi плюс буфер навколо вихідних країв. Краї вороного, які не закриваються, витягнуті на велику відстань, щоб переконатися, що вони перетинаються зовнішністю і використовуються для побудови багатокутників. Ви можете пограти з параметрами буфера.

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;

Дякуємо за пояснення та швидке вирішення проблеми! Він працює з моїми даними (трохи повільніше через ST_Union(ST_Buffer(geom))), але я продовжую тестування з іншими наборами точок. Тим часом я буду чекати, як ви сказали, більш загального рішення. :)
DamnBack

чи є у вас зображення, яке ви можете розмістити на остаточному виході?
Джеріл Кук

10

Після пропозиції @ LR1234567 випробувати новий функціонал ST_Voronoi , розроблений @dbaston, оригінальний дивовижний відповідь @MickyT (як зазначено в питанні ОП) та використання оригінальних даних тепер можна спростити до:

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

що призводить до цього результату, ідентичного питання щодо ОП.

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

Однак це страждає від того ж питання, тепер зафіксованого у відповіді Міккі, що точки на увігнутому корпусі не отримують огороджувальний багатокутник (як можна було б очікувати від алгоритму). Я вирішив цю проблему за допомогою наступної серії кроків.

  1. Обчисліть увігнутий корпус вхідних точок - точки на увігнутому корпусі - це ті, що мають необмежений багатокутник у вихідній діаграмі Вороного.
  2. Знайдіть оригінальні точки на увігнутому корпусі (жовті точки на схемі 2 нижче).
  3. Буфер увігнутий корпус (відстань буфера довільне і, можливо, його можна знайти більш оптимально з вхідних даних?).
  4. Знайдіть найближчі точки на буфері увігнутих корпусів, найближчих до точок на кроці 2. Вони показані зеленим кольором на схемі нижче.
  5. Додайте ці точки до оригінального набору даних
  6. Обчисліть діаграму Вороного цього комбінованого набору даних. Як видно на 3-й діаграмі, точки на корпусі тепер мають огороджуючі багатокутники.

Діаграма 2, що показує точки на увігнутому корпусі (жовтий) та найближчі точки до буфера на корпусі (зелений). Діаграма 2.

Очевидно, що запит можна спростити / стиснути, але я залишив саме цю форму у вигляді серії CTE, оскільки легше слідувати крокам послідовно таким чином. Цей запит працює на оригінальному наборі даних у мілісекундах (середнє значення 11 мс на сервері розробників), тоді як відповідь MickyT за допомогою ST_Delauney працює на 4800 мс на одному сервері. DBaston стверджує, що інший порядок збільшення швидкості можна отримати, будуючи будівництво проти поточної магістралі GEOS, 3,6dev, завдяки вдосконаленням процедур триангуляції.

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;

Діаграма 3, що показує всі точки, тепер укладені в багатокутник діаграма 3

Примітка: В даний час ST_Voronoi передбачає створення Postgis з джерела (версія 2.3 або магістраль) та посилання на GEOS 3.5 або вище.

Редагувати: Я щойно переглянув Postgis 2.3, як він встановлений на веб-службах Amazon, і, здається, назва функції тепер є ST_VoronoiPolygons.

Без сумніву, цей запит / алгоритм можна було б покращити. Пропозиції вітаються.


@dbaston Цікаво, чи є у вас коментарі щодо цього підходу?
Джон Пауелл

1
добре, всі крапки ж отримати охоплює полігон, це просто , що це непропорційно велика. Якщо і як їх слід зменшити, це досить суб'єктивно, і не знаючи, що саме потрібно для зовнішніх багатокутників, важко зрозуміти, що таке "найкращий" спосіб. Ваш здається мені прекрасним методом. Я використав менш складний метод, подібний за духом до вашого, опускаючи зайві точки вздовж буферизованої опуклої кордону корпусу на фіксованому відстані, визначеному середньою щільністю точки.
дбастон

@dbaston Дякую, просто переконавшись, що я не пропустив чогось очевидного. Алгоритм зменшення зовнішніх багатокутників до чогось більшого, що відповідає розміру внутрішніх (у моєму випадку поштових індексів) - це те, про що я мушу ще трохи подумати.
Джон Пауелл

@John Barça Дякую за ще одне чудове рішення. Швидкість розрахунків більш ніж задовольняє такий підхід. На жаль, я хотів би використовувати цей алгоритм у моєму плагіні QGIS, і він повинен працювати з PostGIS 2.1+ і більше. Але, безумовно, я використовуватиму це рішення після офіційного релізу PostGIS 2.3. Все одно дякую за такі вичерпні відповіді. :)
DamnBack

@DamnBack. Ласкаво просимо. Мені це було потрібно для роботи, і ваше запитання насправді мені дуже допомогло, оскільки я не мав уявлення про те, як ST_Voronoi вийде, а старіші рішення набагато повільніше (як ви помітили). Це було весело, з'ясовуючи це теж :-)
Джон Пауелл

3

Якщо у вас є доступ до PostGIS 2.3, спробуйте нову функцію ST_Voronoi, нещодавно здійснену:

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

Існують попередньо складені версії для Windows - http://postgis.net/windows_downloads/


Дякую за інформацію про те, що є вбудована функція ST_Voronoi - я перевірю це. На жаль, мені потрібне рішення, яке працює на версіях PostGIS 2.1 і новіших версій, тому запит @MickyT є найближчим до моїх потреб на даний момент.
DamnBack

@ LR1234567. Для цього потрібна якась конкретна версія GEOS. Завтра встигну випробувати 2.3 і ST_Voronoi.
Джон Пауелл

Потрібна GEOS 3.5
LR1234567
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.