如何最好地解决PostGIS中的非节点交叉点问题?


38

我正在使用一个PL/R函数,并PostGIS围绕一组点生成voronoi多边形。我正在使用的功能在这里定义。当我在特定数据集上使用此函数时,出现以下错误消息:

Error : ERROR:  R interpreter expression evaluation error
DETAIL:  Error in pg.spi.exec(sprintf("SELECT %3$s AS id,   
st_intersection('SRID='||st_srid(%2$s)||';%4$s'::text,'%5$s') 
AS polygon FROM %1$s WHERE st_intersects(%2$s::text,'SRID='||st_srid(%2$s)||';%4$s');",  
:error in SQL statement : Error performing intersection: TopologyException: found non-noded 
intersection between LINESTRING (571304 310990, 568465 264611) and LINESTRING (568465 
264611, 594406 286813) at 568465.05533706467 264610.82749605528
CONTEXT:  In R support function pg.spi.exec In PL/R function r_voronoi

通过检查错误消息的这一部分:

Error performing intersection: TopologyException: found non-noded intersection between
LINESTRING (571304 310990, 568465 264611) and LINESTRING (568465 264611, 594406 286813) 
at 568465.05533706467 264610.82749605528

上面列出的问题是这样的:

在此处输入图片说明

我最初以为该消息可能是由于存在相同的点而引起的,因此尝试使用该st_translate()函数来解决此问题,该函数的使用方式如下:

ST_Translate(geom, random()*20, random()*20) as geom 

这确实解决了问题,但是我担心的是,我现在正在沿x / y方向平移所有点,直到〜20m。我也不知道需要什么适当的翻译量。例如,在该数据集中通过反复试验,a 20m * random number可以,但是如何确定是否需要更大?

基于上图,我认为问题在于算法试图将点与多边形相交时,点与线相交。我不确定应该怎么做才能确保该点在多边形内,而不是与直线相交。该行发生错误:

"SELECT 
  %3$s AS id, 
  st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'') AS polygon 
FROM 
  %1$s 
WHERE 
  st_intersects(%2$s::text,''SRID=''||st_srid(%2$s)||'';%4$s'');"

我已经阅读了前面的问题,什么是“非节点交叉点”?以便更好地了解此问题,并希望就如何最好地解决该问题提供任何建议。


如果您输入的内容开头无效,请对它们运行ST_MakeValid()。如果它们是有效的,那么添加熵(如您所做的那样)是下一个可用的技巧,也许是目前的最后一个技巧。
保罗·拉姆齐

是的,我WHERE ST_IsValid(p.geom)最初用于过滤点。
djq 2013年

Answers:


30

以我的经验,这个问题几乎总是由以下原因引起的:

  1. 结合使用高精度的坐标(43.231499999999996)
  2. 几乎重合但不相同的行

ST_Buffer解决方案的“微调”方法使您远离#2,但是您可以采取任何解决这些根本原因的措施,例如将几何图形捕捉到1e-6网格,将使您的生活变得更轻松。缓冲的几何形状通常适合重叠区域之类的中间计算,但您要特别注意保留它们,因为它们会使长距离但并非十分重要的问题变得更糟。

PostgreSQL的异常处理功能使您可以编写包装函数来处理这些特殊情况,仅在需要时进行缓冲。这是一个例子ST_Intersection;我对使用了类似的功能ST_Difference。您需要确定在您的情况下是否可以接受空多边形的缓冲和潜在返回。

CREATE OR REPLACE FUNCTION safe_isect(geom_a geometry, geom_b geometry)
RETURNS geometry AS
$$
BEGIN
    RETURN ST_Intersection(geom_a, geom_b);
    EXCEPTION
        WHEN OTHERS THEN
            BEGIN
                RETURN ST_Intersection(ST_Buffer(geom_a, 0.0000001), ST_Buffer(geom_b, 0.0000001));
                EXCEPTION
                    WHEN OTHERS THEN
                        RETURN ST_GeomFromText('POLYGON EMPTY');
    END;
END
$$
LANGUAGE 'plpgsql' STABLE STRICT;

这种方法的另一个好处是您可以查明实​​际上导致问题的几何形状。只需RAISE NOTICEEXCEPTION块中添加一些语句以输出WKT或其他可以帮助您查找问题的语句。


这是一个聪明的主意。我经常发现交集问题是由联合,差异,缓冲区等的组合中出现的线串引起的,可以通过缓冲所有内容或转储所有内容并仅选择多边形/多义线形来解决此问题。这是一个有趣的方法。
约翰·鲍威尔

您提到了将几何捕捉到1e-6网格,但是我坐在这里想知道捕捉到2的幂是否更好。PostGIS(和GEOS)使用浮点数,因此捕捉到10的幂实际上可能不会截断坐标,因为数字可能没有有限长度的二进制表示形式。但是,如果您说2 ^ -16,我相信可以保证将任何小数部分截断为2个字节。还是我想错了?
jpmc26

12

经过大量的试验和错误,我最终意识到这non-noded intersection是自交点问题造成的。我发现建议使用的线程ST_buffer(geom, 0)可用于解决问题(尽管确实使它总体上变慢了很多)。然后,我尝试使用,ST_MakeValid()并在将其直接应用于任何其他功能之前直接应用于几何。这似乎可以很好地解决该问题。

ipoint <- pg.spi.exec(
        sprintf(
            "SELECT 
                    %3$s AS id, 
                    st_intersection(ST_MakeValid(''SRID=''||st_srid(%2$s)||'';%4$s''::text), ST_MakeValid(''%5$s'', 0)) AS polygon 
            FROM %1$s 
            WHERE 
                ST_Intersects(ST_MakeValid(%2$s::text),ST_MakeValid(''SRID=''||st_srid(%2$s)||'';%4$s''));",
            arg1,
            arg2,
            arg3,
            curpoly,
            buffer_set$ewkb[1]
        )
    )

我将其标记为答案,因为这似乎是解决我的问题的唯一方法。


11

我遇到了同样的问题(Postgres 9.1.4,PostGIS 2.1.1),对我唯一有效的方法是用很小的缓冲区包装几何。

SELECT ST_Intersection(
    (SELECT geom FROM table1), ST_Union(ST_Buffer(geom, 0.0000001))
) FROM table2

ST_MakeValid我没有工作,也没有的组合ST_NodeST_Dump。缓冲区似乎并没有导致性能降低,但是如果将其减小,我仍然会收到非节点交叉错误。

丑陋,但是行得通。

更新:

ST_Buffer策略似乎运作良好,但遇到一个问题,即在将几何体投射到地理区域时会产生错误。例如,如果一个点原先为-90.0,并且被0.0000001缓冲,则现在为-90.0000001,这是无效的地理位置。

这意味着即使ST_IsValid(geom)t,也ST_Area(geom::geography)返回NaN了许多功能。

为了避免非节点交叉点问题,在保持有效地理位置的同时,我最终使用了ST_SnapToGrid这样的方法

SELECT ST_Union(ST_MakeValid(ST_SnapToGrid(geom, 0.0001))) AS geom, common_id
    FROM table
    GROUP BY common_id;

6

在postgis中,ST_Node应该在相交处断开一系列直线,这应该可以解决非节点相交的问题。将其包装在ST_Dump中会生成虚线的复合数组。

稍微相关一点,这里有一个很棒的演示文稿《PostGIS:面向高级用户的提示》,清楚地概述了这类问题和解决方案。


这是一个很棒的演示文稿(感谢@PaulRamsey)。我应该如何使用ST_NodeST_Dump?我想我需要在函数的此部分附近使用它们,但不确定: st_intersection(''SRID=''||st_srid(%2$s)||'';%4$s''::text,''%5$s'')in
djq

嗯,我没有注意到这两条线具有相同的坐标,应该没问题。如果绘制这些坐标,则交点距交点约18cm。并不是真正的解决方案,只是一个观察。
WolfOdrade

st_node在这里如何使用尚不完全清楚-我可以在以前使用它st_intersection吗?
djq

1
该演示文稿不再可用。尝试ST_Clip(rast,多边形)时,我遇到了同样的问题
Jackie

1
@Jackie:我在答案中修复了指向演示文稿的链接:PostGIS:面向高级用户的提示
皮特

1

以我的经验,我non-noded intersection通过使用St_SnapToGrid函数解决了我的错误,该函数解决了多边形顶点坐标中具有高精度的问题。

SELECT dissolve.machine, dissolve.geom FROM (
        SELECT machine, (ST_Dump(ST_Union(ST_MakeValid(ST_SnapToGrid(geom,0.000001))))).geom 
        FROM cutover_automatique
        GROUP BY machine) as dissolve
WHERE ST_isvalid(dissolve.geom)='t' AND ST_GeometryType(dissolve.geom) = 'ST_Polygon';
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.