Me gustaría hacer pruebas de adyacencia en una capa de parcelas (polígonos) y fusionarlas si se ajustan a ciertos criterios (podría ser el tamaño). Según la imagen siguiente, me gustaría fusionar los polígonos 1,2,3 y 4, pero no 5.
Tengo dos problemas:
ST_TOUCHES
devuelve TRUE si sólo se tocan las esquinas y no un segmento de línea. Creo que necesito ST_RELATE para comprobar si hay segmentos de línea compartidos.- Idealmente, me gustaría fusionar TODOS los polígonos adyacentes en uno, pero no estoy seguro de cómo escalar más allá de dos - es decir, fusionar 1,2,3 y 4 (y posiblemente más en los datos reales) en una ronda.
La estructura que tengo ahora se basa en una autounión en ST_TOUCHES
.
Datos del juguete
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 ;
Selección
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
Este es el resultado:
+-----+---------+-------------------------------------------------------------------------------+
| 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))) |
+-----+---------+-------------------------------------------------------------------------------+
Observe que el polígono id=3 comparte un punto con id=1 y, por tanto, se devuelve como resultado positivo. Si cambio la cláusula WHERE por ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');
No obtengo ningún registro.
-
Así que primero ¿Cómo puedo especificar ST_Relate para asegurarme de que sólo se tienen en cuenta las parcelas que comparten un segmento de línea?
-
Y entonces, ¿cómo fusionaría los polígonos 1,2,3,4 en una ronda, colapsando los resultados de la llamada anterior, todo ello reconociendo que la adyacencia 1 a 2 es la misma que la inversa?
Actualización
Si añado esto al where
Obviamente sólo obtengo polígonos y no multipolígonos, eliminando así los falsos positivos para mis propósitos: los toques en las esquinas serán ignorados.
GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'
Aunque esto no es lo ideal (preferiría utilizar comprobaciones de topología con ST_RELATE
como solución más general), es un camino a seguir. Entonces queda la cuestión de de-duping y union'ing estos. Posiblemente, si pudiera generar una secuencia sólo para los polígonos que se tocan, podría unirlos.
Actualización II
Esta parece funcionar para seleccionar polígonos que comparten líneas (pero no esquinas) y, por tanto, es una solución más general que la anterior MULTIPOLYGON
test. Mi cláusula where tiene ahora este aspecto:
WHERE
ST_Touches( t1.geom, t2.geom )
AND t1.geom && t2.geom
-- 'overlap' relation
AND ST_Relate(t1.geom, t2.geom)='FF2F11212') t2
Ahora lo que queda es cómo hacer la fusión para más de un par de polígonos, sino para un número arbitrario que se ajuste a los criterios, de una sola vez.