如何递归地遍历父多边形相交以获取没有重叠的最小(子)多边形?


11

我在问题上挣扎了几天,意识到当主题是PostGIS(v2.5)中的交叉点时,很多人也陷入困境。因此,我决定提出一个更详细,更通用的常见问题。

我有下表:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(MultiPolygon, 4326),
    att_category character varying(15),
    att_value integer
);
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('MULTIPOLYGON (((0 6, 0 12, 8 9, 0 6)))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('MULTIPOLYGON (((5 0, 5 12, 9 12, 9 0, 5 0)))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('MULTIPOLYGON (((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4)))'::geometry,4326) , 'cat2', 5 );

看起来像这样:

开始

我想基于父多边形的交集获得所有子多边形。对于结果,可以预期:

  • 子多边形之间没有重叠。
  • 包含其父多边形值之和的列,
  • 包含一个类别的父多边形数量的列
  • 包含其他类别计数的列
  • 根据以下规则,包含子多边形类别的列:-如果所有父多边形都来自一个类别,则子多边形也具有该类别。否则,子多边形的类别是第三类别。

所以看起来像这样:

输出

因此,在结束时,产生的输出表(在这个例子中)将具有7行(所有7,非重叠,子多边形),包含的列categorysum_valuect_overlap_cat1ct_overlap_cat2

我开始的以下代码为我提供了各个交集,将一个父级与另一个父级进行比较。

SELECT
(ST_Dump(
    ST_SymDifference(a.geom, b.geom) 
)).geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom)
UNION ALL
SELECT
ST_Intersection(a.geom, b.geom) as geom
FROM tbl_foo a, tbl_foo b
WHERE a.ID < b.ID AND ST_INTERSECTS(a.geom, b.geom);

我如何递归遍历上述代码的结果,即与重叠多边形的数量无关,我总是得到其“最小”(子)多边形(图2)?

Answers:


8

尝试这个:

从此链接下载PostGIS插件:https : //github.com/pedrogit/postgisaddons

通过运行postgis_addons.sql文件进行安装以获取ST_SplitAgg()函数。

通过运行postgis_addons_test.sql文件进行测试。

这是您的查询:

WITH  result_table AS (
    WITH  parts AS (
      SELECT a.att_value val,
             CASE WHEN a.att_category = 'cat1' THEN 1 ELSE 0 END cat1,
             CASE WHEN a.att_category = 'cat2' THEN 1 ELSE 0 END cat2,
             unnest(ST_SplitAgg(a.geom, b.geom, 0.00001)) geom
      FROM tbl_foo a,
           tbl_foo b
      WHERE ST_Equals(a.geom, b.geom) OR
            ST_Contains(a.geom, b.geom) OR
            ST_Contains(b.geom, a.geom) OR
            ST_Overlaps(a.geom, b.geom)
      GROUP BY a.id, a.att_category , ST_AsEWKB(a.geom), val
    )
    SELECT CASE WHEN sum(cat2) = 0 THEN 'cat1'
                WHEN sum(cat1) = 0 THEN 'cat2'
                ELSE 'cat3'
           END category, 
           sum(val*1.0) sum_value, 
           sum(cat1) ct_overlap_cat1, 
           sum(cat2) ct_overlap_cat2, 
           ST_Union(geom) geom
    FROM parts
    GROUP BY ST_Area(geom)
)
SELECT category, sum_value, ct_overlap_cat1, ct_overlap_cat2,
(ST_Dump(result_table.geom)).geom as geom
FROM result_table

我之前看过您的插件git repo。Inspirstionsl的东西。
约翰·鲍威尔

哇,很棒的解决方案。您也为创建此插件做了非常了不起的工作。在单击以授予此答案之前,我只想确定一件事困扰我。运行您提供的代码,“多边形5”(问题的第二个数字)似乎无法识别与另一个多边形(“ ct_overlap_cat2 = 1”;“ ct_overlap_cat2 = 0”)的重叠。因此,此多边形最终分类为“ cat1”和“ sum = 2”,而不是“ cat3”和“ sum = 7”。我在调试这个小问题时遇到了一些困难。你可以帮帮我吗?
Matt_Geo

1
这种解决方案的唯一问题是case语句是硬编码的。原则上,它应该可以处理任意数量的类别。
约翰·鲍威尔

@Matt_Geo我在结果表中得到6个多边形。三角形多边形被一分为三。如您所愿,一个为sum = 2,一个为sum = 7,另一个为sum = 8。
皮埃尔·拉辛

1
如果用ST_Area(geom)替换ST_Centroid(geom)怎么办?
皮埃尔·拉辛

1

我想如果您使用多边形几何类型而不是MultiPolygon,一切都会落入原位:

DROP TABLE IF EXISTS tbl_foo;
CREATE TABLE tbl_foo (
    id bigint NOT NULL,
    geom public.geometry(Polygon, 4326),
    att_category character varying(15),
    att_value integer
);

INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (1, ST_SetSRID('POLYGON ((0 6, 0 12, 8 9, 0 6))'::geometry,4326) , 'cat1', 2 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (2, ST_SetSRID('POLYGON ((5 0, 5 12, 9 12, 9 0, 5 0))'::geometry,4326), 'cat1', 1 );
INSERT INTO tbl_foo (id, geom, att_category, att_value) VALUES 
    (3, ST_SetSRID('POLYGON ((4 4, 3 8, 4 12, 7 14,10 12, 11 8, 10 4, 4 4))'::geometry,4326) , 'cat2', 5 );

结果是9个条目,它们对应于您给出的示例中的不同交集选项。

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.