Создать растворенный буфер из мульти-геометрии (объединение по общему атрибуту и ​​пространственному пересечению)

10

Я должен создать растворенные буферы из многоточечных входных функций. В приведенном ниже примере входная таблица содержит 4 функции. Особенность #2состоит из двух точечных геометрий. После создания буфера я получаю 4 геометрии полигона:

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

Есть ли способ сгруппировать результат? Буферы точек #1и #2распущены, и должны представлять собой одну многоугольную особенность ( a).

Что я сделал до сих пор:

-- collect all buffers to a single multi-polygon feature
-- dissolve overlapping polygon geometries
CREATE TABLE public.pg_multibuffer AS SELECT
    row_number() over() AS gid,
    sub_qry.*
FROM (SELECT
    ST_Union(ST_Buffer(geom, 1000, 8))::geometry(MultiPolygon, /*SRID*/) AS geom
FROM
public.multipoints)
AS sub_qry;

РЕДАКТИРОВАТЬ:

-- create sample geometries

CREATE TABLE public.multipoints (
gid serial NOT NULL,
geom geometry(MultiPoint, 31256),
CONSTRAINT multipoints_pkey PRIMARY KEY (gid)
);

CREATE INDEX sidx_multipoints_geom
ON public.multipoints
USING gist
(geom);

INSERT INTO public.multipoints (gid, geom) VALUES
(1, ST_SetSRID(ST_GeomFromText('MultiPoint(12370 361685)'), 31256)),
(2, ST_SetSRID(ST_GeomFromText('MultiPoint(13520 360880, 19325 364350)'), 31256)),
(3, ST_SetSRID(ST_GeomFromText('MultiPoint(11785 367775)'), 31256)),
(4, ST_SetSRID(ST_GeomFromText('MultiPoint(19525 356305)'), 31256));
eclipsed_by_the_moon
источник
Вы слишком часто используете подзапросы. Это исключает вашу возможность GROUP BY для атрибута, на котором вы хотите кластеризоваться.
Винс
Таким образом, вам нужно выполнить пространственное объединение, а затем также объединение на основе номера объекта, поэтому вы ожидаете 3 мультиполигона из диаграммы выше. Я подозреваю, что это потребует двухэтапного процесса, но я просто хотел прояснить вопрос, прежде чем предлагать ответ.
Джон Пауэлл
Да, я хочу объединить буферные полигоны и собрать результат на основе количества входных объектов.
eclipsed_by_the_moon
Любое обновление по этому поводу? Я хотел бы знать, работает ли это для вас, насколько я вижу, я ответил на вопрос.
Джон Пауэлл
Извините за поздний ответ, я не был в сети в течение нескольких дней.
eclipsed_by_the_moon

Ответы:

7

Начиная с некоторых случайных точек, в стремлении имитировать те, что на изображении ОП, где первые два пространственно пересекаются, затем вторые и третьи имеют одинаковый атрибут id (2) с парой других точек, которые пространственно не пересекаются и не имеют тот же атрибут, следующий запрос производит 3 кластера:

WITH 
  temp (id, geom) AS 
     (VALUES (1, ST_Buffer(ST_Makepoint(0, 0), 2)),
        (2, ST_Buffer(ST_MakePoint(-0.7,0.5), 2)),
        (2, ST_Buffer(ST_MakePoint(10, 10), 2)), 
        (3, ST_Buffer(ST_MakePoint(-2, 12), 2)), 
        (4, ST_Buffer(ST_MakePoint(5, -6), 2))),
 unions(geoms) AS 
      (SELECT ST_Union(geom) FROM temp GROUP BY id),
 clusters(geoms) AS 
      (SELECT ST_CollectionExtract(unnest(ST_ClusterIntersecting(geoms)), 3) 
         FROM unions),
 multis(id, geoms) AS 
      (SELECT row_number() over() as id, geoms FROM clusters)
 SELECT ST_UNION(d.geom) FROM 
      (SELECT id, (ST_DUMP(geoms)).geom FROM multis) d GROUP BY id;

Здесь есть несколько шагов:

  1. использовать ST_Union, группировать по идентификатору, для первой группы по атрибуту
  2. использовать ST_ClusterIntersectingдля объединения тех же групп, которые пересекаются в пространстве
  3. добавить идентификатор для каждого из кластеров (таблица multis) - попытка сделать это непосредственно в ClusterIntersecting приводит к тому, что все геометрии получают идентификатор 1
  4. Объедините сброшенные геометрии из шага 2, сгруппировав их по идентификатору из шага 3 - это часть растворения . Это приводит к тому, что два перекрывающихся полигона в вашем кластере A объединяются, а не перекрываются, как это делается в конце шага 2.

Довольно долго, но это работает (и, я уверен, есть более короткий путь).

Использование инструмента WKT в QGIS (и обнаружение того, насколько я ужасен с инструментами редактирования) создает кластеры, подобные следующему, где вы можете видеть кластер, помеченный вами как a, - все вместе - то есть один цвет.

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

Если вы поместите ST_AsText вокруг финала, ST_UNION (d.geom), то вы сможете увидеть результаты напрямую.

РЕДАКТИРУЙТЕ, следуя дополнительной информации в комментариях: Поскольку вы начинаете с точек, вам нужно будет включить буфер в мое исходное решение, которое я поместил в временный CTE в начале, чтобы имитировать вашу диаграмму. Было бы проще добавить буфер в союзы CTE, чтобы вы могли выполнять все геометрические операции одновременно. Таким образом, используя в качестве примера буферное расстояние 1000, следующий код теперь возвращает 3 кластера, как и ожидалось.

WITH temp(id, geom) AS 
  (VALUES 
      (1, ST_SetSRID(ST_GeomFromText('MultiPoint(12370 361685)'), 31256)),   
      (2, ST_SetSRID(ST_GeomFromText('MultiPoint(13520 360880, 19325 364350)'), 31256)),                                                
      (3, ST_SetSRID(ST_GeomFromText('MultiPoint(11785 367775)'), 31256)),
      (4, ST_SetSRID(ST_GeomFromText('MultiPoint(19525 356305)'), 31256))
),                                              
unions(geoms) AS 
  (SELECT st_buffer(ST_Union(geom), 1000) FROM temp GROUP BY id),
clusters(geoms) AS 
  (SELECT ST_CollectionExtract(unnest(ST_ClusterIntersecting(geoms)), 3) 
     FROM unions),
multis(id, geoms) AS 
  (SELECT row_number() over() as id, geoms FROM clusters)
SELECT id, ST_UNION(d.geom) FROM 
  (SELECT id, (ST_DUMP(geoms)).geom FROM multis) d GROUP BY id;
Джон Пауэлл
источник
Извините, что так долго, чтобы вернуться к вам. У меня есть некоторые проблемы с визуализацией геометрии буфера в QGIS. Я пытался изменить ваш запрос с помощью ST_SetSRID, ST_Multiи ::geometry(Multipolygon, /*SRID*/), но на данный момент он не работает.
eclipsed_by_the_moon
Хорошо, если вы можете опубликовать свой код, а еще лучше некоторые данные, я мог бы помочь.
Джон Пауэлл
Я добавил немного SQL для создания точек выборки.
eclipsed_by_the_moon
Немного связан сегодня, я вернусь, как только смогу. Придется также работать с многоточечным запросом.
Джон Пауэлл
3

Один из способов сделать это - ST_Unionобъединить все буферы, ST_Dumpчтобы получить компоненты полученного многоугольника и соединиться с ST_Intersectsвходными точками, чтобы выяснить, сколько / какие точки составляют каждый кластер.

Это можно сделать без необходимости объединения, сгруппировав точки перед вызовом ST_Buffer. Чтобы две точки находились в одном и том же растворенном буфере, они должны быть достижимы прыжками между точками на расстоянии меньше, чем eps. Это просто проблема кластеризации с минимальной связью, которая может быть решена с помощью ST_ClusterDBSCAN:

SELECT
  cluster_id,
  ST_Union(ST_Buffer(geom, 1000)) AS geom,
  count(*)                        AS num_points,
  array_agg(point_id)             AS point_ids
FROM (
  SELECT
    point_id,
    ST_ClusterDBSCAN(geom, eps := 2000, minpoints := 1) OVER() AS cluster_id ,
    geom
  FROM points) sq
 GROUP BY cluster_id;

Обратите внимание, что это не даст точно такой же результат, как и метод с первым буфером, потому что буферы PostGIS не являются идеальными кругами, и две точки на расстоянии 1000 м друг от друга могут не соединяться двумя буферами по 500 м.

dbaston
источник
Кажется, у нас была похожая идея. Я не проверял ваши, но уверен, что это работает, и более чисто, чем мой.
Джон Пауэлл
Кажется, что PostGIS 2.2.1 не поддерживает ST_ClusterDBSCAN. Я установил PostGIS 2.3.2, но новые расширения postgis в pgAdmin по-прежнему версии 2.2.1.
eclipsed_by_the_moon
0

Согласно этому ответу вы хотите сделать ST_DUMP в вашем подзапросе.

Что-то вроде этого:

-- collect all buffers to a single multi-polygon feature
-- dissolve overlapping polygon geometries
CREATE TABLE public.pg_multibuffer AS SELECT
    row_number() over() AS gid,
    sub_qry.*
FROM (SELECT
    ST_Dump(ST_Union(ST_Buffer(geom, 1000, 8))::geometry(MultiPolygon, /*SRID*/)) AS geom
FROM
public.multipoints)
AS sub_qry;

Причина в том, что это ST_UNIONвозвращает растворенный мультиполигон всех объектов и ST_DUMPразбивает его на отдельные объекты полигона (которые были распущены).

Алекс Лейт
источник
1
Это на самом деле не будет работать, потому что любые атрибуты, которые понадобятся для кластеризации желаемого многочастного многоугольника, будут потеряны.
Винс
Я пробовал ST_Multi((ST_Dump(ST_Union(ST_Buffer(geom, 1000, 8)))).geom)::geometry(MultiPolygon, /*SRID*/) AS geom, но это создает 4 функции вместо 3.
eclipsed_by_the_moon
О, верно, вы хотите сгруппировать по номеру? Вам нужно будет GROUP_BYдо вас ST_UNION.
Алекс Лейт