Об'єднайте будь-які суміжні багатокутники


22

Я хотів би зробити тести на суміжність на шарі посилки (полігони) та об'єднати їх, якщо вони відповідають певним критеріям (може бути розміром). На малюнку нижче я хотів би об'єднати багатокутники 1,2,3 та 4, але не 5.

У мене дві проблеми:

  1. ST_TOUCHESповертає ІСТИНА, якщо торкаються лише кути, а не відрізок лінії. Я думаю, що мені потрібно ST_RELATE, щоб перевірити, чи є сегменти спільного використання рядків.
  2. В ідеалі я хотів би об'єднати ВСІ суміжні багатокутники в один, але я не впевнений, як масштабувати межі двох - як у, об'єднати 1,2,3 та 4 (і, можливо, більше за фактичними даними) за один раунд.

Структура, яку я маю зараз, заснована на самостійному приєднанні ST_TOUCHES.

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

Дані про іграшки

CREATE TABLE testpoly AS 
SELECT 
1 AS id, ST_PolyFromText('POLYGON ((0 0, 10 0, 10 20, 00 20, 0 0 ))') AS geom UNION SELECT
2 AS id, ST_PolyFromText('POLYGON ((10 0, 20 0, 20 20, 10 20, 10 0 ))') AS geom UNION SELECT
3 AS id, ST_PolyFromText('POLYGON ((10 -20, 20 -20, 20 0, 10 0, 10 -20 ))') AS geom UNION SELECT
4 AS id, ST_PolyFromText('POLYGON ((20 -20, 30 -20, 30 0, 20 0, 20 -20 ))') AS geom  UNION SELECT 
5 AS id, ST_PolyFromText('POLYGON ((30 0, 40 0, 40 20, 30 20, 30 0 ))') AS geom ;

Вибір

SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND t1.geom && t2.geom 
) 
l2

Ось вихід:

+-----+---------+-------------------------------------------------------------------------------+
| gid | adj_gid | geo_combo                                                                     |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 2       | POLYGON((10 0,0 0,0 20,10 20,20 20,20 0,10 0))                                |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 3       | MULTIPOLYGON(((10 0,0 0,0 20,10 20,10 0)),((10 0,20 0,20 -20,10 -20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 1       | POLYGON((10 20,20 20,20 0,10 0,0 0,0 20,10 20))                               |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 3       | POLYGON((10 0,10 20,20 20,20 0,20 -20,10 -20,10 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 4       | MULTIPOLYGON(((20 0,10 0,10 20,20 20,20 0)),((20 0,30 0,30 -20,20 -20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 1       | MULTIPOLYGON(((10 0,20 0,20 -20,10 -20,10 0)),((10 0,0 0,0 20,10 20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 2       | POLYGON((20 0,20 -20,10 -20,10 0,10 20,20 20,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 4       | POLYGON((20 -20,10 -20,10 0,20 0,30 0,30 -20,20 -20))                         |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 2       | MULTIPOLYGON(((20 0,30 0,30 -20,20 -20,20 0)),((20 0,10 0,10 20,20 20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 3       | POLYGON((20 0,30 0,30 -20,20 -20,10 -20,10 0,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 5       | MULTIPOLYGON(((30 0,30 -20,20 -20,20 0,30 0)),((30 0,30 20,40 20,40 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 5   | 4       | MULTIPOLYGON(((30 0,30 20,40 20,40 0,30 0)),((30 0,30 -20,20 -20,20 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+

Зауважте, що багатокутник id = 3 ділить точку з id = 1 і, таким чином, повертається як позитивний результат. Якщо я зміню пункт WHERE, ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');я взагалі не отримаю записів.

  1. Отже, спочатку , як вказати ST_Relate, щоб переконатися, що розглядаються лише посилки, що поділяють сегмент рядка.

  2. І тоді, як би я об'єднав багатокутники 1,2,3,4 в один раунд, згортаючи результати вищезгаданого виклику, увесь час визнаючи, що суміжність 1 до 2 - це те саме, що і зворотне?

Оновлення

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

GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'

Хоча це не ідеально (я б скоріше використовував перевірки топології з ST_RELATEбільш загальним рішенням), це шлях вперед. Тоді залишається питання зняття та об'єднання цих об'єднань. Можливо, якби я міг генерувати послідовність лише дотиків полігонів, я міг би з'єднатись із цим.

Оновлення II

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

WHERE
              ST_Touches( t1.geom, t2.geom ) 
              AND t1.geom && t2.geom 

              -- 'overlap' relation
              AND ST_Relate(t1.geom, t2.geom)='FF2F11212') t2 

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


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

Якби був спосіб згрупувати суміжні полігони в масивах можна потім змінити ST_IntersectionArray[функції] [1] для роботи з ST_Union [1]: gis.stackexchange.com/a/60295/36886
Raphael

2
Що стосується групування спільних суміжних багатокутників, ви можете змінити алгоритм кластеризації знизу вгору, про який я писав тут ( gis.stackexchange.com/a/115715/36886 ), щоб перевірити на суміжність, а не простір, а потім використовувати ST_Union під час групування на результуючій cluster_ids
raphael

3
Також є ST_ClusterIntersectimg, який може робити все, що вам потрібно. Вам потрібні Postgis 2.2
Джон Пауелл

Відповіді:


3

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

Для вашого конкретного прикладу це спрацювало б:

WITH rast AS (
  SELECT 
  ST_UNION(ST_AsRaster(geom,10, 20, '2BUI')) r
  FROM testpoly 
)
,p AS (
    SELECT (ST_DumpAsPolygons(r)).geom FROM rast
)
SELECT t.id,p.* 
FROM p
LEFT JOIN testpoly  t ON ST_Equals(p.geom, t.geom)

Що трапляється так, що оскільки ваші багатокутники є ідеально вирівняними клітинами, вони добре перетворяться в растрові (розмір 10x20 клітин). Dumpaspolygons допомагає вам тут, об'єднуючи всі сусідні комірки в одну і, порівнюючи з оригінальними багатокутниками, ви навіть зможете повернути ідентифікатор для не злитих полі.

Пояснивши це, мені дуже цікаво, як би це масштабувало і наскільки великий ваш набір даних: D


Розумна ідея. Хоча це іграшковий приклад - мої фактичні дані - це шар посилки, який не буде чітко відображатись у растрах.
АКО

3

Ось приклад того, як це зробити в процедурному стилі з кількома проходами під кришкою.

CREATE TABLE joined_testpoly AS SELECT array[id] ids, geom FROM testpoly; 

Ви повинні мати можливість переносити більше стовпців і застосовувати додаткові критерії приєднання, змінюючи, як працює LIMIT 1вибраний нижче:

CREATE OR REPLACE FUNCTION reduce_joined_testpoly()
RETURNS void
AS $$
DECLARE
  joined_row joined_testpoly%ROWTYPE;
BEGIN
  LOOP
     SELECT array_cat(a.ids, b.ids), st_union(a.geom, b.geom)
         INTO joined_row 
     FROM joined_testpoly a INNER JOIN joined_testpoly b
           on a.ids != b.ids
              and ST_Touches(a.geom, b.geom) and a.geom && b.geom 
              and ST_Relate(a.geom, b.geom)='FF2F11212'
         LIMIT 1;
     IF NOT FOUND THEN
           EXIT;
     END IF;
     INSERT INTO joined_testpoly VALUES (joined_row.ids, joined_row.geom);
     DELETE FROM joined_testpoly
         WHERE joined_testpoly.ids <@ joined_row.ids 
           AND joined_testpoly.ids != joined_row.ids;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;

Виконати річ:

SELECT reduce_joined_testpoly();

Правильні союзи, без мультиполігонів:

SELECT ids, st_geometrytype(geom), st_area(geom), st_numgeometries(geom) 
FROM joined_testpoly;
    ids    | st_geometrytype | st_area | st_numgeometries 
-----------+-----------------+---------+------------------
 {5}       | ST_Polygon      |     200 |                1
 {1,2,3,4} | ST_Polygon      |     800 |                1

2

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

SELECT st_numgeometries(g), (SELECT st_union(x.geom) FROM st_dump(g) x GROUP BY g)
FROM (
    SELECT unnest(st_clusterintersecting(geom)) g, id < 100 as other_arbitrary_grouping 
    FROM testpoly
    GROUP BY other_arbitrary_grouping) c;

(не соромтесь виправити та опублікувати ще одну відповідь, якщо хтось може отримати геометрію id = 5 у власній групі)

Щоб повернути список ідентифікаторів тощо, вам доведеться st_containsзнову приєднатись до таблиці тестових описів, як це детально описано в такій відповіді: /programming//a/37486732/6691, але я не зміг зробити це для роботи для багатокутників чомусь.


2

Ось швидкий підхід до нього за допомогою оригінального запиту, який трохи відрегульований:

with gr as (SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND ST_Relate(t1.geom,t2.geom, '****1****')
      AND t1.geom && t2.geom 
) 
l2) select ST_AsText(st_union(gr.geo_combo)) from gr;

Довідкова інформація: https://postgis.net/docs/using_postgis_dbmanagement.html#DE-9IM

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