26. 维度扩展的九交模型¶
“维度扩展的九交模型” (DE9IM) 是一个用于模拟两个空间对象如何交互的框架。
首先,每个空间对象都有
一个内部
一个边界
一个外部
对于多边形,内部、边界和外部是显而易见的
内部是被环包围的部分;边界是环本身;外部是平面上的所有其他部分。
对于线性要素,内部、边界和外部不太为人所知
内部是线被端点包围的部分;边界是线性要素的端点,外部是平面上的所有其他部分。
对于点,情况就更奇怪了:内部是点本身;边界是空集,外部是平面上的所有其他部分。
使用这些内部、外部和边界的定义,任何一对空间要素之间的关系都可以通过一对对象内部/边界/外部之间的九个可能的交点的维数来表征。
对于上面的示例中的多边形,内部的交点是一个二维区域,因此矩阵的该部分用“2”填充。边界仅在点处相交,点是零维的,因此矩阵的该部分用“0”填充。
当组件之间没有交点时,矩阵的方块用“F”填充。
以下是一个线串部分进入多边形的另一个示例
交互的 DE9IM 矩阵如下
请注意,这两个对象的边界实际上并没有相交(线的终点与多边形的内部相交,而不是边界,反之亦然),因此 B/B 单元格用“F”填充。
虽然用视觉方式填写 DE9IM 矩阵很有趣,但如果计算机可以做到这一点会更好,这就是 ST_Relate 函数的作用。
前面的例子可以用一个简单的盒子和线来简化,它们具有与我们的多边形和线字符串相同的空间关系。
我们可以在 SQL 中生成 DE9IM 信息。
SELECT ST_Relate(
'LINESTRING(0 0, 2 0)',
'POLYGON((1 -1, 1 1, 3 1, 3 -1, 1 -1))'
);
答案 (1010F0212) 与我们用视觉方式计算的结果相同,但以 9 个字符的字符串形式返回,将表格的第一行、第二行和第三行连接在一起。
101
0F0
212
然而,DE9IM 矩阵的强大之处不在于生成它们,而在于将它们用作匹配键来查找与彼此具有非常特定关系的几何图形。
CREATE TABLE lakes ( id serial primary key, geom geometry );
CREATE TABLE docks ( id serial primary key, good boolean, geom geometry );
INSERT INTO lakes ( geom )
VALUES ( 'POLYGON ((100 200, 140 230, 180 310, 280 310, 390 270, 400 210, 320 140, 215 141, 150 170, 100 200))');
INSERT INTO docks ( geom, good )
VALUES
('LINESTRING (170 290, 205 272)',true),
('LINESTRING (120 215, 176 197)',true),
('LINESTRING (290 260, 340 250)',false),
('LINESTRING (350 300, 400 320)',false),
('LINESTRING (370 230, 420 240)',false),
('LINESTRING (370 180, 390 160)',false);
假设我们有一个数据模型,其中包含 Lakes 和 Docks,并且假设码头必须位于湖泊内部,并且必须在一端接触其包含的湖泊的边界。我们能否找到数据库中所有符合该规则的码头?
我们的合法码头具有以下特征:
它们的内部与湖泊内部有线性 (1D) 交点。
它们的边界与湖泊内部有点 (0D) 交点。
它们的边界 也与湖泊边界有点 (0D) 交点。
它们的内部与湖泊外部没有交点 (F)。
因此,它们的 DE9IM 矩阵如下所示:
因此,要找到所有合法码头,我们需要找到所有与湖泊相交的码头(我们用于连接键的 潜在候选者的超集),然后找到该集合中具有合法关系模式的所有码头。
SELECT docks.*
FROM docks JOIN lakes ON ST_Intersects(docks.geom, lakes.geom)
WHERE ST_Relate(docks.geom, lakes.geom, '1FF00F212');
-- Answer: our two good docks
请注意 ST_Relate 的三参数版本的使用,如果模式匹配则返回 true,否则返回 false。对于像这样完全定义的模式,不需要三参数版本 - 我们只需使用字符串相等运算符即可。
但是,对于更松散的模式搜索,三参数允许在模式字符串中使用替换字符。
“*”表示“此单元格中的任何值都是可接受的”。
“T”表示“任何非假值 (0、1 或 2) 都是可接受的”。
例如,我们示例图形中没有包含的一种可能的码头是与湖泊边界具有二维交点的码头。
INSERT INTO docks ( geom, good )
VALUES ('LINESTRING (140 230, 150 250, 210 230)',true);
如果我们要将这种情况包含在我们的一组“合法”码头中,我们需要更改查询中的关系模式。特别是,码头内部与湖泊边界的交点现在可以是 1(我们的新情况)或 F(我们的原始情况)。因此,我们在模式中使用“*”通配符。
SQL 代码如下所示:
SELECT docks.*
FROM docks JOIN lakes ON ST_Intersects(docks.geom, lakes.geom)
WHERE ST_Relate(docks.geom, lakes.geom, '1*F00F212');
-- Answer: our (now) three good docks
确认上一个示例中更严格的 SQL 代码 *不会* 返回新的码头。
26.1. 数据质量测试¶
TIGER 数据在准备时经过了严格的质量控制,因此我们期望我们的数据符合严格的标准。例如:任何人口普查区块都不应与其他人口普查区块重叠。我们可以测试一下吗?
当然可以!
SELECT a.gid, b.gid
FROM nyc_census_blocks a, nyc_census_blocks b
WHERE ST_Intersects(a.geom, b.geom)
AND ST_Relate(a.geom, b.geom, '2********')
AND a.gid != b.gid
LIMIT 10;
-- Answer: 10, there's some funny business
同样,我们期望道路数据都是端点节点的。也就是说,我们期望交叉点只发生在线的端点,而不是在中点。
我们可以通过查找相交的街道(因此我们有一个连接)来测试这一点,但边界之间的交点不是零维的(也就是说,端点没有接触)。
SELECT a.gid, b.gid
FROM nyc_streets a, nyc_streets b
WHERE ST_Intersects(a.geom, b.geom)
AND NOT ST_Relate(a.geom, b.geom, '****0****')
AND a.gid != b.gid
LIMIT 10;
-- Answer: This happens, so the data is not end-noded.
26.1.1. 函数列表¶
ST_Relate(geometry A, geometry B): 返回表示几何体之间 DE9IM 关系的文本字符串。