如何创建线以可视化PostGIS中多边形要素之间的差异?


15

我有一个polygon_b带有某些面要素的PostGIS表。还有一个表格polygon_a,其中包含与多边形相同的多边形,polygon_b但变化较小。现在,我想创建线条以可视化多边形要素之间的差异。

在此处输入图片说明 在此处输入图片说明 在此处输入图片说明

我想这ST_ExteriorRingST_Difference将做的工作,但在WHERE子句似乎是相当棘手。

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, yourSRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    -- ?
    ) AS g;

谁能帮我?

编辑1

正如“倾斜”所发布的,我已经尝试过,ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom)但结果与预期不符。

CREATE VIEW line_difference AS SELECT
row_number() over() AS gid,
g.geom::geometry(LineString, your_SRID) AS geom
FROM 
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(ST_ExteriorRing(polygon_a.geom), ST_ExteriorRing(polygon_b.geom))))).geom AS geom
    FROM polygon_a, polygon_b
    WHERE 
    ST_Overlaps(polygon_a.geom, polygon_b.geom) AND NOT ST_Touches(polygon_a.geom, polygon_b.geom))
     AS g;

在此处输入图片说明

编辑2

workupload.com/file/J0WBvRBb(示例数据集)


我曾尝试在使用ST_Difference之前将多边形转换为多线,但结果仍然很奇怪。

CREATE VIEW multiline_a AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_a.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_a;

CREATE VIEW multiline_b AS SELECT
row_number() over() as gid,
ST_Union(ST_ExteriorRIng(polygon_b.geom))::geometry(multilinestring, 4326) AS geom
FROM
polygon_b;

CREATE VIEW line_difference AS SELECT
row_number() over() as gid,
g.geom
FROM
    (SELECT
    (ST_Dump(COALESCE(ST_Difference(multiline_a.geom, multiline_b.geom)))).geom::geometry(linestring, 4326) AS geom
    FROM
    multiline_a, multiline_b)
As g;

在此处输入图片说明


看起来更像是一个拓扑问题。您要确定另一层未覆盖的段。我在PostGIS拓扑方面工作不多,无法为您提供直接答案,但是我建议您对此进行更多研究。
托马斯

有趣的是,您有示例数据集可供下载吗?
huckfinn

Answers:


10

这是一些新技巧,使用:

  • EXCEPT从两个表中删除相同的几何图形,因此我们只能关注每个表(A_onlyB_only)唯一的几何图形。
  • ST_Snap 以获得叠加层运算符的精确点头。
  • 使用ST_SymDifference叠加运算符可以找到两个几何集之间的对称差异,以显示差异。更新ST_Difference对于此示例显示相同的结果。您可以尝试使用任一功能来查看它们得到了什么。

这应该达到您的期望:

-- CREATE OR REPLACE VIEW polygon_SymDifference AS
SELECT row_number() OVER () rn, *
FROM (
  SELECT (ST_Dump(ST_SymDifference(ST_Snap(A, B, tol), ST_Snap(B, A, tol)))).*
  FROM (
    SELECT ST_Union(DISTINCT A_only.geom) A, ST_Union(DISTINCT B_only.geom) B, 1e-5 tol
    FROM (
      SELECT ST_Boundary(geom) geom FROM polygon_a
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b
    ) A_only,
    (
      SELECT ST_Boundary(geom) geom FROM polygon_b
      EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_a
    ) B_only
  ) s
) s;

 rn |                                        geom
----+-------------------------------------------------------------------------------------
  1 | LINESTRING(206.234028204842 -92.0360704110685,219.846021625456 -92.5340701703592)
  2 | LINESTRING(18.556700448873 -36.4496098325257,44.44438533894 -40.5104231486146)
  3 | LINESTRING(-131.974995802602 -38.6145334122719,-114.067738329597 -39.0215165366584)
(3 rows)

三行


为了进一步解开这个答案,第一步是ST_Boundary获取每个多边形的边界,而不仅仅是外部。例如,如果有孔,则将通过边界追踪。

EXCEPT子句用于删除A的几何(属于B的部分)和B的行(作为A的一部分)。这减少了仅属于A且仅属于B的部分的行数。例如,要获取A_only:

SELECT ST_Boundary(geom) geom FROM polygon_a
EXCEPT SELECT ST_Boundary(geom) geom FROM polygon_b

这是A_only的6行和B_only的3行: A_only 仅B_

下一个, ST_Union(DISTINCT A_only.geom)用于将线条合并为单个几何图形,通常为MultiLineString。

ST_Snap用于将节点从一种几何形状捕捉到另一种几何形状。例如,ST_Snap(A, B, tol)将采用A几何,并从B几何中添加更多节点,如果它们在tol距离之内,则将其移至B几何。可能有几种使用这些功能的方法,但是其思想是从每个几何中获取彼此精确的坐标。因此,捕捉后的两个几何如下所示:

抢购 B响了

为了显示差异,您可以选择使用ST_SymDifferenceST_Difference。在此示例中,它们都显示相同的结果。


好答案。我想知道您用来可视化中介查询结果的方式。它并没有立即看起来像qgis,我也许是渲染速度更快的东西?
RoperMaps

1
我使用JTS Testbuilder来查看和处理几何。它是GEOS和Shapely的相关几何引擎,但具有基于Java的GUI。
Mike T

有没有办法忽略/跳过“ LINESTRING之间的非节点相交”问题?所有输入的多边形似乎都很好(使用QGIS几何检查器检查)。
eclipsed_by_the_moon,

1
解决方法是使用“ ST_Boundary(ST_SnapToGrid(geom,0.001))”代替“ ST_Boundary(geom)”。
eclipsed_by_the_moon '18年

6

我认为这有点棘手,因为两个多边形的节点集不同(绿色多边形A,红色多边形B的不同部分)。比较两个多边形的线段,可以得出多边形B的哪些线段将被修改的线索。

节点多边形A

聚A

“不同”分段多边形B的节点

段差

不幸的是,这仅显示了段结构的差异,但是我希望这是一个起点,并且它的工作方式如下:

在完成下载和解压缩之后,我已经在Debian Linux Jessie下使用PostgrSQL 9.46,PostGIS 2.1和命令导入了数据集。

$ createdb gis-se
$ psql gis-se < /usr/share/postgis-2.1/postgis.sql
$ psql gis-se < /usr/share/postgis-2.1/spatial_ref_sys.sql
$ shp2pgsql -S polygon_a | psql gis-se
$ shp2pgsql -S polygon_b | psql gis-se

假设多边形A的段不在B中,反之亦然,我尝试建立两个多边形集的段之间的差异,而忽略每个组(A或B)中多边形的段成员。由于教学上的原因,我在几种视图中阐述了SQL的内容。

对应于此GIS-SE帖子,我将两个多边形分解为段表segments_asegments_b

-- Segments of the polygon A
CREATE VIEW segments_a AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_a
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

段表多边形A:

SELECT 
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_a 
LIMIT 3;
                    sp                     |                 ep
-------------------------------------------+--------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.03428773875

将相同的步骤应用于多边形B。

-- Segments of the polygon B
CREATE VIEW segments_b AS SELECT sp, ep
FROM
   -- extract the endpoints for every 2-point line segment for each linestring
   (SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
    FROM
    -- extract the individual linestrings
     (SELECT (ST_Dump(ST_Boundary(geom))).geom
      FROM polygon_b
     ) AS linestrings
    -- be sure that nothing is scrambled
    ORDER BY sp, ep
) AS segments;

段表多边形B

SELECT
  st_astext(sp) AS sp, 
  st_astext(ep) AS ep 
FROM segments_b 
LIMIT 3;
                    sp                     |                    ep
-------------------------------------------+-------------------------------------------
POINT(-292.268907321861 95.0342877387557)  | POINT(-287.118411917425 99.4165242769195)
POINT(-287.118411917425 99.4165242769195)  | POINT(-264.62129248575 93.2470010145007)
POINT(-277.459563916327 -44.5629543976138) | POINT(-292.268907321861 95.0342877387557)
...                        

我可以建立一个名为的差异表视图segments_diff_{a,b}。差异由段集A和B中不存在已排序的起点或终点给出。

CREATE VIEW segments_diff_a AS
SELECT st_makeline(b.sp, b.ep) as geom
FROM segments_b as b
LEFT JOIN segments_a as a ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon A
WHERE a.sp IS NULL;

段差b

和补充的东西:

CREATE VIEW segments_diff_b AS
SELECT st_makeline(a.sp, a.ep) as geom
FROM segments_a as a
LEFT JOIN segments_b as b ON (a.sp=b.sp and a.ep = b.ep)
-- filter segments without corresponding stuff in polygon B
WHERE b.sp IS NULL;

段差异

结论:为了获得用红色箭头标记的小片段的正确结果,两个多边形必须具有相同的结点结构,并且需要在结点级别上有一个相交步骤(在B中插入多边形A的顶点)。交叉口可以通过以下方式完成:

CREATE VIEW segments_bi AS 
SELECT distinct sp, ep
FROM (
 SELECT
      ST_PointN(geom, generate_series(1, ST_NPoints(geom)-1)) as sp,
      ST_PointN(geom, generate_series(2, ST_NPoints(geom)  )) as ep
 FROM (
   SELECT st_difference(b.seg, a.seg) as geom FROM 
      segments_diff_a as a, segments_diff_b as b 
      WHERE st_intersects(a.seg, b.seg)
    ) as cut
  ) as segments
  WHERE sp IS NOT NULL AND ep IS NOT NULL
ORDER BY sp, ep;

但是结果很奇怪...

切割版


感谢您的努力。好吧,结果很奇怪。我只是想知道ST_HausdorffDistance()是否有助于回答这个问题:gis.stackexchange.com/questions/180593/…–
Lunar Sea,

嗯,st_haudorffdistance为您提供了相似度,而不是所需的线段(红色箭头指向)。
huckfinn '16

只是一个想法,ST_HausdorffDistance可用于比较两个表的几何形状。如果多边形在空间上不相等,我就必须创建线。我只是不知道该怎么做。
农历海


1

看示例,更改意味着新表中已更改的要素将始终与旧表中的要素重叠。因此,您将完成

ST_Overlaps(geoma,geomb)和!ST_Touches(geoma,geomb)

否定触摸是因为如果要素的边界共享相同的顶点位置,则要素也会重叠。

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.