13

I have a multipolygon table with overlapping and non overlapping multipolygons. Where they overlap I need to keep the one with the highest (=1) priority and erase away from the lower priority (prio values >1) ones to make the table non-overlapping:

enter image description here

create table table1 (
prio    int,
geom    geometry(MULTIPOLYGON,3006));

insert into table1 (prio, geom) values
    (1, ST_PolygonFromText('MULTIPOLYGON(((666263 7266050,666869 7259810,671789 7265158,666263 7266050)))')),
    (2, ST_PolygonFromText('MULTIPOLYGON(((670720 7261236,668652 7264445,672966 7264124,670720 7261236)))')),
    (3, ST_PolygonFromText('MULTIPOLYGON(((669401 7267012,670328 7263233,674677 7266157,669401 7267012)))')),
    (4, ST_PolygonFromText('MULTIPOLYGON(((666263 7258563,666905 7254748,669615 7257315,666263 7258563)))'));

I can maybe solve this with something like this but its going to require alot of code since I have ~40 prios.

select id, prio, geom from table1 where prio=1
union
select p1.id, p1.prio, ST_Difference(p2.geom, p1.geom) geom from
(select * from table1 where prio=1) p1
join
(select * from table1 where prio=2) p2
on st_intersects(p1.geom, p2.geom) and p1.id <> p2.id

--union
--select id, prio, geom from table1 where prio=2
-- ...

Isnt there some easier way?

(This is the query I ended up with:

SELECT a.prio, ST_Multi(COALESCE(
         ST_Difference(a.geom, blade.geom),
         a.geom
       )) AS geom
FROM  table123 AS a
CROSS JOIN LATERAL (
  SELECT ST_Union(geom) AS geom
  FROM   table123 AS b
  WHERE a.geom && b.geom and a.prio > b.prio --The intersects operator greatly improves performance (from hours to a few seconds) with my data since most polygons does not overlap and doesnt need to be prioritized.
) AS blade
;

)

1 Answer 1

14

Fortunately, with the prio dependency, this is easier to solve than a purely recursive difference aggregation.

I'd prefer the LATERAL statement (here in full verbose mode), both for clarity of the statement and performance

SELECT ST_Multi(COALESCE(
         ST_Difference(a.geom, blade.geom),
         a.geom
       )) AS geom
FROM   table1 AS a
CROSS JOIN LATERAL (
  SELECT ST_Union(geom) AS geom
  FROM   table1 AS b
  WHERE  a.prio > b.prio
) AS blade
;

over a (correlated, but performance fenced) sub-query in the function

SELECT  ST_Multi(COALESCE(
          ST_Difference(a.geom, (SELECT ST_Union(geom) FROM table1 AS b WHERE a.prio > b.prio)),
          a.geom
        )) AS geom
FROM    table1 AS a
;

However, there isn't much to optimize in this particular case, so you should get the same query plan for both; run the tests on your actual tables and their sizes, as that may make a difference.

Use the latter structure to UDPATE:

UPDATE table1 AS a
    SET  geom = ST_Multi(COALESCE(
                  ST_Difference(a.geom, (SELECT ST_Union(geom) FROM table1 AS b WHERE a.prio > b.prio)),
                  a.geom
                ))
;

Note that this is overall a very costly operation; you could work in an index lookup (... WHERE a.geom && b.geom AND a.prio > b.prio ...) to save ST_Union costs for geometries that don't intersect higher prio geometries, but if you expect that to be rare, it won't make much difference.

1
  • 1
    @BERA For each polygon in the table, the sub-query first unions all (intersecting, if you used the bbox filter) polygons with any higher prio, to then erase overlapping parts of that union from the current polygon.
    – geozelot
    Commented Jan 24, 2023 at 14:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.