空间数据质量通常涉及几何之间的更高阶关系。正如强大的非空间模型将强制执行外键关系一样,空间约束可用于强制执行空间关系。
触发器约束
地块"地籍" 的形式,反映土地合法划分的空间层。地籍的核心业务规则之一是,一块土地不能同时位于两个地籍中。
两种不一致违反了上述原则:

一个宗地与另一个宗地部分重叠,例如右下角的红色宗地。 一个宗地完全包含在另一个宗地中,如左侧的红色宗地。
创建一个检查规则(插入和更新时候使用):
CREATE OR REPLACE FUNCTION overlapping_parcel_trigger()
RETURNS trigger AS
$$
DECLARE
c bigint;
BEGIN
SELECT Count(*)
INTO c
FROM parcels
WHERE ST_Intersects(parcels.geom, NEW.geom)
AND parcels.pk != NEW.pk;
AND ST_Relate(parcels.geom, NEW.geom, '2********')
IF c > 0 THEN
RAISE EXCEPTION 'parcel % shares
area with another parcel', NEW.pk;
END IF;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
此触发器内的查询将查找共享区域的宗地。
如果宗地共享区域大于零,则存在问题,因此我们引发异常以中止交易。
注意三个 WHERE
子句:
首先 ST_Intersects 快速查找可能共享区域的地块。 然后进行主键检查,以排除包裹与其自身重复的情况。 最后,调用 ST_Relate 函数,以检查几何之间的关系是否包括跨区域问题。
我们无法对 ST_Overlaps 的简单调用来测试跨区域问题,因为 “重叠”仅测试多边形重叠但不包含的情况 ( 左侧的多边形)。
尺寸扩展的9交叉模型(DE9IM)
ST_Relate 计算两个输入几何的“尺寸扩展的9交叉模型”(DE9IM)。
DE9IM 精确地描述了两个几何之间的关系。
每个几何都有三个组成部分:
它具有几何图形所包含的平面的“内部”部分。 具有 外部 几何所不包含的部分。 它具有将内部与外部分隔的平面的“边界”部分。
通过填写指示几何对象如何相互作用的矩阵,可以得出两个几何的DE9IM代码。例如,对于两个重叠的多边形:

每个图形交互根据其交集的维数进行编码。内部有一个2维(区域)交叉点,因此I/I单元获得一个“2”。边界和内部具有一维(线性)交点,因此B/I单元为“1”,依此类推。
通过附加行将最终矩阵转换为相关字符串,因此生成DE9IM的SQL返回单个字符串值:
SELECT ST_Relate(
'POLYGON ((0 100, 100 100, 100 0, 0 0, 0 100))',
'POLYGON ((200 0, 100 0, 91 55, 100 100, 200 100, 200 0))'
);
------------------
st_relate
-----------
212101212
覆盖多边形的情况矩阵如下所示。请注意,在多边形没有交集的情况下,单元格将填充“ F”。

两种矩阵都完全不同,但是它们有一个共同的关键点:在两种情况下,内部的交集都是一个区域。因此,在I/I单元中具有两个 “2” 的两个多边形的任何相关矩阵都表示该多边形具有某些重叠的区域,因此它们不是合法的地块。
幸运的是,有一种 ST_Relate 形式,它与模式相关,类似正则表达式,因此我们不需要进行字符串详细的解读或者解析。
ST_Relate(parcels.geom, NEW.geom, '2********')
ST_Relate的三参数形式针对字符串参数测试两个几何对象的关联矩阵,并在模式一致的情况下返回 true
其中 标记"*"与任何单元格值匹配。因此,模式“ 2 ********”匹配I/I单元为 2
的任何关联。
约束触发
要在宗地表上启用宗地重叠检查,我们需要将触发函数与实际触发相关联。
-- 创建我们自己的表
CREATE TABLE parcels (
pk bigint PRIMARY KEY,
geom geometry(Polygon, 3005)
NOT NULL CHECK (ST_IsValid(geom))
);
-- 增加约束触发器(此时触发器才和表的操作关联起来了)
CREATE CONSTRAINT TRIGGER overlapping_parcel
AFTER INSERT OR UPDATE ON parcels
FOR EACH ROW EXECUTE FUNCTION overlapping_parcel_trigger();
测试一下。首先,添加三个不重叠的宗地:
INSERT INTO parcels VALUES (1,
'POLYGON((0 100, 100 100, 100 0, 0 0, 0 100))');
INSERT INTO parcels VALUES (2,
'POLYGON((100 100, 200 100, 200 0, 100 0, 100 100))');
INSERT INTO parcels VALUES (3,
'POLYGON((200 100, 300 100, 300 0, 200 0, 200 100))');
正确!

插入重叠地块:
INSERT INTO parcels VALUES (4,
'POLYGON ((300 100, 400 100, 400 0,
300 0, 290 50, 300 100))'
);
--------------------------------
ERROR: parcel 4 shares area with another parcel
CONTEXT: PL/pgSQL function overlapping_parcel_trigger()
line 14 at RAISE
出错了!
对于简单的触发约束,重叠是一个很好的测试用例,但是对于某些结构,有些结构需要更高的质量来控制:连接的网络。
在后面一部分教程中,将研究如何使用约束和延迟来增强道路网络的连接性和其他数据质量。