Рекурсивное пересечение PostGIS между полигонами

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

изображение (не в масштабе), чтобы объяснить это: Example

допустим есть A, B, C квадраты в таблице. Я хотел бы иметь A, B, C, A+B, A+C, B+C, A+B+C полигоны на выходе, и мне нужно знать, что A+B пересечение A и B и так на.

пока у меня есть запрос, который выполняет пересечения, но он не "отрезает" пересекаемую часть исходных полигонов. Например:

Polygon A should be      A - (A+B) - (A+C) - (A+B+C)
Polygon A+C should be    A+C - (A+B+C)

изображение результата, который я получаю сейчас для A и A+C полигонов:

Current WRONG result

вот тестовый скрипт, использующий квадраты в изображениях в качестве данных. Глядя на area столбец, ясно, что отсутствует рекурсивная ST_Difference, я просто не могу понять, как. Любой идея приветствуется.

-- Create a test table
CREATE TABLE test (
    name text PRIMARY KEY,
    geom geometry(POLYGON)
);

-- Insert test data
INSERT INTO test (name, geom) VALUES 
    ('A', ST_GeomFromText('POLYGON((1 2, 1 6, 5 6, 5 2, 1 2))')),
    ('B', ST_GeomFromText('POLYGON((0 0, 0 4, 4 4, 4 0, 0 0))')),
    ('C', ST_GeomFromText('POLYGON((2 0, 2 4, 6 4, 6 0, 2 0))'));


-- Query    
WITH RECURSIVE 
source (rownum, geom, ret) AS (
    SELECT row_number() OVER (ORDER BY name ASC), ST_Multi(geom), ARRAY[name] FROM test 
),
r (rownum, geom, ret, incroci) AS (
    SELECT rownum, geom, ret, 0 FROM source 
    UNION ALL
    SELECT s.rownum, ST_CollectionExtract(ST_Intersection(s.geom, r.geom), 3), (r.ret || s.ret), (r.incroci + 1) 
        FROM source AS s INNER JOIN r ON s.rownum > r.rownum AND ST_Intersects(s.geom, r.geom) AND ST_Area(ST_Intersection(s.geom, r.geom)) > 0.5
),
result (geom, ret) AS (
    SELECT ST_Union(geom) AS geom, ret FROM r GROUP BY ret
)
SELECT geom, ST_Area(geom) AS area, ret FROM result ORDER BY ret

функция окна не является строго необходимой в этом конкретном примере, конечно, но этот код является упрощенной версией моего реального случая, который делает еще несколько вещей на стороне.

я использую PostgreSQL 9.2 и PostGIS 2.0

1 ответов


ST_DIFFRENCE не должен быть рекурсивным, у вас уже есть все полигоны, поэтому из каждой геомы вы должны вычитать объединение других Геом, которые содержат этот ret, но не равны ему. Это работает, поэтому вы должны сделать это примерно так:

    WITH RECURSIVE 
source (rownum, geom, ret) AS (
    SELECT row_number() OVER (ORDER BY name ASC), ST_Multi(geom), ARRAY[name] FROM test 
),
r (rownum, geom, ret, incroci) AS (
    SELECT rownum, geom, ret, 0 FROM source 
    UNION ALL
    SELECT s.rownum, ST_CollectionExtract(ST_Intersection(s.geom, r.geom), 3), (r.ret || s.ret), (r.incroci + 1) 
        FROM source AS s INNER JOIN r ON s.rownum > r.rownum AND ST_Intersects(s.geom, r.geom) AND ST_Area(ST_Intersection(s.geom, r.geom)) > 0.5
),
result (geom, ret) AS (
    SELECT ST_Difference(ST_Union(r.geom),q.geom) AS geom, r.ret FROM r JOIN (SELECT r.ret,ST_UNION(COALESCE(r2.geom,ST_GeomFromText('POLYGON EMPTY'))) as geom FROM r LEFT JOIN r AS r2 ON r.ret<@r2.ret AND r.ret!=r2.ret GROUP BY r.ret) AS q on r.ret=q.ret GROUP BY r.ret,q.geom
)
SELECT geom, ST_Area(geom) AS area, ret FROM result ORDER BY ret