Come ricorrere in modo ricorsivo tra le intersezioni dei poligoni padre per ottenere i poligoni più piccoli (figlio) senza sovrapposizioni?


11

Sto affrontando un problema da un paio di giorni e ho capito che molte persone rimangono bloccate quando l'argomento è l'intersezione in PostGIS (v2.5). Ecco perché ho deciso di porre una domanda più dettagliata e generica, comune.

Ho la seguente tabella:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(MultiPolygon, 4326),
    att_category character varying(15),
    att_value integer
);
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('MULTIPOLYGON (((0 6, 0 12, 8 9, 0 6)))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('MULTIPOLYGON (((5 0, 5 12, 9 12, 9 0, 5 0)))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('MULTIPOLYGON (((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4)))'::geometry,4326) , 'cat2', 5 );

Sembra così:

inizio

Voglio ottenere tutti i poligoni figlio in base all'intersezione dei poligoni padre. Per il risultato, ci si aspetterebbe:

  • I poligoni bambini senza sovrapposizioni tra di loro.
  • Una colonna contenente la somma del valore dei loro poligoni principali,
  • Una colonna contenente il conteggio dei poligoni principali di una categoria
  • Una colonna contenente il conteggio di un'altra categoria
  • Una colonna contenente la categoria del poligono figlio, basata sulla seguente regola: -Se TUTTI i poligoni padre appartengono a una classe, anche il poligono figlio ha questa classe. Altrimenti, la categoria del poligono figlio è una terza categoria.

Quindi sarebbe simile:

produzione

Quindi, alla fine, la tabella di output generato (in questo esempio) avrà 7 righe (tutte le 7, poligoni, non sovrapposti bambino), contenenti colonne category, sum_value, ct_overlap_cat1,ct_overlap_cat2

Il seguente codice che ho iniziato mi dà le singole intersezioni, confrontando un genitore con un altro.

SELECT
(ST_Dump(
    ST_SymDifference(a.geom, b.geom) 
)).geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom)
UNION ALL
SELECT
ST_Intersection(a.geom, b.geom) as geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom);

Come posso ricorrere in modo ricorsivo al risultato di questo codice citato, che, indipendentemente dal numero di poligoni sovrapposti, ottengo sempre i suoi poligoni (più piccoli) (figura 2)?

Risposte:


8

Prova questo:

Scarica i componenti aggiuntivi PostGIS da questo link: https://github.com/pedrogit/postgisaddons

Installa eseguendo il file postgis_addons.sql per ottenere la funzione ST_SplitAgg ().

Test eseguendo il file postgis_addons_test.sql.

Ecco la tua richiesta:

WITH  result_table AS (
    WITH  parts AS (
      SELECT a.att_value val,
             CASE WHEN a.att_category = 'cat1' THEN 1 ELSE 0 END cat1,
             CASE WHEN a.att_category = 'cat2' THEN 1 ELSE 0 END cat2,
             unnest(ST_SplitAgg(a.geom, b.geom, 0.00001)) geom
      FROM tbl_foo a,
           tbl_foo b
      WHERE ST_Equals(a.geom, b.geom) OR
            ST_Contains(a.geom, b.geom) OR
            ST_Contains(b.geom, a.geom) OR
            ST_Overlaps(a.geom, b.geom)
      GROUP BY a.id, a.att_category , ST_AsEWKB(a.geom), val
    )
    SELECT CASE WHEN sum(cat2) = 0 THEN 'cat1'
                WHEN sum(cat1) = 0 THEN 'cat2'
                ELSE 'cat3'
           END category, 
           sum(val*1.0) sum_value, 
           sum(cat1) ct_overlap_cat1, 
           sum(cat2) ct_overlap_cat2, 
           ST_Union(geom) geom
    FROM parts
    GROUP BY ST_Area(geom)
)
SELECT category, sum_value, ct_overlap_cat1, ct_overlap_cat2,
(ST_Dump(result_table.geom)).geom as geom
FROM result_table

Ho già visto i tuoi addon git repo prima. Roba d'ispirazione.
John Powell,

Wow ottima soluzione. Anche tu hai fatto un lavoro fantastico per creare questi componenti aggiuntivi. Prima di fare clic per assegnare questa risposta, ne ho solo uno per essere sicuro di una cosa che mi infastidisce. Eseguendo il codice fornito, il 'poligono 5' (della seconda figura della domanda) non sembra riconoscere la sovrapposizione con un altro poligono ('ct_overlap_cat2 = 1'; 'ct_overlap_cat2 = 0'). Pertanto, questo poligono finisce classificato come 'cat1' e 'sum = 2', invece di 'cat3' e 'sum = 7'. Sto affrontando un po 'di difficoltà nel debug di questo piccolo problema. Potresti aiutarmi?
Matt_Geo,

1
L'unico problema con questa soluzione è che le istruzioni del caso sono codificate. In linea di principio, dovrebbe probabilmente essere in grado di gestire un numero arbitrario di categorie.
John Powell,

@Matt_Geo Ottengo 6 poligoni nella tabella risultante. Il poligono triangolare viene diviso in tre. Uno con somma = 2, uno con somma = 7 e uno con somma = 8 come nella tua figura desiderata.
Pierre Racine,

1
Cosa succede se si sostituisce ST_Centroid (geom) con ST_Area (geom)?
Pierre Racine,

1

Suppongo che se usi il tipo di geometria poligonale anziché MultiPolygon tutto andrà a posto:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(Polygon, 4326),
    att_category character varying(15),
    att_value integer
);

INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('POLYGON ((0 6, 0 12, 8 9, 0 6))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('POLYGON ((5 0, 5 12, 9 12, 9 0, 5 0))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('POLYGON ((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4))'::geometry,4326) , 'cat2', 5 );

Il risultato sono 9 voci che corrispondono alle diverse opzioni di intersezione nell'esempio fornito da te.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.