SpatialDB Advisor
I received an email a few weeks ago asking:
I was wondering if you could post an article explaining how to create a polygon from overlapping lines, if this is possible.
I am new as in 1 week into exploring this product and am finding the documentation for ST_BuildArea and the like a little hard to understand at the moment.
This is an example of the sort of thing I would like to be able to do.
The correspondant provided me with an ascii image of 4 overlapping lines. Here is an image of the actual test data I used as it is the same as what he asked (except I have added a mid-point vertex in each linestring).

First off, let’s build a table and populate it with linestrings.
drop table test;
| gid integer | st_astext text |
|---|---|
| 1 | “LINESTRING (1 0,1 5,1 11)” |
| 2 | “LINESTRING (10 0,10 5,10 11)” |
| 3 | “LINESTRING (0 1,5 1,11 1)” |
| 4 | “LINESTRING (0 10,5 10,11 10)” |
| 6 | “POINT (1 5)” |
| 7 | “POINT (1 11)” |
| 1 | “POINT (1 0)” |
| 9 | “POINT (10 5)” |
| 10 | “POINT (10 11)” |
| 2 | “POINT (10 0)” |
| 12 | “POINT (5 1)” |
| 13 | “POINT (11 1)” |
| 3 | “POINT (0 1)” |
| 15 | “POINT (5 10)” |
| 16 | “POINT (11 10)” |
| 4 | “POINT (0 10)” |
The steps to build a polygon from the four linestrings is:
Here is the SQL that does all this.
insert into test (geom)
select ST_SetSRID(ST_BuildArea(ST_Collect(ST_GeomFromText(p.geom))),28355) as geom
from (select DISTINCT ST_AsText(g.geom) as geom, g.iPoint
from (select (ST_Dump(e.geom)).geom,
f.ig as iPoint
from (SELECT /* This query is the set of each line with each and every line that intersects it */
ST_SymDifference((select a.geom from test a where a.gid = c.gid),c.geom) as geom
FROM (select a.gid as gid, ST_Collect(b.geom) as geom
from test a,
test b
where a.gid <> b.gid
and ST_Intersects(a.geom,b.geom)
group by a.gid
) as c
) e,
(select /* Collect the set of all intersection points in a single multipoint geometry */
ST_Collect(i.point) as ig
from ( select ST_SetSRID(ST_Point(ST_X(a.pnt),ST_Y(a.pnt)),28355) as point
from (select ST_Intersection(a.geom,b.geom) as pnt
from test a,
test b
where a.gid <> b.gid
and ST_Intersects(a.geom,b.geom)
) as a
group by ST_X(a.pnt), ST_Y(a.pnt)
having count(*) > 1
) i
) f
) g
where /* We only want those linestrings that have an intersection point (see d3) at both ends */
ST_Intersects(g.ipoint,ST_StartPoint(g.geom))
and ST_Intersects(g.ipoint,ST_EndPoint(g.geom))
) as p;
Execution of this SQL gives us a lovely, square polygon with all the mid-point vertices in tact (I added them in to show that any internal vertices, not matter how many describing a complex line, are maintained).

Now, ST_BuildArea will work with more lines that just 4 straight lines forming a square. I will demonstrate by processing a hexagon (I won’t include the processing SQL as it is the same.)
-- Test Hexagon
drop table test;
| gid integer | st_astext text |
|---|---|
| 1 | “LINESTRING (5 14,13 10)” |
| 2 | “LINESTRING (13 2,5 -1)” |
| 3 | “LINESTRING (-1 10,7 14)” |
| 4 | “LINESTRING (1 12,-2 5)” |
| 5 | “LINESTRING (-2 7,1 0)” |
| 6 | “LINESTRING (-1 2,7 -1)” |
| 7 | “LINESTRING (11 12,14 5)” |
| 8 | “LINESTRING (11 0,14 7)” |
The formed Hexagonal polygon is as follows.

It is not possible have ST_BuildArea create two polygons where those polygons share a simple line.
Here is the data I used.
-- Test 5 lines 2 polygons
drop table test;
| gid integer | st_astext text |
|---|---|
| 1 | “LINESTRING (1 0,1 11)” |
| 2 | “LINESTRING (10 0,10 11)” |
| 3 | “LINESTRING (5.5 0,5.5 11)” |
| 4 | “LINESTRING (0 1,11 1)” |
| 5 | “LINESTRING (0 10,11 10)” |
And you can see from the shading that there is only one polygon even though 5 linestrings went in to ST_BuildArea.

However, even though this is an article about ST_BuildArea, ST_Polygonize will do what we want (thanks to Regina Obe for pointing this out in the comments below).
Here is a modified version of the SQL that finishes with the MULTIPOLYGON that ST_Polygonize produces being “exploded” into individual polygons for writing back to the table.
-- ST_BuildArea doesn't produce two polygons so let's try ST_Polygonize
insert into test (geom)
SELECT geom
FROM ST_Dump(
(SELECT ST_SetSRID(ST_Polygonize(ST_GeomFromText(p.geom)),28355)
from (select DISTINCT ST_AsText(g.geom) as geom, g.iPoint
from (select (ST_Dump(e.geom)).geom,
f.ig as iPoint
from (SELECT /* This query is the set of each line with each and every line that intersects it */
ST_SymDifference((select a.geom from test a where a.gid = c.gid),c.geom) as geom
FROM (select a.gid as gid, ST_Collect(b.geom) as geom
from test a,
test b
where a.gid <> b.gid
and ST_Intersects(a.geom,b.geom)
group by a.gid
) as c
) e,
(select /* Collect the set of all intersection points in a single multipoint geometry */
ST_Collect(i.point) as ig
from ( select ST_SetSRID(ST_Point(ST_X(a.pnt),ST_Y(a.pnt)),28355) as point
from (select ST_Intersection(a.geom,b.geom) as pnt
from test a,
test b
where a.gid <> b.gid
and ST_Intersects(a.geom,b.geom)
) as a
group by ST_X(a.pnt), ST_Y(a.pnt)
having count(*) > 1
) i
) f
) g
where /* We only want those linestrings that have an intersection point (see d3) at both ends */
ST_Intersects(g.ipoint,ST_StartPoint(g.geom))
and ST_Intersects(g.ipoint,ST_EndPoint(g.geom))
) As p)) As final;
The following image shows that two polygons are created.

Finally, what happens if we give ST_BuildArea the linework for two disjoint polygons? Here is the linework.

Here is the code to create a table with the linework.
-- Can we create multiple polygons?
drop table test;
| gid integer | st_astext text |
|---|---|
| 1 | “LINESTRING” |
| 2 | “LINESTRING” |
| 3 | “LINESTRING” |
| 4 | “LINESTRING” |
| 5 | “LINESTRING” |
| 6 | “LINESTRING” |
| 7 | “LINESTRING” |
| 8 | “LINESTRING” |
Here is a modified version of the SQL that, as with ST_Polygonize, finishes with the MULTIPOLYGON that ST_BuildArea produces being “exploded” into individual polygons for writing back to the table.
insert into test (geom)
SELECT (ST_Dump(polys.geom)).geom
FROM (select ST_SetSRID(ST_BuildArea(ST_Collect(p.geom)),28355) as geom
from (select DISTINCT g.geom, g.iPoint
from (select (ST_Dump(e.geom)).geom,
f.ig as iPoint
from (select /* Intersect each line witheach and every line that intersects it */
ST_SymDifference((select a.geom from test a where a.gid = c.gid),c.geom) as geom
FROM (select a.gid as gid, ST_Collect(b.geom) as geom
from test a,
test b
where a.gid <> b.gid
and ST_Intersects(a.geom,b.geom)
group by a.gid
) as c
) e,
(select ST_Collect(i.point) as ig
from ( select ST_SetSRID(ST_Point(ST_X(a.pnt),ST_Y(a.pnt)),28355) as point
from (select ST_Intersection(a.geom,b.geom) as pnt
from test a,
test b
where a.gid <> b.gid
and ST_Intersects(a.geom,b.geom)
) as a
group by ST_X(a.pnt), ST_Y(a.pnt)
having count(*) > 1
) i
) f
) g
where ST_Intersects(g.ipoint,ST_StartPoint(g.geom)) /* We only want those linestrings that have an intersection point (see d3) at both ends */
and ST_Intersects(g.ipoint,ST_EndPoint(g.geom))
) as p
) as polys;
And here are the polygons:

I hope this article was useful to someone.


















<<Loading Point Data from a CSV File in PostGIS >>Converting Oracle Optimized Rectangles to PostGIS
— Regina Apr 12, 06:34 am #
— Simon Greener Apr 12, 12:12 pm #