PostGIS - wie ST_Union alle überlappenden Polygone in einer einzigen Tabelle effizient


13

Mein Ziel ist es, eine einzelne Tabelle und st_union alle Polygone, die sich berühren oder nahe beieinander liegen, zu einzelnen Polygonen zusammenzufassen

Ich bin ein C # -Entwickler, der langsam etwas über PostGIS lernt. Mit dem folgenden Code konnte ich dies erreichen, aber es scheint ineffizient zu sein, und PostGIS hat viel zu bieten, was mir neu ist.

Seit meinem ersten Versuch (noch in Kommentaren) konnte ich die Iterationen reduzieren, indem ich array_agg mit ST_UNION verwendete, anstatt nur polys gleichzeitig zu verbinden.

Ich lande mit 133 Polys aus meiner Heimatstadt 173.

sql = "DROP TABLE IF Exists tmpTable; create table tmpTable ( ID varchar(50), Geom  geometry(Geometry,4326), Touchin varchar(50) ); create index idx_tmp on tmpTable using GIST(Geom); ";
                CommandText = sql;
                ExecuteNonQuery();

                sql = "";
                for (int i = 0; i < infos.Count(); i++)
                {
                    sql += "INSERT INTO tmpTable SELECT '" + infos[i].ID + "', ST_GeomFromText('" + infos[i].wkt + "', 4326), '0';";
                }
                CommandText = sql;
                ExecuteNonQuery();

                CommandText = "update tmpTable set touchin = (select id from tmpTable as t where st_intersects(st_buffer(geom, 0.0001), (select geom from tmpTable as t2 where t2.ID = tmpTable.ID ) ) and t.ID <> tmpTable.ID limit 1)";
                ExecuteNonQuery();

                CommandText = "select count(*) from tmpTable where touchin is not null";
                long touching = (long)ExecuteScalar();
                string thisId = "";
                // string otherId = "";
                while (touching > 0)
                {
                    CommandText = "select touchin, count(*)  from tmpTable where touchin is not null group by touchin order by 2 desc limit 1";
                    //CommandText = "select id, touchin from tmpTable where touchin is not null";
                    using (var prdr = ExecuteReader())
                    {
                        CommandText = "";
                        if (prdr.Read())
                        {
                            thisId = prdr.GetString(0);
                             // otherID = prdr.GetString(1);
                            CommandText = @"update tmpTable set geom = st_union(unioned) 
                                from (select array_agg(geom) as unioned from tmpTable where touchin = '" + thisId + "' or id = '" + thisId + @"') as data
                                where id = '" + thisId + "'";
                             // CommandText = "update tmpTable set geom = st_union(geom, (select geom from tmpTable where ID = '" + otherId + "')) where id = '" + thisId + "'";
                        }
                    }

                    if (!string.IsNullOrEmpty(CommandText))
                    {
                        ExecuteNonQuery();
                        //CommandText = "update tmpTable set geom = null, touchin = null where ID = '" + otherId + "'";
                        CommandText = "update tmpTable set geom = null, touchin = null where touchin = '" + thisId + "'";
                        ExecuteNonQuery();                            
                    }

                    CommandText = "update tmpTable set touchin = (select id from tmpTable as t where st_intersects(st_buffer(geom, 0.0001), (select geom from tmpTable as t2 where t2.ID = tmpTable.ID ) ) and t.ID <> tmpTable.ID limit 1)";
                    ExecuteNonQuery();

                    CommandText = "select count(*) from tmpTable where touchin is not null";
                    touching = (long)ExecuteScalar();
                }

Hier ist ein Beispiel des Datensatzes, den ich verwende:

INSERT INTO tmpTable SELECT '872538', ST_GeomFromText('POLYGON((-101.455035985 26.8835084441,-101.455035985 26.8924915559,-101.444964015 26.8924915559,-101.444964015 26.8835084441,-101.455035985 26.8835084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872550', ST_GeomFromText('POLYGON((-93.9484752173 46.0755084441,-93.9484752173 46.0844915559,-93.9355247827 46.0844915559,-93.9355247827 46.0755084441,-93.9484752173 46.0755084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872552', ST_GeomFromText('POLYGON((-116.060688575 47.8105084441,-116.060688575 47.8194915559,-116.047311425 47.8194915559,-116.047311425 47.8105084441,-116.060688575 47.8105084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872553', ST_GeomFromText('POLYGON((-116.043688832 47.8125084441,-116.043688832 47.8214915559,-116.030311168 47.8214915559,-116.030311168 47.8125084441,-116.043688832 47.8125084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872557', ST_GeomFromText('POLYGON((-80.6380222359 26.5725084441,-80.6380222359 26.5814915559,-80.6279777641 26.5814915559,-80.6279777641 26.5725084441,-80.6380222359 26.5725084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872558', ST_GeomFromText('POLYGON((-80.6520223675 26.5755084441,-80.6520223675 26.5844915559,-80.6419776325 26.5844915559,-80.6419776325 26.5755084441,-80.6520223675 26.5755084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872559', ST_GeomFromText('POLYGON((-80.6400224991 26.5785084441,-80.6400224991 26.5874915559,-80.6299775009 26.5874915559,-80.6299775009 26.5785084441,-80.6400224991 26.5785084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872560', ST_GeomFromText('POLYGON((-80.6530226307 26.5815084441,-80.6530226307 26.5904915559,-80.6429773693 26.5904915559,-80.6429773693 26.5815084441,-80.6530226307 26.5815084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872568', ST_GeomFromText('POLYGON((-90.7892258584 30.7365084441,-90.7892258584 30.7454915559,-90.7787741416 30.7454915559,-90.7787741416 30.7365084441,-90.7892258584 30.7365084441))', 4326), '0';
INSERT INTO tmpTable SELECT '872569', ST_GeomFromText('POLYGON((-90.7832259127 30.7375084441,-90.7832259127 30.7464915559,-90.7727740873 30.7464915559,-90.7727740873 30.7375084441,-90.7832259127 30.7375084441))', 4326), '0';

Werden die eigentlichen Daten in Ihrer Frage benötigt?
Paul

@ Paul - war nicht sicher, ob es hilfreich wäre oder nicht.
Carol AndorMarten Liebster

Antworten:


20

Der einfachste Weg wäre zum ST_Uniongesamten Tisch:

SELECT ST_Union(geom) FROM tmpTable;

Dies gibt Ihnen eine riesige MultiPolygon, was wahrscheinlich nicht das ist, was Sie wollen. Sie erhalten die einzelnen gelösten Komponenten mit ST_Dump. So:

SELECT (ST_Dump(geom)).geom FROM (SELECT ST_Union(geom) AS geom FROM tmpTable) sq;

Dies gibt Ihnen ein separates Polygon für jeden Satz berührender Eingaben, aber Gruppen von Eingaben, die durch einen kurzen Abstand voneinander getrennt wurden, bleiben als separate Geometrien erhalten. Wenn Sie Zugriff auf PostGIS 2.2.0rc1 haben , können Sie Geometrien, die nahe beieinander liegen, GeometryCollectionmit ST_ClusterWithin zu einer einzigen zusammenführen :

SELECT unnest(ST_ClusterWithin(geom, 0.0001)) AS grp FROM tmpTable;

Wenn Sie Polygonsinnerhalb der GeometryCollectionaufgelöst werden möchten , können Sie ST_UnaryUnionauf jeden GeometryCollectionim Ergebnis laufen , wie:

SELECT ST_UnaryUnion(grp) FROM
(SELECT unnest(ST_ClusterWithin(geom, 0.0001)) AS grp FROM tmpTable) sq;

Das ist definitiv viel schneller, aber zwei Dinge: (1) Kann ich das ID-Feld in den Ergebnissen beibehalten? Es ist nicht wichtig, welches, aber ich muss das Ergebnis nehmen und andere Daten daraus abrufen. (2) Gibt es eine Möglichkeit, den ST_Buffer wieder hinzuzufügen?
Carol AndorMarten Liebster

1
(1) Nicht einfach, aber eine einfache Möglichkeit, Attribute zurückzugewinnen, besteht darin, die Ergebnispolygone räumlich mit einem inneren Punkt der Eingabepolygone zu verbinden. (2) Es wurde eine Erklärung für den Umgang mit Geometrien hinzugefügt, die nahe beieinander liegen, sich aber nicht berühren.
Dbaston

Vielen Dank für die Hilfe - ich habe derzeit keine Version 2.2, daher muss ich diese Version erneut aufrufen, wenn ich ein Upgrade auf diese Version vornehme. Derzeit ist der Ausschluss des Puffers kein Deal Breaker.
Carol AndorMarten Liebster

Ich stimme zu, das ist das einfachste. Ich frage mich, ob es eine Möglichkeit gibt, eine rekursive Abfrage durchzuführen, die berührende Geome findet, aber ich habe sie aufgegeben - postgresql.org/docs/current/static/queries-with.html
chrismarx

1
Unter gis.stackexchange.com/a/94228/18189 finden Sie unter @chrismarx einige Vorschläge zur rekursiven Lösung.
Dbaston
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.