合并所有相邻多边形


22

我想在宗地(多边形)层上进行邻接测试,并在它们符合特定条件(可能是大小)的情况下合并它们。根据下面的图片,我想合并多边形1、2、3和4,但合并5。

我有两个问题:

  1. ST_TOUCHES如果仅接触角而不是线段,则返回TRUE。我想我需要ST_RELATE检查共享的线段。
  2. 理想情况下,我想将所有相邻的多边形合并为一个,但是我不确定如何缩放到两个以上,例如在一轮中合并1,2,3和4(可能合并更多的实际数据)。

我现在拥有的结构基于上的自我联接ST_TOUCHES

在此处输入图片说明

玩具数据

CREATE TABLE testpoly AS 
SELECT 
1 AS id, ST_PolyFromText('POLYGON ((0 0, 10 0, 10 20, 00 20, 0 0 ))') AS geom UNION SELECT
2 AS id, ST_PolyFromText('POLYGON ((10 0, 20 0, 20 20, 10 20, 10 0 ))') AS geom UNION SELECT
3 AS id, ST_PolyFromText('POLYGON ((10 -20, 20 -20, 20 0, 10 0, 10 -20 ))') AS geom UNION SELECT
4 AS id, ST_PolyFromText('POLYGON ((20 -20, 30 -20, 30 0, 20 0, 20 -20 ))') AS geom  UNION SELECT 
5 AS id, ST_PolyFromText('POLYGON ((30 0, 40 0, 40 20, 30 20, 30 0 ))') AS geom ;

选拔

SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND t1.geom && t2.geom 
) 
l2

这是输出:

+-----+---------+-------------------------------------------------------------------------------+
| gid | adj_gid | geo_combo                                                                     |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 2       | POLYGON((10 0,0 0,0 20,10 20,20 20,20 0,10 0))                                |
+-----+---------+-------------------------------------------------------------------------------+
| 1   | 3       | MULTIPOLYGON(((10 0,0 0,0 20,10 20,10 0)),((10 0,20 0,20 -20,10 -20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 1       | POLYGON((10 20,20 20,20 0,10 0,0 0,0 20,10 20))                               |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 3       | POLYGON((10 0,10 20,20 20,20 0,20 -20,10 -20,10 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 2   | 4       | MULTIPOLYGON(((20 0,10 0,10 20,20 20,20 0)),((20 0,30 0,30 -20,20 -20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 1       | MULTIPOLYGON(((10 0,20 0,20 -20,10 -20,10 0)),((10 0,0 0,0 20,10 20,10 0)))   |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 2       | POLYGON((20 0,20 -20,10 -20,10 0,10 20,20 20,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 3   | 4       | POLYGON((20 -20,10 -20,10 0,20 0,30 0,30 -20,20 -20))                         |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 2       | MULTIPOLYGON(((20 0,30 0,30 -20,20 -20,20 0)),((20 0,10 0,10 20,20 20,20 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 3       | POLYGON((20 0,30 0,30 -20,20 -20,10 -20,10 0,20 0))                           |
+-----+---------+-------------------------------------------------------------------------------+
| 4   | 5       | MULTIPOLYGON(((30 0,30 -20,20 -20,20 0,30 0)),((30 0,30 20,40 20,40 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+
| 5   | 4       | MULTIPOLYGON(((30 0,30 20,40 20,40 0,30 0)),((30 0,30 -20,20 -20,20 0,30 0))) |
+-----+---------+-------------------------------------------------------------------------------+

请注意,多边形id = 3与id = 1共享一个点,因此将其作为肯定结果返回。如果我将WHERE子句更改为ST_Touches( t1.geom, t2.geom ) AND t1.geom && t2.geom AND ST_Relate(t1.geom, t2.geom ,'T*T***T**');我根本没有记录。

  1. 因此,首先,我如何指定ST_Relate以确保仅考虑共享线段的宗地。

  2. 然后,我如何将多边形1,2,3,4合并为一轮,从而折叠上述调用的结果,同时又认识到1到2的邻接与反向相同?

更新资料

如果将其添加到where子句中,显然我只会得到多边形而不是多多边形,因此出于我的目的清除了误报-角触摸将被忽略。

GeometryType(st_union(t1.geom,t2.geom)) != 'MULTIPOLYGON'

尽管这不是理想的选择(我宁愿使用拓扑检查ST_RELATE作为更通用的解决方案),但这是一种前进的道路。然后剩下去重复和合并这些问题。可能的话,如果我只能为接触的多边形生成一个序列,那么我可以对此进行合并。

更新二

这似乎适用于选择共享线(而不是角)的多边形,因此是比上述MULTIPOLYGON测试更通用的解决方案。我的where子句现在看起来像这样:

WHERE
              ST_Touches( t1.geom, t2.geom ) 
              AND t1.geom && t2.geom 

              -- 'overlap' relation
              AND ST_Relate(t1.geom, t2.geom)='FF2F11212') t2 

现在剩下的仍然是如何对不仅仅是一对多边形的合并进行合并,而是一次性地对符合条件的任意数进行合并。


2
我确定ST_Relate是正确的方法。我通过检查相交的长度是否大于零以排除单点相交来解决了类似的问题。骇客,但有效。
约翰·鲍威尔

如果有一种方法可以将连续的多边形组合成数组,则可以修改ST_IntersectionArray[function] [1]以与ST_Union [1]一起使用:gis.stackexchange.com/a/60295/36886
raphael

2
关于将连续的多边形分组在一起,您可以修改我在此处编写的自底向上聚类算法(gis.stackexchange.com/a/115715/36886),以测试邻接性而不是空间,然后在对结果cluster_id分组时使用ST_Union
raphael 2014年

3
还有ST_ClusterIntersectimg可能会满足您的需求。您需要Postgis 2.2
John Powell

Answers:


3

我不禁想到您的示例实际上是一个栅格,尽管您提到您希望基于“某些条件(可能是大小)”进行合并,但我还是想通过栅格转换对其进行介绍。

对于您的特定示例,这将起作用:

WITH rast AS (
  SELECT 
  ST_UNION(ST_AsRaster(geom,10, 20, '2BUI')) r
  FROM testpoly 
)
,p AS (
    SELECT (ST_DumpAsPolygons(r)).geom FROM rast
)
SELECT t.id,p.* 
FROM p
LEFT JOIN testpoly  t ON ST_Equals(p.geom, t.geom)

发生的事情是,由于多边形是完全对齐的像元,它们将很好地转换为栅格(10x20像元大小)。dumpaspolygons通过将所有相邻单元合并为一个单元,并与原始多边形进行比较,可以帮助您获得非合并的poly的id。

解释了这一点之后,我很好奇这将如何扩展以及您的数据集有多大:D


聪明的主意。不过,这只是一个玩具示例-我的实际数据是一个宗地图层,不会整齐地映射到栅格。
2016年

3

这是一个示例,该示例说明如何以程序样式在后台进行多次传递。

CREATE TABLE joined_testpoly AS SELECT array[id] ids, geom FROM testpoly; 

通过修改以下LIMIT 1选择的工作方式,您应该能够携带更多的列并应用附加的加入条件:

CREATE OR REPLACE FUNCTION reduce_joined_testpoly()
RETURNS void
AS $$
DECLARE
  joined_row joined_testpoly%ROWTYPE;
BEGIN
  LOOP
     SELECT array_cat(a.ids, b.ids), st_union(a.geom, b.geom)
         INTO joined_row 
     FROM joined_testpoly a INNER JOIN joined_testpoly b
           on a.ids != b.ids
              and ST_Touches(a.geom, b.geom) and a.geom && b.geom 
              and ST_Relate(a.geom, b.geom)='FF2F11212'
         LIMIT 1;
     IF NOT FOUND THEN
           EXIT;
     END IF;
     INSERT INTO joined_testpoly VALUES (joined_row.ids, joined_row.geom);
     DELETE FROM joined_testpoly
         WHERE joined_testpoly.ids <@ joined_row.ids 
           AND joined_testpoly.ids != joined_row.ids;
  END LOOP;
  RETURN;
END;
$$ LANGUAGE plpgsql;

运行东西:

SELECT reduce_joined_testpoly();

正确的联合,没有多面体:

SELECT ids, st_geometrytype(geom), st_area(geom), st_numgeometries(geom) 
FROM joined_testpoly;
    ids    | st_geometrytype | st_area | st_numgeometries 
-----------+-----------------+---------+------------------
 {5}       | ST_Polygon      |     200 |                1
 {1,2,3,4} | ST_Polygon      |     800 |                1

2

这是另一种(不起作用的)参考策略(我无法排除单个接触点的情况)。它应该比我的其他答案更快,因为它只需要“一次通过”。

SELECT st_numgeometries(g), (SELECT st_union(x.geom) FROM st_dump(g) x GROUP BY g)
FROM (
    SELECT unnest(st_clusterintersecting(geom)) g, id < 100 as other_arbitrary_grouping 
    FROM testpoly
    GROUP BY other_arbitrary_grouping) c;

(如果有人可以在自己的组中获得id = 5几何,则可以随意修改并发布另一个答案)

要获取ID等的列表,您必须使用st_contains它在testpoly表上重新加入,如以下答案中所述:https ://stackoverflow.com/a/37486732/6691 但我无法使它正常工作出于某种原因多边形。


2

这是使用原始查询稍作调整后的快速解决方案:

with gr as (SELECT 
    gid, adj_gid,
    st_AStext(st_union(l2.g1,l2.g2)) AS geo_combo
from (
    --level 2
    SELECT
      t1.id AS gid,
      t1.geom AS g1,
      t2.id AS adj_gid,
      t2.geom AS g2
     from
      testpoly  t1,
      testpoly  t2
     where
      ST_Touches( t1.geom, t2.geom ) 
      AND ST_Relate(t1.geom,t2.geom, '****1****')
      AND t1.geom && t2.geom 
) 
l2) select ST_AsText(st_union(gr.geo_combo)) from gr;

参考:https : //postgis.net/docs/using_postgis_dbmanagement.html#DE-9IM

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.