PostGIS - tek bir tabloda çakışan tüm çokgenlerin ST_Union nasıl verimli bir şekilde


13

Amacım tek bir tablo almak ve birbirine dokunan veya birbirine yakın olan tüm çokgenleri tek çokgenler halinde st_union almak

PostGIS hakkında bilgi edinmeye başlayan bir C # geliştiricisiyim. Aşağıdaki kodu kullanarak bunu başardım, ancak verimsiz görünüyor ve PostGIS'te benim için yeni olan çok şey var.

İlk denememden (hala yorumlarda), bir seferde sadece polys birleştirme yerine ST_UNION ile array_agg kullanarak yinelemeleri azaltabildim.

Origim 173'ten 133 polise sarılıyorum

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

İşte kullandığım veri kümesinin örneği:

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

Sorunuzda gerçek verilerin kendisi gerekli mi?
Paul

@Paul - yararlı olup olmayacağından emin değildi.
Carol AndorMarten Liebster

Yanıtlar:


20

En basit yol ST_Uniontüm tabloya ulaşmak olacaktır :

SELECT ST_Union(geom) FROM tmpTable;

Bu size büyük bir tane verecektir MultiPolygon, ki bu muhtemelen istediğiniz şey değildir. Tek tek çözünmüş bileşenleri ile alabilirsiniz ST_Dump. Yani:

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

Bu, her dokunma girişi seti için ayrı bir çokgen verir , ancak kısa bir mesafe ile ayrılmış giriş grupları ayrı geometriler olarak kalacaktır. PostGIS 2.2.0rc1'e erişiminiz varsa, ST_ClusterWithin'iGeometryCollection kullanarak birbirine yakın olan geometrileri tek bir dosyada birleştirebilirsiniz :

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

İsterseniz Polygonsiçinde GeometryCollectionçözünmüş olan, Çalıştırabileceğiniz ST_UnaryUnionher biri üzerinde GeometryCollection, sonuç şöyle:

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

Bu kesinlikle çok daha hızlı, ancak 2 şey: (1) Sonuçlarda kimlik alanını koruyabilir miyim? Hangisi önemli değil, ama sonucu almam ve ondan başka veriler almam gerekiyor. (2) ST_Buffer'ı tekrar eklemenin bir yolu var mı?
Carol AndorMarten Liebster

1
(1) Kolay değil, ancak öznitelikleri geri almanın basit bir yolu, sonuç çokgenlerinizi, giriş çokgenlerinizin bir iç noktasına karşı mekansal olarak birleştirmektir. (2) Yakın ancak dokunmayan geometrileri işlemek için bazı açıklamalar eklendi.
dbaston

Yardım için teşekkürler - Şu anda 2.2'im yok, bu yüzden yükselttiğimde bunu tekrar gözden geçirmem gerekecek. Şimdilik tamponun hariç tutulması bir anlaşma kırıcı değildir.
Carol AndorMarten Liebster

Bunun en basit olduğunu kabul ediyorum. Dokunaklı geoms bulan özyinelemeli bir sorgu yapmanın bir yolu olup olmadığını merak ediyorum, ama bundan vazgeçtim- postgresql.org/docs/current/static/queries-with.html
chrismarx

1
@chrismarx, özyinelemeli çözüm hakkında bazı fikirler için gis.stackexchange.com/a/94228/18189 adresine bakın.
dbaston
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.