Используя ST_Difference, чтобы удалить перекрывающиеся функции?

11

Я пытаюсь использовать ST_Difference для создания набора полигонов (processing.trimmedparcelsnew), которые не содержат области, покрытой другим набором полигонов (test.single_geometry_1), используя PostGis 2.1 (и Postgres SQL 9.3). Вот мой запрос:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig;

Но полученные многоугольники не были обрезаны, вместо этого они, кажется, были разделены там, где они пересекаются с другим слоем. Я пытался просто запустить select, не помещая результат в таблицу и все остальное, что я могу придумать, но я не могу заставить эту функцию работать.

Я приложил картину результата

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


После комментариев я попытался добавить предложение WHERE. Я хочу, чтобы участки, у которых нет пересечений, и области пересечения других участков были удалены (слой test.single_geometry представляет загрязнение, которое я хочу удалить из своих участков). Я попытался пересечь, но, конечно, я на самом деле хочу, чтобы пересечения не пересекались, поэтому я сейчас пытаюсь пересечь. Я также попытался добавить orig к моей таблице, но документация для ST_Difference ( http://postgis.net/docs/ST_Difference.html ) говорит, что она возвращает именно ту геометрию, которая мне нужна (геометрия, которая представляет ту часть геометрии A, которая не пересекается с геометрией B), поэтому я не понимаю, зачем мне вместо этого исходный многоугольник в моей таблице. В любом случае, вот мой модифицированный код:

CREATE TABLE processing.trimmedparcelsnew AS
SELECT
    orig.id, ST_Difference(orig.geom, cont.geom) AS difference, orig.geom AS geom
FROM 
    test.single_geometry_1 cont,
    test.multi_geometry_1 orig
WHERE ST_Disjoint(orig.geom, cont.geom);

Исходя из ответа dbaston, я попробовал:

CREATE TABLE processing.parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Результатом этого является просто копия test.multi_geometry_1. Хотя сейчас расщепление больше не происходит.

Я пробовал более раннюю версию, но снова просто получить копию test.multi_geometry_1:

CREATE TABLE processing.parcels_trimmed_no_coalesce AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.single_geometry_1 b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.multi_geometry_1 a;

Я начинаю задаваться вопросом, есть ли что-то еще, что я делаю неправильно? Исходное заявление:

DROP TABLE IF EXISTS processing.parcels_trimmed_no_coalesce;

И я выполняю запросы из окна SQL-запросов PostgreSQL и Openjump.

Утверждение, которое я использую, чтобы увидеть таблицу:

SELECT * FROM processing.parcels_trimmed_no_coalesce;

В целях упрощения я теперь сократил этот запрос до просто:

SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM test.geometriestocutagainst b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM test.geometriestocut a;

Это по-прежнему приводит только к оригинальным полигонам (test.geometriestocut), когда желаемый результат - это оригинал, обрезанный по test.geometriestocutagainst.

рынок
источник
Вы не указали WHEREпредложение, поэтому вы можете иметь полиномиальное расширение в результирующей таблице. Сколько строк в trimmedparcelsnew?
Винс
Если вы хотите получить разницу только там, где они пересекаются, попробуйте добавить WHERE ST_Intersects (orig.geom, cont.geom). В противном случае разница двух многоугольников, которые не пересекаются, является исходным многоугольником.
Джон Пауэлл
В обрезанной посылке 24 строки, новые, я хочу разницу, даже если они не пересекаются, так что я могу исправить, что мне нужно использовать orig.geom в таблице, а не разницу?
Март
Непересекающийся тест должен производить полиномиальное расширение - каждый элемент в продолжение должен появляться один раз для каждого элемента в orig , который не перекрывается, и различие никогда не изменит геометрию ввода
Винс,
Хорошо, спасибо за разъяснения, но я все еще не уверен, почему оригинальный код не работает. Если ST_Difference (orig.geom, cont.geom) возвращает геометрии в a, которые не пересекаются с b, то почему в таблице содержатся разделенные геометрии, а не геометрии в a, которые не пересекаются с b.
Март

Ответы:

14

Самостоятельное соединение позволяет вам управлять отношениями между парами двух функций. Но я не думаю, что вас интересуют пары: для каждой функции вы хотите оперировать отношениями между этой функцией и всеми другими функциями в вашем наборе данных. Вы можете сделать это с помощью выражения подзапроса:

CREATE TABLE parcels_trimmed AS
SELECT id, ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                FROM parcels b
                                WHERE ST_Intersects(a.geom, b.geom)
                                  AND a.id != b.id))
FROM parcels a;

Вы можете увидеть что-то странное в результатах. Посылки, которые не имеют перекрытий, удаляются полностью! Это потому, что ST_Unionагрегат на пустом наборе записей будет NULLи ST_Difference(geom, NULL)есть NULL. Чтобы сгладить это, вам нужно заключить ваш ST_Differenceзвонок в COALESCE:

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Union(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         AND a.id != b.id)), a.geom)
FROM parcels a;

Это означает, что если результатом ST_Differenceявляется NULL, объединенное выражение будет вычислять исходную геометрию.

Приведенный выше запрос полностью удалит перекрывающиеся области из вашего домена. Если вы вместо этого хотите выбрать победителя, вы можете использовать a.id < b.idдругой критерий вместо a.id != b.id.

dbaston
источник
Спасибо за ответ, к сожалению, у меня проблемы с тем, чтобы это сработало для меня, вместо этого просто получился оригинальный полигон (а). Я отредактирую свой вопрос с дополнительной информацией. Еще раз спасибо.
Март
2

У меня была такая же проблема, как и у вас. Я не знаю, нашли ли вы решение вашей проблемы, но я изменил принятый ответ выше и получил то, что хотел.

CREATE TABLE parcels_trimmed AS
SELECT id, COALESCE(ST_Difference(geom, (SELECT ST_Collect(b.geom) 
                                         FROM parcels b
                                         WHERE ST_Intersects(a.geom, b.geom)
                                         )), a.geom)
FROM parcels a;
Даг
источник
1

Я использую ST_DifferenceAgg () из дополнений PostGIS . Вы должны объединить две таблицы вместе, иметь уникальный идентификатор и индекс в столбце геометрии. Вот краткий пример:

WITH overlappingtable AS (
  SELECT 1 id, ST_GeomFromText('POLYGON((0 1, 3 2, 3 0, 0 1), (1.5 1.333, 2 1.333, 2 0.666, 1.5 0.666, 1.5 1.333))') geom
  UNION ALL
  SELECT 2 id, ST_GeomFromText('POLYGON((1 1, 3.8 2, 4 0, 1 1))')
  UNION ALL
  SELECT 3 id, ST_GeomFromText('POLYGON((2 1, 4.6 2, 5 0, 2 1))')
  UNION ALL
  SELECT 4 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
  UNION ALL
  SELECT 5 id, ST_GeomFromText('POLYGON((3 1, 5.4 2, 6 0, 3 1))')
)
SELECT a.id, ST_DifferenceAgg(a.geom, b.geom) geom
FROM overlappingtable a,
     overlappingtable b
WHERE a.id = b.id OR -- Make sure to pass at least once the polygon with itself
      ((ST_Contains(a.geom, b.geom) OR -- Select all the containing, contained and overlapping polygons
        ST_Contains(b.geom, a.geom) OR
        ST_Overlaps(a.geom, b.geom)) AND
       (ST_Area(a.geom) < ST_Area(b.geom) OR -- Make sure bigger polygons are removed from smaller ones
        (ST_Area(a.geom) = ST_Area(b.geom) AND -- If areas are equal, arbitrarily remove one from the other but in a determined order so it's not done twice.
         a.id < b.id)))
GROUP BY a.id
HAVING ST_Area(ST_DifferenceAgg(a.geom, b.geom)) > 0 AND NOT ST_IsEmpty(ST_DifferenceAgg(a.geom, b.geom));

Это объединит перекрывающиеся части с самым большим перекрывающимся многоугольником. Если вы хотите оставить перекрывающуюся часть отделенной, посмотрите на пример ST_splitAgg ().

Пьер Расин
источник