PostGIS - วิธีการที่มีประสิทธิภาพ ST_Union รูปหลายเหลี่ยมที่ทับซ้อนกันทั้งหมดในตารางเดียว


13

เป้าหมายของฉันคือใช้ตารางเดียวและ st_union รูปหลายเหลี่ยมทั้งหมดที่แตะหรือใกล้กันเป็นรูปหลายเหลี่ยมเดียว

ฉันเป็นนักพัฒนา C # ที่เริ่มเรียนรู้เกี่ยวกับ PostGIS ด้วยการใช้รหัสด้านล่างนี้ฉันสามารถทำสิ่งนี้ได้ แต่ดูเหมือนว่าจะไม่มีประสิทธิภาพและมี PostGIS จำนวนมากที่ใหม่สำหรับฉัน

จากความพยายามครั้งแรกของฉัน (ยังอยู่ในความคิดเห็น) ฉันสามารถลดการวนซ้ำได้โดยใช้ array_agg กับ ST_UNION แทนที่จะรวมตัวกันทีละครั้ง

ฉันปิดท้ายด้วย 133 polys จากจุดเริ่มต้น 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();
                }

นี่คือตัวอย่างของชุดข้อมูลที่ฉันใช้:

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 - ไม่แน่ใจว่ามันจะมีประโยชน์หรือไม่
Carol 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

ฉันยอมรับว่านี่เป็นวิธีที่ง่ายที่สุด ฉันสงสัยว่าจะมีวิธีการทำแบบสอบถามแบบเรียกซ้ำที่พบ geoms สัมผัส แต่ฉันให้ขึ้นกับมัน - postgresql.org/docs/current/static/queries-with.html
chrismarx

1
@chrismarx ตรวจสอบgis.stackexchange.com/a/94228/18189สำหรับแนวคิดบางประการเกี่ยวกับโซลูชันแบบเรียกซ้ำ
dbaston
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.