5

Is it possible to convert a MultiPolygon to a polygon when the result is a self-intersecting polygon?

I have a bunch of MultiPolygons that are essentially originally formed to be multipolygons because they would be self-intersecting as a one-part polygon. Im trying to convert these multipolygons to polygons.

I've tried first to use ST_Dump to break the MultiPolygon in parts and then use ST_Union to merge polygons but this always results in MultiPolygon. I also tried ST_Collect and ST_UnaryUnion. For example:

SELECT 
    objectid,
    ST_AsText(ST_Union(geom))
FROM (
    SELECT 
        objectid,
        (ST_Dump(shape)).geom
    FROM 
        schema.table
    WHERE 
        objectid = 1234
) AS subquery
GROUP BY 
    objectid;

results in multipolygon.

Is there a way to force MultiPolygons to polygons even if they are self-intersecting?

Here is the wkt of a MultiPolygon I want to convert:

MULTIPOLYGON(((508416.031308245 6751318.78006006,508419.678890453 6751315.13247785,508417.850887359 6751319.31077064,508416.031308245 6751318.78006006)),((508403.264770517 6751459.21197506,508403.264770517 6751428.20752629,508406.912352725 6751344.31313551,508417.850887359 6751319.31077064,508459.802294741 6751331.54659778,508403.264770517 6751459.21197506)))
6
  • If the parts would be self-intersecting in a Polygon, they're also self-intersecting in a MultiPolygon. Neither is a valid geometry. Please Edit the Question to contain Well-Known Text of your geometry (or a minimal simulation thereof).
    – Vince
    Commented Apr 18 at 14:25
  • st_union does what you describe (i.e. to dissolve the overlapping polygons). On a side note, you can apply st_union on the multipolygon directly with src(geom) as ( values ('MULTIPOLYGON(((0 0, 0 10, 10 10, 10 0, 0 0)),((5 5, 5 15, 15 15, 15 5, 5 5)))'::geometry)) SELECT st_asText(St_union(geom)) from src; --> POLYGON((0 10,5 10,5 15,15 15,15 5,10 5,10 0,0 0,0 10))
    – JGH
    Commented Apr 18 at 14:31
  • 2
    Can you provide an example of input and desired output?
    – dr_jts
    Commented Apr 18 at 16:57
  • 1
    you need to union first, then dump
    – jbalk
    Commented Apr 18 at 17:00
  • 2
    why do you want to do this?
    – dr_jts
    Commented Apr 18 at 17:01

3 Answers 3

4

PostGIS implements the OGC geometry specification, which prohibits self-intersecting polygons. Some other systems use a different topology, which uses self-touching rings to represent holes that touch the polygon shell, and holes that touch.

The ST_MakeValid function allows converting self-touching polygons into OGC-valid polygons. But there is no function that does the opposite.

2

As already established, there's no built-in method for what you're trying to achieve. What's available is made with the opposite goal in mind: going from invalid to valid, not the other way around.

What you can do is deconstruct the MultiPolygon and reconstruct the self-intersecting unary Polygon out of its sub-segments.
Demo at db<>fiddle:

objectid comment st_astext st_isvalidreason
7777 bowtie POLYGON((0 0,2 2,2 0,0 2,0 0)) Self-intersection[1 1]
8888 bowtie processed by st_makevalid and split into two triangles MULTIPOLYGON(((0 2,1 1,0 0,0 2)),((2 0,1 1,2 2,2 0))) Valid Geometry
1234 OP's example polygon MULTIPOLYGON(((508416.031308245 6751318.78006006,508419.678890453 6751315.13247785,508417.850887359 6751319.31077064,508416.031308245 6751318.78006006)),((508403.264770517 6751459.21197506,508403.264770517 6751428.20752629,508406.912352725 6751344.31313551,508417.850887359 6751319.31077064,508459.802294741 6751331.54659778,508403.264770517 6751459.21197506))) Valid Geometry
WITH cte AS(
    SELECT *,st_boundary(geom) as boundary FROM your_table
    CROSS JOIN ST_Dump(st_makevalid(shape)) with ordinality as dp(path,geom,n)
    WHERE objectid = 8888
) 
SELECT st_astext(st_makepolygon(st_makeline(array[ 
          ST_GeometryN(st_split(a.boundary,st_intersection(a.geom,b.geom)),1)
         ,ST_GeometryN(st_split(b.boundary,st_intersection(a.geom,b.geom)),2)
         ,ST_GeometryN(st_split(b.boundary,st_intersection(a.geom,b.geom)),1)
         ,ST_GeometryN(st_split(a.boundary,st_intersection(a.geom,b.geom)),2)
       ])))
FROM cte AS a 
JOIN cte AS b ON a.n=1 AND 1<>b.n;
st_astext
POLYGON((0 2,1 1,2 2,2 0,1 1,0 0,0 2))

It manages to re-tie the bowtie above, and changing objectid to 1234 shows you can tie the valid Polygons inside your MultiPolygon, back into an invalid, self-intersecting unary Polygon:

POLYGON((508416.031308245 6751318.78006006,508419.678890453 6751315.13247785,508417.850887359 6751319.31077064,508459.802294741 6751331.54659778,508403.264770517 6751459.21197506,508403.264770517 6751428.20752629,508406.912352725 6751344.31313551,508417.850887359 6751319.31077064,508416.031308245 6751318.78006006))

You can also choose how invalid you want it to be: a regular/basic self-intersection, where it's just crossing its own boundary following segments (a1,b1,b2,a2) or an (a1,b2,b1,a2), resulting in a ring self-intersection (example above). That way the boundary touches at the self-intersection point, without crossing.

It's a proof of concept. It'll quickly fall apart as soon as, for example

  1. the polygons intersect in multiple spots
  2. you give it more than two polygons to tie
  3. the more-than-two polygons all intersect one another
  4. shapes happen to split at their first/origin/start vertex

Just to name a few.

Still, this does work for cases where you're just re-tying what's topologically just a bowtie with a single point self-intersection.
An array_agg() with an adequate order by that would arrange the sequence of sub-segments based on whether you want a ring or regular self-intersection might come handy if you want to handle multiple "twists". It could handle a set of >2 polygons, where one's the main polygon and the rest is a series of twists on its boundary.

0

So, let's try to take the easy way out, and if I understand your question correctly, you just need to convert multipolygons to polygons.

The original geodata is drawn in Figure 1 based on the geodata you provided.

enter image description here

Figure 1

Run the query:

WITH
    poly(geom) AS (SELECT ST_SETSrid(ST_BuildArea('MULTIPOLYGON(((508416.031308245 6751318.78006006,508419.678890453 6751315.13247785,508417.850887359 6751319.31077064,508416.031308245 6751318.78006006)),((508403.264770517 6751459.21197506,508403.264770517 6751428.20752629,508406.912352725 6751344.31313551,508417.850887359 6751319.31077064,508459.802294741 6751331.54659778,508403.264770517 6751459.21197506)))'
                                                 ),3857) geom)
   SELECT ((ST_Dump(geom)).geom) geom FROM poly

The result is shown in Figure 2 as two separate entries in the table, which means that the ST_Dump() function did its job correctly.

enter image description here

Figure 2

P.S.

The ST_Union() function, in addition to merging geometries, converts geodata from the "POLYGON" type to the "MYLTIPOLYGON" type, and the ST_Dump() function splits it, i.e. from the "MYLTIPOLYGON" type to the "POLYGON" type, and so on for all types of geodata, for example - POINT, LINESTRING, POLYGON, etc.

Adapt this approach to your query and I hope that now you will get the required result... i.e. add ST_Dump() function to your query before ST_Union()...


After the comment @Zegarek, I decided to experiment with the data further and this is what happens - the PostGIS function library does not allow you to implement what you are asking for and that is their right.

Experiment

WITH
poly(geom) AS (SELECT ST_SETSrid(ST_BuildArea('MULTIPOLYGON(((508416.031308245 6751318.78006006,
                                              508419.678890453 6751315.13247785,
                                              508417.850887359 6751319.31077064,
                                              508416.031308245 6751318.78006006)),
                                              ((508403.264770517 6751459.21197506,
                                              508403.264770517 6751428.20752629,
                                              508406.912352725 6751344.31313551,
                                              508417.850887359 6751319.31077064,
                                              508459.802294741 6751331.54659778,
                                              508403.264770517 6751459.21197506)))'
                                                 ),3857) geom),
tbla AS (SELECT row_number() OVER() id, ((ST_Dumppoints(geom)).geom) geom FROM poly),
tblb AS (SELECT row_number() OVER() id, ((ST_DumpPoints(geom)).geom) geom FROM tbla),
unique_geoms AS (SELECT row_number() OVER (PARTITION BY ST_AsBinary(geom)) id, geom FROM tblb),
pts AS (SELECT row_number() OVER() id, geom FROM unique_geoms WHERE id=1)
SELECT ST_AsText(ST_MakeValid(ST_BuildArea(ST_Union(geom)))) geom FROM (
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (1,2) UNION 
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (2,6) UNION 
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (6,1) UNION 
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (1,7) UNION 
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (7,3) UNION 
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (3,4) UNION 
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (4,5) UNION 
SELECT ST_MakeLine(geom) geom FROM pts WHERE id IN (5,1)) foo

Rezalt

MULTIPOLYGON(((508417.850887359 6751319.31077064,508419.678890453 6751315.13247785,508416.031308245 6751318.78006006,508417.850887359 6751319.31077064)),((508406.912352725 6751344.31313551,508403.264770517 6751428.20752629,508403.264770517 6751459.21197506,508459.802294741 6751331.54659778,508417.850887359 6751319.31077064,508406.912352725 6751344.31313551)))

The interim conclusions are as follows:

  1. This is a problem that needs to be resolved.
  2. It is a problem that, without workarounds, has not yet been resolved.
  3. We need to document the concept of a self-touching polygons (a valid polygon) that only has an appearance similar to a self-intersecting multipolygons (not a valid polygon).
  4. A function that can distinguish between a self-intersecting multipolygon and a self-touching polygons needs to be developed.
  5. We need to develop function(s) that convert a self-intersecting multipolygon to a self-touching polygon and vice versa.

CM-FOGS...

Translated with DeepL.com (free version)

2
  • convert multi-part multipolygon to self-intersecting polygon (both singular) and break the multipolygon in parts and then use ST_Union to merge polygons (single input, multiple intermediate shapes, single merged output shape) - I think OP's aim is to process the single input MultiPolygon into a single output Polygon, avoiding the separation into standalone polygons or one multi with multiple polygon parts.
    – Zegarek
    Commented Apr 21 at 11:44
  • @Zegarek, I think we should wait for clarifications and comments from the OP, maybe something is still hidden from us. If what you described is really the OP's question, then a new type of polygon should be introduced - a self-touching polygon and the question should sound like this - How to create self-touching polygon from self-intersecting myltipolygons.... and then the answer dr_jts is sufficient and clear.... Commented Apr 21 at 17:28

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.