Me gustaría hacer pruebas de adyacencia en una capa de parcela (polígonos) y fusionarlas si se ajustan a ciertos criterios (podría ser el tamaño). Según la imagen a continuación, me gustaría fusionar los polígonos 1,2,3 y 4, pero no 5.
Tengo dos problemas:
ST_TOUCHES
devuelve VERDADERO si solo se tocan las esquinas y no un segmento de línea. Creo que necesito ST_RELATE para buscar 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, como en, fusionar 1,2,3 y 4 (y posiblemente más en datos reales) en una ronda.
La estructura que tengo ahora se basa en una autounión 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
Aquí está la salida:
+-----+---------+-------------------------------------------------------------------------------+
| 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))) |
+-----+---------+-------------------------------------------------------------------------------+
Tenga en cuenta que el polígono id = 3 comparte un punto con id = 1 y, por lo tanto, se devuelve como un resultado positivo. Si cambio la cláusula WHERE a 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.
Entonces , primero , ¿cómo especifico ST_Relate para asegurarme de que solo se consideren las parcelas que comparten un segmento de línea?
Y luego, ¿cómo fusionaría los polígonos 1,2,3,4 en una ronda, colapsando los resultados de la llamada anterior, reconociendo al mismo tiempo que la adyacencia 1 a 2 es lo mismo que el reverso?
Actualizar
Si agrego esto a la where
cláusula, obviamente solo obtengo polígonos y no multipolígonos, eliminando así los falsos positivos para mis propósitos: se ignorarán los toques de esquina.
GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'
Si bien esto no es ideal (prefiero usar las comprobaciones de topología ST_RELATE
como una solución más general), es un camino a seguir. Luego queda la cuestión de eliminarlas y unirlas. Posiblemente, si pudiera generar una secuencia para solo tocar polígonos, podría unirme a eso.
Actualización II
Este parece funcionar para seleccionar polígonos que comparten líneas (pero no esquinas) y, por lo tanto, es una solución más general que la MULTIPOLYGON
prueba anterior . Mi cláusula where ahora se ve así:
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 fusionar más de un par de polígonos, pero para un número arbitrario que se ajuste a los criterios, de una sola vez.
ST_IntersectionArray
[función] [1] para que funcione con ST_Union [1]: gis.stackexchange.com/a/60295/36886