Объединить все смежные полигоны

22

Я хотел бы провести тесты смежности на слое участков (полигонов) и объединить их, если они соответствуют определенным критериям (может быть размером). Согласно рисунку ниже, я хотел бы объединить полигоны 1,2,3 и 4, но не 5.

У меня две проблемы:

  1. ST_TOUCHESвозвращает TRUE, если касаются только углы, а не отрезок. Я думаю, что мне нужно 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 теперь выглядит так:

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
Рафаэль
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
EoghanM
источник
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снова присоединиться к таблице testpoly, как подробно описано в следующем ответе: /programming//a/37486732/6691, но я не смог заставить это работать для полигонов по какой-то причине.

EoghanM
источник
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

cm1
источник