PostGIS-단일 테이블에서 모든 중첩 다각형을 효율적으로 ST_Union하는 방법


13

내 목표는 단일 테이블을 가져 와서 서로 접촉하거나 가까이있는 모든 다각형을 단일 다각형으로 만듭니다.

PostGIS에 대해 배우기 시작한 C # 개발자입니다. 아래 코드를 사용하면이 작업을 수행 할 수 있었지만 비효율적이며 PostGIS에는 새로운 것이 많이 있습니다.

초기 시도 (여전히 주석으로 작성)에서 한 번에 폴리만을 결합하는 대신 ST_UNION과 함께 array_agg를 사용하여 반복을 줄일 수있었습니다.

나는 orig 173에서 133 개의 폴리를 감습니다.

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

사용중인 데이터 세트의 샘플은 다음과 같습니다.

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

실제 데이터 자체가 귀하의 질문에 필요합니까?
Paul

@Paul-도움이 될지 확실하지 않았습니다.
캐롤 AndorMarten Liebster

답변:


20

가장 간단한 방법은 ST_Union전체 테이블에 대한 것입니다.

SELECT ST_Union(geom) FROM tmpTable;

이것은 당신에게 하나의 거대한 것을 줄 것이며 MultiPolygon, 아마도 당신이 원하는 것이 아닐 것입니다. 를 사용하여 개별 용존 성분을 얻을 수 있습니다 ST_Dump. 그래서:

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

이를 통해 각 터치 입력 세트에 대해 별도의 다각형이 제공 되지만 짧은 거리로 분리 된 입력 그룹은 별도의 형상으로 유지됩니다. 액세스 권한이있는 경우 PostGIS와의 2.2.0rc1을 , 당신은 하나에 함께 가까운 형상 병합 할 수 있습니다 GeometryCollection사용 ST_ClusterWithin를 :

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

당신이 원하는 경우 Polygons내에서 GeometryCollection용해 될, 당신은 실행할 수있는 ST_UnaryUnionGeometryCollection결과에 같은 :

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

확실히 빠르지 만 2 가지 : (1) 결과에서 ID 필드를 보존 할 수 있습니까? 어느 것이 중요하지는 않지만 결과를 가져 와서 다른 데이터를 가져와야합니다. (2) ST_Buffer를 다시 추가하는 방법이 있습니까?
Carol AndorMarten Liebster

1
(1) 쉽지는 않지만 속성을 다시 얻는 간단한 방법은 입력 다각형의 내부 점에 대해 결과 다각형을 공간적으로 결합하는 것입니다. (2) 가까이 있지만 손대지 않는 형상 처리에 대한 설명이 추가되었습니다.
dbaston

도움 주셔서 감사합니다-현재 2.2가 없으므로 업그레이드 할 때 다시 방문해야합니다. 현재로서는 버퍼 제외는 거래 차단기가 아닙니다.
Carol AndorMarten Liebster

이것이 가장 간단하다는 데 동의합니다. 터치하는
지오메트리

1
@ chrismarx, 재귀 솔루션에 대한 아이디어는 gis.stackexchange.com/a/94228/18189 를 확인하십시오 .
dbaston
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.