23. 有效性¶
在 90% 的情况下,对于“为什么我的查询会返回‘TopologyException’错误”这个问题的答案是“一个或多个输入无效”。这引出了一个问题:无效意味着什么,我们为什么要关心它?
23.1. 什么是有效性¶
有效性对于多边形最为重要,多边形定义了有界区域,需要大量的结构。线非常简单,不能无效,点也不能无效。
多边形有效性的一些规则感觉很明显,而另一些则感觉很随意(事实上,它们确实是随意的)。
多边形环必须闭合。
定义孔洞的环应该位于定义外部边界的环的内部。
环不能自相交(它们既不能接触也不能交叉)。
环不能接触其他环,除非在一点上。
多边形集合的元素不能互相接触。
最后三条规则属于随意类别。还有其他方法可以定义同样自洽的多边形,但上面的规则是 PostGIS 符合的 OGC SFSQL 标准所使用的规则。
这些规则之所以重要,是因为几何计算算法依赖于输入的一致结构。可以构建没有结构假设的算法,但这些例程往往非常慢,因为任何无结构例程的第一步都是分析输入并在其中构建结构。
以下是一个结构为何重要的示例。这个多边形无效
POLYGON((0 0, 0 1, 2 1, 2 2, 1 2, 1 0, 0 0));
您可以在此图中更清楚地看到无效性
外环实际上是一个“8”字形,中间有一个自相交点。请注意,图形例程成功地渲染了多边形填充,因此在视觉上它看起来像一个“区域”:两个单位正方形,因此总面积为两个面积单位。
让我们看看数据库认为我们多边形的面积是多少。
SELECT ST_Area(ST_GeometryFromText(
'POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))'
));
st_area
---------
0
这里发生了什么?计算面积的算法假设环不自我相交。一个表现良好的环将始终具有边界(内部)位于边界线一侧的面积(哪一侧无关紧要,只要它位于一侧)。但是,在我们(行为不佳的)“8”字形中,边界区域在一条叶瓣的右侧,而在另一条叶瓣的左侧。这会导致为每个叶瓣计算的面积抵消(一个结果为 1,另一个结果为 -1),因此出现“零面积”结果。
23.2. 检测有效性¶
在前面的示例中,我们有一个我们知道无效的多边形。我们如何在一个包含数百万个几何图形的表中检测无效性?使用ST_IsValid(geometry)函数。针对我们的“8”字形使用它,我们可以快速得到答案。
SELECT ST_IsValid(ST_GeometryFromText(
'POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))'
));
f
现在我们知道该要素无效,但我们不知道原因。我们可以使用ST_IsValidReason(geometry)函数来找出无效性的来源。
SELECT ST_IsValidReason(ST_GeometryFromText(
'POLYGON((0 0, 0 1, 1 1, 2 1, 2 2, 1 2, 1 1, 1 0, 0 0))'
));
Self-intersection[1 1]
请注意,除了原因(自相交)之外,还返回了无效性的位置(坐标 (1 1))。
我们也可以使用ST_IsValid(geometry)函数来测试我们的表。
-- Find all the invalid polygons and what their problem is
SELECT name, boroname, ST_IsValidReason(geom)
FROM nyc_neighborhoods
WHERE NOT ST_IsValid(geom);
name | boroname | st_isvalidreason
-------------------------+---------------+-----------------------------------------
Howard Beach | Queens | Self-intersection[597264.08 4499924.54]
Corona | Queens | Self-intersection[595483.05 4513817.95]
Steinway | Queens | Self-intersection[593545.57 4514735.20]
Red Hook | Brooklyn | Self-intersection[584306.82 4502360.51]
23.3. 修复无效性¶
修复无效性涉及将多边形分解为最简单的结构(环),确保环遵循有效性规则,然后构建遵循环封闭规则的新多边形。结果通常是直观的,但在输入极度不规范的情况下,有效输出可能不符合您对它们外观的直觉。最新版本的 PostGIS 包含不同的几何修复算法:仔细阅读手册页并选择您最喜欢的算法。
例如,这里有一个经典的无效性——“香蕉多边形”——一个封闭区域但弯曲到自身并接触的单个环,留下一个实际上不是洞的“洞”。
POLYGON((0 0, 2 0, 1 1, 2 2, 3 1, 2 0, 4 0, 4 4, 0 4, 0 0))
在多边形上运行ST_MakeValid将返回一个有效的OGC多边形,它由一个外环和一个内环组成,它们在一个点处接触。
SELECT ST_AsText(
ST_MakeValid(
ST_GeometryFromText('POLYGON((0 0, 2 0, 1 1, 2 2, 3 1, 2 0, 4 0, 4 4, 0 4, 0 0))')
)
);
POLYGON((0 0,0 4,4 4,4 0,2 0,0 0),(2 0,3 1,2 2,1 1,2 0))
注意
“香蕉多边形”(或“倒置外壳”)是一个案例,其中OGC的有效几何拓扑模型与 ESRI 内部使用的模型不同。ESRI 模型认为相接的环无效,并更倾向于这种形状的香蕉形式。OGC 模型则相反。两者都没有“正确”,它们只是对同一情况的不同建模方式。
23.4. 批量有效性修复¶
以下是一个 SQL 示例,用于在将修复后的版本添加到表中时标记无效几何以供审查。
-- Column for old invalid form
ALTER TABLE nyc_neighborhoods
ADD COLUMN geom_invalid geometry
DEFAULT NULL;
-- Fix invalid and save the original
UPDATE nyc_neighborhoods
SET geom = ST_MakeValid(geom),
geom_invalid = geom
WHERE NOT ST_IsValid(geom);
-- Review the invalid cases
SELECT geom, ST_IsValidReason(geom_invalid)
FROM nyc_neighborhoods
WHERE geom_invalid IS NOT NULL;
OpenJump (http://openjump.org) 是一个用于直观修复无效几何的良好工具,它在 **工具->质量保证->验证所选图层** 下包含一个验证例程。
23.5. 函数列表¶
ST_IsValid(geometry A): 返回一个布尔值,指示几何是否有效。
ST_IsValidReason(geometry A): 返回一个文本字符串,其中包含无效的原因和无效的坐标。
ST_MakeValid(geometry A): 返回一个重新构建以符合有效性规则的几何。