Використання ST_Difference для видалення функцій, що перекриваються?


11

Я намагаюся використовувати ST_Difference для створення набору багатокутників (treatment.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;

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

Я додав картину результату

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


Після коментарів я спробував додати пункт WHERE. Я хочу, щоб посилки, які не перетиналися, а ділянки, що перетинаються, були видалені (шар test.single_geometry являє собою забруднення, яке я хочу видалити з моїх посилок). Я спробував перехрестя, але, звичайно, я дійсно хочу, щоб не перехрестя, тому я зараз намагаюся роз'єднати. Я також спробував додати оригінал до моєї таблиці, але документація для 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);

Виходячи з відповіді Дбастона, я зараз спробував:

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;

І я запускаю запити з вікна запитів PostgreSQL SQL і 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 в таблиці, а не різницю?
березня

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

Добре дякую за уточнення, але я все ще не впевнений, чому тоді оригінальний код не працює. Якщо 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.


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

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 Addons . Ви повинні об'єднати дві таблиці разом, мати унікальний ідентифікатор та індекс на стовпчику геометрії. Ось короткий приклад:

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 ().

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