[GIS] How to break multilinestring into constituent linestrings in PostGIS

postgis

I have a multilinestring in PostGIS which I need to divide into segments that break anywhere there is an intersection. So in the below example, the lines need to be split at the each point, since that's where more than two parts converge.

Example:

o--       o
   \      |
    o-----o---o
   /     /
  o      |
         o

This a simplification of my data. I've tried ST_LineMerge() but that just returned most of my multilinestrings back to me as is. One complicating factor is that there are many parts in the multilinestring that begin or end at the intersection of only two parts. So in the example below, ST_Dump() breaks the geometry into two parts, but I want them to be stitched together as a single linestring (since only two parts are converging).

     o
    /
   o------o

What is a good strategy for approaching this problem?

Best Answer

A combination of both ST_LineMerge and ST_Dump should give you the result that you want.

First apply ST_LineMerge to combine single line intersections, then ST_Dump to break apart the multi-linestrings.

If you have linestrings that go through an intersection without breaking you will also need to use ST_Node to segment the lines at the intersections.

An example query is

SELECT id, (dump_set).path AS line_part, ST_AsEWKT((dump_set).geom) AS line_string
FROM (
    SELECT id, ST_Dump(ST_LineMerge(ST_Node(geom))) AS dump_set
    FROM (VALUES
        (1, 'MULTILINESTRING((0 10, 10 10),(10 0, 10 10),(10 0, 20 0))'::Geometry),
        (2, 'MULTILINESTRING((0 10, 10 10),(10 10, 10 20),(10 0, 10 10))'::Geometry),
        (3, 'MULTILINESTRING((0 10, 10 10),(10 10, 10 20),(10 0, 10 10),(10 0, 20 0))'::Geometry),
        (4, 'MULTILINESTRING((0 10, 10 10, 10 20),(10 0, 10 10),(10 0, 20 0))'::Geometry)
        )Lines(id,geom)
    ) AS dump_results;

Stepping through each part of the operation using the 4th row from above.

-- Multi line string composed of three parts.  Intersection between first part and second at 10 10
SELECT ST_AsEWKT(
    ST_Node(geom) -- Splits part one at 10 10
    )
FROM ( VALUES(4, 'MULTILINESTRING((0 10, 10 10, 10 20),(10 0, 10 10),(10 0, 20 0))'::Geometry) )Lines(id,geom);
-- RESULT: "MULTILINESTRING( (0 10,10 10) , (10 10,10 20) , (10 10,10 0) , (10 0,20 0) )"

SELECT ST_AsEWKT(
    ST_LineMerge( -- merges parts 3 and 4 at 10 0
        ST_Node(geom)
    )
    )
FROM ( VALUES(4, 'MULTILINESTRING((0 10, 10 10, 10 20),(10 0, 10 10),(10 0, 20 0))'::Geometry) )Lines(id,geom);
-- RESULT: "MULTILINESTRING( (0 10,10 10) , (10 10,10 20) , (10 10,10 0,20 0) )"

SELECT ST_AsEWKT(
    (
        ST_Dump ( -- Returns the components of the multi linestring in an geometry_dump type
            ST_LineMerge( 
                ST_Node(geom)
            )
        )
    ).geom -- Extracts the geometry component of the geometry_dump type
    )
FROM ( VALUES(4, 'MULTILINESTRING((0 10, 10 10, 10 20),(10 0, 10 10),(10 0, 20 0))'::Geometry) )Lines(id,geom);
-- RESULT:
--    "LINESTRING(0 10,10 10)"
--    "LINESTRING(10 10,10 20)"
--    "LINESTRING(10 10,10 0,20 0)"
Related Question