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/36886Respuestas:
No pude evitar pensar que su ejemplo es en realidad una trama y, aunque mencionó que le gustaría fusionarse en base a "ciertos criterios (podría ser el tamaño)", me gustaría darle una oportunidad con una conversión de trama.
Para su ejemplo específico, esto funcionaría:
Lo que sucede es que, dado que sus polígonos son celdas perfectamente alineadas, se convertirán muy bien en un ráster (tamaño de celda 10x20). Los dumpaspolygons lo ayudan aquí al fusionar todas las celdas adyacentes en una y al comparar con los polígonos originales, incluso podrá recuperar la identificación de los polígonos no fusionados.
Habiendo explicado esto, tengo mucha curiosidad sobre cómo se escalará y qué tan grande es su conjunto de datos: D
fuente
Aquí hay un ejemplo de cómo hacer esto en estilo de procedimiento con múltiples pases debajo del capó.
Debería poder llevar más columnas y aplicar criterios adicionales para unirse modificando cómo funciona la
LIMIT 1
selección a continuación:Ejecuta la cosa:
Uniones adecuadas, sin multipolígonos:
fuente
Aquí hay otra estrategia de referencia (que no funciona) (que no pude excluir el caso de punto de contacto único). Debería ser más rápido que mi otra respuesta, ya que solo toma 'una pasada'.
(siéntase libre de enmendar y publicar otra respuesta si alguien puede obtener la geometría id = 5 en su propio grupo)
Para volver a la lista de identificadores, etc., tendría que usar
st_contains
para volver a unirse a la tabla testpoly como se detalla en la siguiente respuesta: /programming//a/37486732/6691 pero no pude hacer que eso funcione para polígonos por alguna razón.fuente
Aquí hay una puñalada rápida usando su consulta original ajustada un poco:
Referencias: https://postgis.net/docs/using_postgis_dbmanagement.html#DE-9IM
fuente