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_UnaryUnion각 GeometryCollection결과에 같은 :

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.