本节摘要:空间分析的基础是判断几何关系。本节讲 DE-9IM 模型、相交/包含/相邻等空间关系——GIS 算法的基石。
阅读完本节,你应当能够:
GIS 要回答"这块地和那块地相邻吗"、"这个点在哪个区内"等问题。空间关系判断是空间查询、叠加分析、拓扑验证的基础。
DE-9IM(Dimensionally Extended 9-Intersection Model)用两个几何的内部(I)、边界(B)、外部(E)的 9 个交集维度判断空间关系:

| 关系 | 含义 |
|---|---|
| Intersects | 有任何公共点 |
| Contains | A 完全包含 B |
| Within | A 在 B 内 |
| Touches | 边界相切,内部不相交 |
| Overlaps | 同维交集,非包含 |
| Crosses | 内部相交,不同维 |
| Equals | 完全重合 |
-- 点在哪个区 SELECT name FROM districts WHERE ST_Contains(geom, ST_Point(116.4, 39.9)); -- 两块地相邻 SELECT a.name, b.name FROM parcels a JOIN parcels b ON ST_Touches(a.geom, b.geom); -- 范围内要素 SELECT name FROM pois WHERE ST_DWithin(geom, ST_Point(116.4, 39.9), 1000);
PostGIS 用 ST_ 前缀函数做空间判断,底层用 GEOS 库实现 DE-9IM。
空间判断慢,加空间索引加速。PostGIS 用 GiST 索引:
CREATE INDEX idx_pois_geom ON pois USING GIST(geom);
空间索引用 R-树,先粗筛候选(边界框相交),再精确判断,快几个数量级。
空间关系判断要在合适坐标系:
PostGIS 在 4326 上用 ST_DWithin 会用球面距离,但 ST_Area 要投影到平面才准。
⚠️ 常见坑:在 4326 上做面积/距离量算——4326 是度单位,结果不准。投影到平面坐标系(如 3857 或 UTM)再算。
💡 关键直觉:DE-9IM 用内部/边界/外部 9 交集定义空间关系。PostGIS 用 ST_ 函数判断,加 GiST 空间索引加速。量算要投影到平面坐标系。
DE-9IM 的 9 个值分别是两个几何的内部(I)、边界(B)、外部(E)两两求交的维度:-1 表示不相交,0 表示交于点,1 表示交于线,2 表示交于面。PostGIS 可以用 ST_Relate 拿到矩阵,调试空间关系时非常有用:
-- 返回两个几何的 DE-9IM 矩阵 SELECT ST_Relate( ST_GeomFromText('POLYGON((0 0, 2 0, 2 2, 0 2, 0 0))', 4326), ST_GeomFromText('POINT(1 1)', 4326) ); -- 点在面内时结果为 FF2F110212 等形态,可从矩阵反推关系
常见关系的矩阵模式可以记一个:包含关系一般满足 T*****FF*,相交满足 T********,相邻满足 FT*******。实际开发中直接调用 ST_Contains、ST_Intersects 就够了,矩阵主要用于理解函数行为和排查边界情况。
空间关系判断的前提是数据拓扑正确。常见的拓扑错误有:
PostGIS 提供检查和修复函数:
-- 检查几何是否有效 SELECT id, ST_IsValidReason(geom) FROM parcels WHERE NOT ST_IsValid(geom); -- 修复无效几何(去掉自相交部分) UPDATE parcels SET geom = ST_MakeValid(geom) WHERE NOT ST_IsValid(geom);
数据入库前先跑一遍有效性检查,能省掉大量后续排查时间。
空间关系函数能否用索引,取决于写法。像 ST_Intersects(a.geom, b.geom) 这类函数底层是"索引粗筛 + 精算",只要两边几何列都有 GiST 索引就能提速。但要注意:把几何包进函数再比较(如 ST_Intersects(ST_Buffer(a.geom,10), b.geom))会让优化器无法直接利用索引,性能骤降。大数据量下,先把缓冲范围算出来存字段,再建索引,是常见优化手段。
业务场景里空间关系很少单独出现,通常是组合判断。例如选址分析:找"位于规划区 A 内、距离主干道 200 米以内、且不与生态红线相交"的地块:
SELECT p.id, p.name FROM parcels p JOIN districts a ON ST_Contains(a.geom, p.geom) JOIN roads r ON ST_DWithin(p.geom, r.geom, 200) LEFT JOIN eco_redline e ON ST_Intersects(p.geom, e.geom) WHERE a.name = '规划区A' AND e.id IS NULL GROUP BY p.id, p.name;
这类 SQL 把关系判断、距离判断、排除逻辑揉在一起,是 GIS 业务查询的常见形态,值得熟练掌握。
空间关系判断在边界上最容易出问题,几个典型场景:
空间关系判断是其他空间算法的基础构件:叠加分析用相交判断决定哪些要素参与裁剪;网络分析用点和线的包含关系把起点投影到网络上;空间统计按包含关系把属性聚合到区域。学这一节时不要把函数当孤立知识,遇到后面的叠加、缓冲、网络章节时,会不断回头用到"两个几何关系是什么"这个基本功。这也是本教程把空间关系放在第 5 章第一小节的原因。
写空间关系 SQL 调试时,推荐一套组合拳:
-- 第一步:看两个几何的实际形状(用文本或转 GeoJSON 检查) SELECT ST_AsText(geom) FROM parcels WHERE id = 100; -- 第二步:算两个几何的最小距离,确认是"差一点相交"还是"完全不相干" SELECT ST_Distance(a.geom, b.geom) FROM parcels a, parcels b WHERE a.id=100 AND b.id=200; -- 第三步:逐个关系函数验证 SELECT ST_Intersects(a.geom, b.geom), ST_Within(a.geom, b.geom), ST_Touches(a.geom, b.geom) FROM parcels a, parcels b WHERE a.id=100 AND b.id=200;
配合 QGIS 打开数据目视检查,绝大部分"关系判断结果不符合预期"都能快速定位:要么是数据本身有拓扑问题,要么是坐标系统一没做好,要么是对函数语义理解偏差。
下一节讲叠加分析和缓冲区。