PostGIS - comment efficacement ST_Union tous les polygones qui se chevauchent dans une seule table


13

Mon objectif est de prendre une seule table et de st_union tous les polygones qui se touchent ou se rapprochent en polygones simples

Je suis un développeur C # qui commence à découvrir PostGIS. En utilisant le code ci-dessous, j'ai pu accomplir cela, mais cela semble inefficace, et il y a beaucoup à PostGIS qui est nouveau pour moi.

Depuis ma tentative initiale (toujours dans les commentaires), j'ai pu réduire les itérations en utilisant array_agg avec ST_UNION au lieu de ne réunir que des polys à la fois.

Je me retrouve avec 133 polys de mon origine 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();
                }

Voici un échantillon de l'ensemble de données que j'utilise:

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

Les données réelles sont-elles nécessaires dans votre question?
Paul

@Paul - je ne savais pas si cela serait utile ou non.
Carol AndorMarten Liebster

Réponses:


20

Le moyen le plus simple serait de parcourir ST_Unionl'ensemble du tableau:

SELECT ST_Union(geom) FROM tmpTable;

Cela vous en donnera un énorme MultiPolygon, ce qui n'est probablement pas ce que vous voulez. Vous pouvez obtenir les composants dissous individuels avec ST_Dump. Donc:

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

Cela vous donne un polygone distinct pour chaque ensemble d' entrées en contact , mais les groupes d'entrées qui ont été séparés par une courte distance resteront des géométries distinctes. Si vous avez accès à PostGIS 2.2.0rc1 , vous pouvez fusionner des géométries qui sont rapprochées en une seule à l' GeometryCollectionaide de ST_ClusterWithin :

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

Si vous voulez Polygonsdans le GeometryCollectionêtre dissous, vous pouvez exécuter ST_UnaryUnionsur chaque GeometryCollectiondans le résultat, comme:

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

C'est certainement beaucoup plus rapide, mais 2 choses: (1) Puis-je conserver le champ ID dans les résultats? Ce n'est pas important lequel, mais je dois prendre le résultat et en obtenir d'autres données. (2) Existe-t-il un moyen d'ajouter à nouveau le ST_Buffer?
Carol AndorMarten Liebster

1
(1) Pas facilement, mais un moyen simple de récupérer des attributs consiste à joindre spatialement vos polygones résultants à un point intérieur de vos polygones d'entrée. (2) Ajout d'une explication sur la gestion des géométries proches mais sans contact.
dbaston

Merci pour l'aide - je n'ai pas actuellement 2.2, donc je devrai revisiter ceci quand je passerai à cela. Pour l'instant, l'exclusion du tampon n'est pas un facteur de rupture.
Carol AndorMarten Liebster

Je suis d'accord que c'est le plus simple. Je me demande s'il y aurait un moyen de faire une requête récursive qui trouve des geoms touchants, mais j'ai abandonné - postgresql.org/docs/current/static/queries-with.html
chrismarx

1
@chrismarx, consultez gis.stackexchange.com/a/94228/18189 pour quelques idées sur la solution récursive.
dbaston
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.