Wie kann man rekursiv durch übergeordnete Polygonschnittpunkte durchlaufen, um kleinste (untergeordnete) Polygone ohne Überlappungen zu erhalten?

11

Ich habe ein paar Tage mit einem Problem zu kämpfen und festgestellt, dass viele Leute auch stecken bleiben, wenn es um Schnittpunkte in PostGIS (v2.5) geht. Aus diesem Grund habe ich mich entschlossen, eine detailliertere und allgemeinere, häufig gestellte Frage zu stellen.

Ich habe folgende Tabelle:

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

Es sieht aus wie das:

Anfang

Ich möchte alle untergeordneten Polygone basierend auf dem Schnittpunkt der übergeordneten Polygone erhalten. Für das Ergebnis wäre zu erwarten:

  • Die untergeordneten Polygone ohne Überlappung zwischen ihnen.
  • Eine Spalte, die die Summe des Werts ihrer übergeordneten Polygone enthält.
  • Eine Spalte, die die Anzahl der übergeordneten Polygone einer Kategorie enthält
  • Eine Spalte, die die Anzahl einer anderen Kategorie enthält
  • Eine Spalte, die die Kategorie des untergeordneten Polygons enthält, basierend auf der folgenden Regel: -Wenn ALLE übergeordneten Polygone aus einer Klasse stammen, hat das untergeordnete Polygon auch diese Klasse. Andernfalls ist die Kategorie des untergeordneten Polygons eine dritte Kategorie.

So sieht es aus:

Ausgabe

Also, am Ende erzielte die Ausgabetabelle (für dieses Beispiel) hat 7 Zeilen (alle den 7, nicht-überlappende, Kind Polygone), mit Spalten category, sum_value, ct_overlap_cat1,ct_overlap_cat2

Der folgende Code, den ich gestartet habe, gibt mir die einzelnen Schnittpunkte und vergleicht einen Elternteil mit einem anderen.

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

Wie durchlaufe ich rekursiv das Ergebnis dieses genannten Codes, dass ich unabhängig von der Anzahl der Überlappungspolygone immer die "kleinsten" (untergeordneten) Polygone erhalte (Abb. 2)?

Matt_Geo
quelle

Antworten:

8

Versuche dies:

Laden Sie die PostGIS-Addons von diesem Link herunter: https://github.com/pedrogit/postgisaddons

Installieren Sie die Datei, indem Sie die Datei postgis_addons.sql ausführen, um die Funktion ST_SplitAgg () abzurufen.

Testen Sie, indem Sie die Datei postgis_addons_test.sql ausführen.

Hier ist Ihre Anfrage:

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
quelle
Ich habe mir deine Addons Git Repo schon einmal angesehen. Inspirierende Sachen.
John Powell
Wow tolle Lösung. Sie haben auch bei der Erstellung dieser Addons großartige Arbeit geleistet. Bevor ich auf diese Antwort klicke, möchte ich nur eine, um sicherzugehen, dass mich etwas nervt. Wenn Sie den von Ihnen angegebenen Code ausführen, scheint das 'Polygon 5' (der zweiten Abbildung der Frage) die Überlappung mit einem anderen Polygon nicht zu erkennen ('ct_overlap_cat2 = 1'; 'ct_overlap_cat2 = 0'). Daher wird dieses Polygon als "cat1" und "sum = 2" anstelle von "cat3" und "sum = 7" klassifiziert. Ich habe ein bisschen Schwierigkeiten, dieses kleine Problem zu beheben. Kannst du mir helfen?
Matt_Geo
1
Das einzige Problem bei dieser Lösung ist, dass die case-Anweisungen fest codiert sind. Im Prinzip sollte es wahrscheinlich in der Lage sein, eine beliebige Anzahl von Kategorien zu handhaben.
John Powell
@Matt_Geo Ich erhalte 6 Polygone in der resultierenden Tabelle. Das Dreieckspolygon wird in drei Teile geteilt. Eins mit Summe = 2, eins mit Summe = 7 und eins mit Summe = 8 wie in Ihrer gewünschten Zahl.
Pierre Racine
1
Was ist, wenn Sie ST_Centroid (geom) durch ST_Area (geom) ersetzen?
Pierre Racine
1

Ich nehme an, wenn Sie den Polygongeometrietyp anstelle von MultiPolygon verwenden, passt alles zusammen:

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

Das Ergebnis sind 9 Einträge, die den verschiedenen Schnittoptionen in dem von Ihnen angegebenen Beispiel entsprechen.

Cyril Mikhalchenko
quelle