Combinar todos y cada uno de los polígonos adyacentes

22

Me gustaría hacer pruebas de adyacencia en una capa de parcelas (polígonos) y combinarlas si se ajustan a ciertos criterios (podría ser el tamaño). Por la imagen de abajo, me gustaría unir los polígonos 1,2,3 y 4, pero no 5.

Tengo dos problemas:

  1. ST_TOUCHES devuelve VERDADERO si solo las esquinas se tocan y no un segmento de línea. Creo que necesito ST_RELATE para verificar los segmentos de línea compartidos.
  2. Idealmente, me gustaría fusionar TODOS los polígonos adyacentes en uno solo, 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 unión automática en ST_TOUCHES .

Datosdejuguete

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 id. de polígono = 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.

  1. Entonces primero , ¿cómo especifico ST_Relate para asegurarme de que solo se consideren las parcelas que comparten un segmento de línea?

  2. Y luego, ¿cómo combinarí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 a la inversa? / p>

Actualizar

Si agrego esto a la cláusula where , obviamente solo obtengo polígonos y no multipolígonos, por lo que eliminé los falsos positivos para mis propósitos: los toques en las esquinas se ignorarán.

GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'

Si bien esto no es lo ideal (preferiría usar las comprobaciones de topología con ST_RELATE como una solución más general), es un camino a seguir. Luego queda la cuestión de desincrustar y unir estos. Posiblemente, si pudiera generar una secuencia para solo polígonos en contacto, podría unirme en eso.

Actualización II

Este parece funcionar para seleccionar polígonos que compartan líneas (pero no esquinas) y, por lo tanto, es una solución más general que la prueba MULTIPOLYGON anterior. Mi cláusula donde 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 hacer la fusión para más que solo un par de polígonos, pero para un número arbitrario que se ajusta a los criterios, de una sola vez.

    
pregunta ako 18.12.2014 - 06:11

4 respuestas

4

No pude evitar pensar que su ejemplo es en realidad un ráster y, aunque mencionó que le gustaría fusionarse basándose en "ciertos criterios (podría ser el tamaño)", me gustaría darle una oportunidad con una conversión de ráster.

Para su ejemplo específico, esto funcionaría:

WITH rast AS (
  SELECT 
  ST_UNION(ST_AsRaster(geom,10, 20, '2BUI')) r
  FROM testpoly 
)
,p AS (
    SELECT (ST_DumpAsPolygons(r)).geom FROM rast
)
SELECT t.id,p.* 
FROM p
LEFT JOIN testpoly  t ON ST_Equals(p.geom, t.geom)

Lo que sucede es que, dado que tus polígonos son celdas perfectamente alineadas, se convertirán muy bien en un ráster (tamaño de celda 10x20). Los dumpaspolygons te ayudan aquí fusionando todas las celdas adyacentes en una sola y comparando con los polígonos originales, incluso podrás recuperar la ID de las poli no fusionadas.

Habiendo explicado esto, siento mucha curiosidad por la forma en que se escalaría y el tamaño de su conjunto de datos: D

    
respondido por el tilt 03.11.2016 - 22:06
3

Este es un ejemplo de cómo hacer esto en un estilo de procedimiento con varios pases bajo el capó.

CREATE TABLE joined_testpoly AS SELECT array[id] ids, geom FROM testpoly; 

Debería poder llevar más columnas y aplicar criterios adicionales para unirse modificando la forma en que funciona el siguiente LIMIT 1 select:

CREATE OR REPLACE FUNCTION reduce_joined_testpoly()
RETURNS void
AS $$
DECLARE
  joined_row joined_testpoly%ROWTYPE;
BEGIN
  LOOP
     SELECT array_cat(a.ids, b.ids), st_union(a.geom, b.geom)
         INTO joined_row 
     FROM joined_testpoly a INNER JOIN joined_testpoly b
           on a.ids != b.ids
              and ST_Touches(a.geom, b.geom) and a.geom && b.geom 
              and ST_Relate(a.geom, b.geom)='FF2F11212'
         LIMIT 1;
     IF NOT FOUND THEN
           EXIT;
     END IF;
     INSERT INTO joined_testpoly VALUES (joined_row.ids, joined_row.geom);
     DELETE FROM joined_testpoly
         WHERE joined_testpoly.ids <@ joined_row.ids 
           AND joined_testpoly.ids != joined_row.ids;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;

Ejecuta la cosa:

SELECT reduce_joined_testpoly();

Uniones adecuadas, no multipolígonos:

SELECT ids, st_geometrytype(geom), st_area(geom), st_numgeometries(geom) 
FROM joined_testpoly;
    ids    | st_geometrytype | st_area | st_numgeometries 
-----------+-----------------+---------+------------------
 {5}       | ST_Polygon      |     200 |                1
 {1,2,3,4} | ST_Polygon      |     800 |                1
    
respondido por el EoghanM 04.05.2018 - 14:54
2

Aquí hay otra (no funciona) estrategia de referencia (que no pude excluir el único punto de contacto). Debería ser más rápido que mi otra respuesta, ya que solo toma 'una pasada'.

SELECT st_numgeometries(g), (SELECT st_union(x.geom) FROM st_dump(g) x GROUP BY g)
FROM (
    SELECT unnest(st_clusterintersecting(geom)) g, id < 100 as other_arbitrary_grouping 
    FROM testpoly
    GROUP BY other_arbitrary_grouping) c;

(no dude en enmendar y publicar otra respuesta si alguien puede obtener la geometría id = 5 en su propio grupo)

Para recuperar 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: enlace pero no pude conseguir que funcionara para polígonos por alguna razón.

    
respondido por el EoghanM 04.05.2018 - 17:46
2

Aquí le damos un rápido vistazo utilizando su consulta original ajustada un poco:

with gr as (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 ST_Relate(t1.geom,t2.geom, '****1****')
      AND t1.geom && t2.geom 
) 
l2) select ST_AsText(st_union(gr.geo_combo)) from gr;

Referencias: enlace

    
respondido por el cm1 04.05.2018 - 19:00

Lea otras preguntas en las etiquetas