Comment parcourir récursivement les intersections des polygones parents pour obtenir les plus petits polygones (enfants) sans chevauchements?

11

Je me bats avec un problème depuis quelques jours et j'ai réalisé que beaucoup de gens sont également bloqués lorsque le sujet est les intersections dans PostGIS (v2.5). C'est pourquoi j'ai décidé de poser une question commune plus détaillée et générique.

J'ai le tableau suivant:

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 );

Cela ressemble à ceci:

début

Je veux obtenir tous les polygones enfants en fonction de l'intersection des polygones parents. Pour le résultat, on s'attendrait à:

  • Les polygones enfants sans chevauchement entre eux.
  • Une colonne contenant la somme de la valeur de leurs polygones parents,
  • Une colonne contenant le nombre de polygones parents d'une catégorie
  • Une colonne contenant le nombre d'une autre catégorie
  • Une colonne contenant la catégorie du polygone enfant, basée sur la règle suivante: -Si TOUS les polygones parents appartiennent à une classe, le polygone enfant a également cette classe. Sinon, la catégorie du polygone enfant est une troisième catégorie.

Donc ça ressemble à ça:

production

Ainsi, à la fin, la table de sortie généré (pour cet exemple) aura 7 lignes (tous les 7, ne se chevauchant pas , les polygones de l' enfant), contenant des colonnes category, sum_value, ct_overlap_cat1,ct_overlap_cat2

Le code suivant que j'ai commencé, me donne les intersections individuelles, en comparant un parent avec un autre.

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);

Comment puis-je parcourir récursivement le résultat de ce code mentionné, que, indépendamment du nombre de polygones se chevauchant, j'obtiens toujours ses «plus petits» polygones (enfants) (Fig.2)?

Matt_Geo
la source

Réponses:

8

Essaye ça:

Téléchargez les addons PostGIS à partir de ce lien: https://github.com/pedrogit/postgisaddons

Installez en exécutant le fichier postgis_addons.sql pour obtenir la fonction ST_SplitAgg ().

Testez en exécutant le fichier postgis_addons_test.sql.

Voici votre requête:

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
Pierre Racine
la source
J'ai déjà regardé vos addons git repo. Des trucs inspirants.
John Powell
Wow excellente solution. Vous avez également fait un travail assez étonnant pour créer ces addons. Avant de cliquer pour attribuer cette réponse, j'en ai juste une pour m'assurer d'une chose qui me dérange. En exécutant le code que vous fournissez, le «polygone 5» (de la deuxième figure de la question) ne semble pas reconnaître le chevauchement avec un autre polygone («ct_overlap_cat2 = 1»; «ct_overlap_cat2 = 0»). Par conséquent, ce polygone finit par être classé comme «cat1» et «sum = 2», au lieu de «cat3» et de «sum = 7». J'ai du mal à déboguer ce petit problème. Pourriez-vous m'aider?
Matt_Geo
1
Le seul problème avec cette solution est que les déclarations de cas sont codées en dur. En principe, il devrait probablement pouvoir gérer un nombre arbitraire de catégories.
John Powell
@Matt_Geo J'obtiens 6 polygones dans la table résultante. Le polygone triangulaire est divisé en trois. Un avec somme = 2, un avec somme = 7 et un avec somme = 8 comme dans votre figure souhaitée.
Pierre Racine
1
Et si vous remplacez ST_Centroid (geom) par ST_Area (geom)?
Pierre Racine
1

Je suppose que si vous utilisez un type de géométrie de polygone au lieu de MultiPolygon, tout se mettra en place:

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 );

Le résultat est 9 entrées qui correspondent aux différentes options d'intersection dans l'exemple donné par vous.

Cyril Mikhalchenko
la source