I would like to do adjacency tests on a parcel (polygons) layer and merge them if they fit certain criteria (could be size). Per the picture below, I would like to merge polygons 1,2,3 and 4, but not 5.
I have two problems:
ST_TOUCHES
returns TRUE if just the corners touch and not a line segment. I think I need ST_RELATE to check for shared line segments.- Ideally, I would like to merge ALL adjacent polygons into one, but I am not sure how to scale beyond two--as in, merge 1,2,3 and 4 (and possibly more on actual data) in one round.
The structure I have now is based on a self join on ST_TOUCHES
.
Toy data
CREATE TABLE testpoly AS
SELECT
1 AS id, ST_PolyFromText('POLYGON ((0 0, 10 0, 10 20, 00 20, 0 0 ))') AS geom UNION SELECT
2 AS id, ST_PolyFromText('POLYGON ((10 0, 20 0, 20 20, 10 20, 10 0 ))') AS geom UNION SELECT
3 AS id, ST_PolyFromText('POLYGON ((10 -20, 20 -20, 20 0, 10 0, 10 -20 ))') AS geom UNION SELECT
4 AS id, ST_PolyFromText('POLYGON ((20 -20, 30 -20, 30 0, 20 0, 20 -20 ))') AS geom UNION SELECT
5 AS id, ST_PolyFromText('POLYGON ((30 0, 40 0, 40 20, 30 20, 30 0 ))') AS geom ;
Selection
SELECT
gid, adj_gid,
st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
--level 2
SELECT
t1.id AS gid,
t1.geom AS g1,
t2.id AS adj_gid,
t2.geom AS g2
from
testpoly t1,
testpoly t2
where
ST_Touches( t1.geom, t2.geom )
AND t1.geom && t2.geom
)
l2
Here is the output:
+-----+---------+-------------------------------------------------------------------------------+
| gid | adj_gid | geo_combo |
+-----+---------+-------------------------------------------------------------------------------+
| 1 | 2 | POLYGON((10 0,0 0,0 20,10 20,20 20,20 0,10 0)) |
+-----+---------+-------------------------------------------------------------------------------+
| 1 | 3 | MULTIPOLYGON(((10 0,0 0,0 20,10 20,10 0)),((10 0,20 0,20 -20,10 -20,10 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 2 | 1 | POLYGON((10 20,20 20,20 0,10 0,0 0,0 20,10 20)) |
+-----+---------+-------------------------------------------------------------------------------+
| 2 | 3 | POLYGON((10 0,10 20,20 20,20 0,20 -20,10 -20,10 0)) |
+-----+---------+-------------------------------------------------------------------------------+
| 2 | 4 | MULTIPOLYGON(((20 0,10 0,10 20,20 20,20 0)),((20 0,30 0,30 -20,20 -20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 3 | 1 | MULTIPOLYGON(((10 0,20 0,20 -20,10 -20,10 0)),((10 0,0 0,0 20,10 20,10 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 3 | 2 | POLYGON((20 0,20 -20,10 -20,10 0,10 20,20 20,20 0)) |
+-----+---------+-------------------------------------------------------------------------------+
| 3 | 4 | POLYGON((20 -20,10 -20,10 0,20 0,30 0,30 -20,20 -20)) |
+-----+---------+-------------------------------------------------------------------------------+
| 4 | 2 | MULTIPOLYGON(((20 0,30 0,30 -20,20 -20,20 0)),((20 0,10 0,10 20,20 20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 4 | 3 | POLYGON((20 0,30 0,30 -20,20 -20,10 -20,10 0,20 0)) |
+-----+---------+-------------------------------------------------------------------------------+
| 4 | 5 | MULTIPOLYGON(((30 0,30 -20,20 -20,20 0,30 0)),((30 0,30 20,40 20,40 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 5 | 4 | MULTIPOLYGON(((30 0,30 20,40 20,40 0,30 0)),((30 0,30 -20,20 -20,20 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+
Note that polygon id=3 shares a point with id=1 and thus is returned as a positive result. If I change the WHERE clause to ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');
I get no records at all.
So first, how do I specify ST_Relate to make sure only parcels sharing a line segment are considered.
And then, how would I merge polygons 1,2,3,4 in one round, collapsing the results from the above call, all the while recognizing that adjacency 1 to 2 is the same as the reverse?
Update
If I add this to the where
clause I obviously only get polygons and not multipolygons, thus weeding out false positives for my purposes--corner touches will be ignored.
GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'
While this is not ideal (I would rather use topology checks with ST_RELATE
as a more general solution), it is a way forward. Then remains the matter of de-duping and union'ing these. Possibly, if I could generate a sequence for only polygons touching, I could union on that.
Update II
This one seems to work for selecting polygons sharing lines (but not corners) and is thus a more general solution than the above MULTIPOLYGON
test. My where clause now looks like this:
WHERE
ST_Touches( t1.geom, t2.geom )
AND t1.geom && t2.geom
-- 'overlap' relation
AND ST_Relate(t1.geom, t2.geom)='FF2F11212') t2
Now what remains is still how to do the merge for more than just a pair of polygons, but for an arbitrary number fitting the criteria, in one go.
ST_IntersectionArray
[function][1] to work with ST_Union [1]: gis.stackexchange.com/a/60295/36886