Як рекурсивно провести цикл через перетини батьківських полігонів, щоб отримати найменші (дочірні) багатокутники без перекриттів?


11

Я боровся з проблемою пару днів і зрозумів, що багато людей також застрягають, коли тема перехрестя в PostGIS (v2.5). Ось чому я вирішив задати більш детальне та загальне спільне запитання.

У мене є така таблиця:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(MultiPolygon, 4326),
    att_category character varying(15),
    att_value integer
);
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('MULTIPOLYGON (((0 6, 0 12, 8 9, 0 6)))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('MULTIPOLYGON (((5 0, 5 12, 9 12, 9 0, 5 0)))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('MULTIPOLYGON (((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4)))'::geometry,4326) , 'cat2', 5 );

Це виглядає приблизно так:

почати

Я хочу отримати всі дочірні багатокутники на основі перетину батьківських багатокутників. Для результату слід очікувати:

  • Дочірні багатокутники без перекриття між ними.
  • Стовпець, що містить суму значення їхніх батьківських багатокутників,
  • Стовпець, що містить кількість батьківських багатокутників однієї категорії
  • Стовпець, що містить кількість іншої категорії
  • Стовпець, що містить категорію дочірнього багатокутника, заснований на наступному правилі: -Якщо ВСІ батьківські багатокутники є з одного класу, дочірній багатокутник також має цей клас. Крім того, категорія дитячого багатокутника - третя категорія.

Так виглядає так:

вихід

Так, в кінці кінців, вихідна таблиця генерується (для даного прикладу) буде мати 7 рядків (всі 7, неперекривающіеся, дитячі багатокутників), що містять стовпці category, sum_value, ct_overlap_cat1,ct_overlap_cat2

Наступний код, який я почав, дає мені окремі перехрестя, порівнюючи одного з батьків з іншим.

SELECT
(ST_Dump(
    ST_SymDifference(a.geom, b.geom) 
)).geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom)
UNION ALL
SELECT
ST_Intersection(a.geom, b.geom) as geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom);

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

Відповіді:


8

Спробуйте це:

Завантажте PostGIS Addons за цим посиланням: https://github.com/pedrogit/postgisaddons

Встановіть, запустивши файл postgis_addons.sql, щоб отримати функцію ST_SplitAgg ().

Перевірте, запустивши файл postgis_addons_test.sql.

Ось ваш запит:

WITH  result_table AS (
    WITH  parts AS (
      SELECT a.att_value val,
             CASE WHEN a.att_category = 'cat1' THEN 1 ELSE 0 END cat1,
             CASE WHEN a.att_category = 'cat2' THEN 1 ELSE 0 END cat2,
             unnest(ST_SplitAgg(a.geom, b.geom, 0.00001)) geom
      FROM tbl_foo a,
           tbl_foo b
      WHERE ST_Equals(a.geom, b.geom) OR
            ST_Contains(a.geom, b.geom) OR
            ST_Contains(b.geom, a.geom) OR
            ST_Overlaps(a.geom, b.geom)
      GROUP BY a.id, a.att_category , ST_AsEWKB(a.geom), val
    )
    SELECT CASE WHEN sum(cat2) = 0 THEN 'cat1'
                WHEN sum(cat1) = 0 THEN 'cat2'
                ELSE 'cat3'
           END category, 
           sum(val*1.0) sum_value, 
           sum(cat1) ct_overlap_cat1, 
           sum(cat2) ct_overlap_cat2, 
           ST_Union(geom) geom
    FROM parts
    GROUP BY ST_Area(geom)
)
SELECT category, sum_value, ct_overlap_cat1, ct_overlap_cat2,
(ST_Dump(result_table.geom)).geom as geom
FROM result_table

Я раніше дивився на ваші аддони git repo. Inspirstionsl речі.
Джон Пауелл

Нічого чудового рішення. Ви зробили досить дивовижну роботу і для створення цих адонів. Перш ніж натиснути, щоб присвоїти цю відповідь, я лише один, щоб переконатися в одній справі, яка мене клопоче. Запускаючи наданий вами код, "багатокутник 5" (другої фігури питання), схоже, не розпізнає перекриття з іншим багатокутником ('ct_overlap_cat2 = 1'; 'ct_overlap_cat2 = 0'). Тому цей многокутник в кінцевому підсумку класифікується як "cat1" і "sum = 2", а не "cat3" і "sum = 7". Я зіткнувся з невеликими труднощами, щоб налагодити цю маленьку проблему. Не могли б ви мені допомогти?
Matt_Geo

1
Єдина проблема цього рішення полягає в тому, що заяви справи є жорсткими. В принципі, це, мабуть, має бути здатним обробляти довільну кількість категорій.
Джон Пауелл

@Matt_Geo Я отримую 6 багатокутників у отриманій таблиці. Трикутник трикутника ділимо на три. Один із сумою = 2, один із сумою = 7 та один із сумою = 8, як у вашому бажаному малюнку.
П’єр Расін

1
Що робити, якщо замінити ST_Centroid (geom) на ST_Area (geom)?
П’єр Расін

1

Я думаю, якщо ви використовуєте тип геометрії багатокутника замість MultiPolygon, все стане на свої місця:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(Polygon, 4326),
    att_category character varying(15),
    att_value integer
);

INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('POLYGON ((0 6, 0 12, 8 9, 0 6))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('POLYGON ((5 0, 5 12, 9 12, 9 0, 5 0))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('POLYGON ((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4))'::geometry,4326) , 'cat2', 5 );

Результат - 9 записів, які відповідають різним варіантам перетину в наведеному вами прикладі.

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