Alle angrenzenden Polygone zusammenführen

22

Ich möchte Adjazenztests für eine Parzellenebene (Polygone) durchführen und diese zusammenführen, wenn sie bestimmten Kriterien entsprechen (möglicherweise Größe). In der folgenden Abbildung möchte ich die Polygone 1, 2, 3 und 4 zusammenführen, nicht jedoch 5.

Ich habe zwei Probleme:

  1. ST_TOUCHESGibt TRUE zurück, wenn sich nur die Ecken berühren und kein Liniensegment. Ich glaube, ich brauche ST_RELATE, um nach gemeinsam genutzten Liniensegmenten zu suchen.
  2. Idealerweise möchte ich ALLE benachbarten Polygone zu einem zusammenführen, aber ich bin nicht sicher, wie ich über zwei hinaus skalieren soll - wie in, füge 1,2,3 und 4 (und möglicherweise mehr zu tatsächlichen Daten) in einer Runde zusammen.

Die Struktur, die ich jetzt habe, basiert auf einem Self-Join ST_TOUCHES.

Bildbeschreibung hier eingeben

Spielzeugdaten

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 ;

Auswahl

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

Hier ist die Ausgabe:

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

Beachten Sie, dass das Polygon id = 3 einen Punkt mit id = 1 teilt und daher als positives Ergebnis zurückgegeben wird. Wenn ich die WHERE-Klausel in ändere, ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');erhalte ich überhaupt keine Datensätze.

  1. Wie kann ich also zuerst ST_Relate angeben, um sicherzustellen, dass nur Pakete berücksichtigt werden, die ein Liniensegment gemeinsam nutzen.

  2. Und wie würde ich dann die Polygone 1, 2, 3, 4 in einer Runde zusammenführen und dabei die Ergebnisse des obigen Aufrufs zusammenfassen und gleichzeitig erkennen, dass die Angrenzungen 1 bis 2 die gleichen sind wie die umgekehrten?

Aktualisieren

Wenn ich dies zu der whereKlausel hinzufüge, erhalte ich offensichtlich nur Polygone und keine Multipolygone, wodurch falsche Positive für meine Zwecke ausgesondert werden - Eckberührungen werden ignoriert.

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

Dies ist zwar nicht ideal (ich würde eher Topologieprüfungen ST_RELATEals allgemeinere Lösung verwenden), aber es ist ein Weg nach vorn. Dann bleibt die Frage der Enttäuschung und Vereinigung dieser. Wenn ich eine Sequenz nur für Polygone erzeugen könnte, die sich berühren, könnte ich mich darauf einigen.

Update II

Dieser scheint für die Auswahl von Polygonen zu funktionieren, die Linien (aber keine Ecken) teilen, und ist daher eine allgemeinere Lösung als der obige MULTIPOLYGONTest. Meine where-Klausel sieht jetzt so aus:

WHERE
              ST_Touches( t1.geom, t2.geom ) 
              AND t1.geom && t2.geom 

              -- 'overlap' relation
              AND ST_Relate(t1.geom, t2.geom)='FF2F11212') t2 

Jetzt bleibt nur noch, wie Sie die Zusammenführung für mehr als nur ein Paar Polygone, sondern für eine beliebige Zahl, die den Kriterien entspricht, auf einmal durchführen.

ako
quelle
2
Ich bin sicher, dass ST_Relate der richtige Weg ist. Ich habe ein ähnliches Problem gelöst, indem ich überprüft habe, ob die Länge der Schnittpunkte größer als Null ist, um einzelne Punktschnittpunkte auszuschließen. Ein Hack, aber funktioniert.
John Powell
Wenn es eine Möglichkeit gibt, zusammenhängende Polygone zu Arrays zusammenzufassen, können Sie die ST_IntersectionArrayFunktion [1] so ändern , dass sie mit ST_Union [1] zusammenarbeitet: gis.stackexchange.com/a/60295/36886
raphael 23.12.14
2
In Bezug auf die Gruppierung zusammen zusammenhängenden Polygone, könnten Sie den Bottom-up - Clustering - Algorithmus ändern I (hier schrieb gis.stackexchange.com/a/115715/36886 ) zu Test für adjacency anstatt Raum und dann ST_Union verwenden , während auf dem resultierenden cluster_ids Gruppierung
raphael
3
Es gibt auch ST_ClusterIntersectimg, die möglicherweise das tun, was Sie benötigen. Sie benötigen Postgis 2.2
John Powell

Antworten:

3

Ich konnte nicht anders, als zu denken, dass Ihr Beispiel tatsächlich ein Raster ist, und obwohl Sie erwähnt haben, dass Sie basierend auf "bestimmten Kriterien (möglicherweise Größe)" zusammenführen möchten, möchte ich es mit einer Rasterumwandlung versuchen.

Für Ihr spezielles Beispiel würde dies funktionieren:

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)

Da Ihre Polygone perfekt ausgerichtete Zellen sind, werden sie in ein Raster (10 x 20 Zellen) umgewandelt. Die dumpaspolygons helfen Ihnen dabei, indem Sie alle benachbarten Zellen zu einer zusammenführen und durch Vergleich mit den ursprünglichen Polygonen sogar die ID für nicht zusammengeführte Polygone zurückerhalten.

Nachdem ich das erklärt habe, bin ich sehr gespannt, wie sich das skalieren lässt und wie groß Ihr Datensatz ist: D

Neigung
quelle
Clevere Idee. Dies ist jedoch ein Spielzeugbeispiel - meine eigentlichen Daten sind Parcel-Layer, die Raster nicht ordentlich zuordnen können.
Ako
3

Hier ist ein Beispiel, wie dies im prozeduralen Stil mit mehreren Durchgängen unter der Haube durchgeführt wird.

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

Sie sollten in der Lage sein, mehr Spalten mitzunehmen und zusätzliche Kriterien für den Beitritt anzuwenden, indem Sie die Funktionsweise der folgenden LIMIT 1Auswahl ändern:

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;

Führe das Ding aus:

SELECT reduce_joined_testpoly();

Richtige Gewerkschaften, keine Multipolygone:

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
EoghanM
quelle
2

Hier ist eine andere (nicht funktionierende) Strategie als Referenz (die ich nicht ausschließen konnte, wenn es nur um Berührungspunkte ging). Es sollte schneller sein als meine andere Antwort, da es nur einen Durchgang dauert.

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;

(Fühlen Sie sich frei, eine andere Antwort zu ändern und zu posten, wenn jemand die Geometrie id = 5 in seiner eigenen Gruppe erhalten kann.)

Um die Liste der IDs usw. zurückzugewinnen, st_containsmüssten Sie sich wie in der folgenden Antwort beschrieben erneut für die Testpoly-Tabelle anmelden: /programming//a/37486732/6691, aber ich konnte das nicht zum Laufen bringen für Polygone aus irgendeinem Grund.

EoghanM
quelle
2

Hier ist ein kleiner Vorgeschmack auf Ihre ursprüngliche Abfrage:

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;

Referenzen: https://postgis.net/docs/using_postgis_dbmanagement.html#DE-9IM

cm1
quelle