PostGIS求交集怎么写?空间分析函数详解?

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

在做地块叠加、行政区裁剪、缓冲区影响范围统计时,很多同学都会问:PostGIS求交集怎么写?空间分析函数详解? 这类问题的核心不是只会写一个 ST_Intersection,而是要理解“空间相交判断”和“几何交集生成”是两件事。

本文以 PostGIS 求交集为主线,围绕 ST_IntersectsST_IntersectionST_DifferenceST_Union 等常用空间分析函数,讲清楚什么时候用判断函数,什么时候用叠加分析函数,以及如何避免结果为空、运行很慢、几何无效等常见问题。

PostGIS求交集 ST_Intersection 空间分析函数示意图
PostGIS 求交集的基本流程:先用空间关系过滤,再生成真实交集几何。

引言:PostGIS求交集到底是在求什么

在 GIS 里,“求交集”通常有两种含义。

  • 判断两个要素是否相交:只需要知道 A 和 B 是否有空间重叠,用 ST_Intersects
  • 生成相交后的新几何:需要得到重叠部分的面、线或点,用 ST_Intersection

很多 PostGIS 求交集写错,原因就是把这两个函数混用了。ST_Intersects 返回的是布尔值,结果是 truefalseST_Intersection 返回的是几何对象,才会真正生成交集图形。

一句话记忆:ST_Intersects 用来筛选相交对象,ST_Intersection 用来生成交集结果。

背景:典型业务场景中的PostGIS求交集

假设有两个表:

  • parcels:宗地面图层,字段包括 idownergeom
  • zones:规划分区面图层,字段包括 zone_idzone_namegeom

现在要计算每个宗地落入不同规划分区的部分,并输出新的交集面。这就是一个典型的 PostGIS 求交集任务。

最终结果通常需要包含:

  • 宗地编号;
  • 规划分区编号;
  • 交集后的几何;
  • 交集面积;
  • 原始属性字段。

如果只写 ST_Intersection(parcels.geom, zones.geom) 而不加空间过滤,数据库会对大量不相交的要素也尝试计算,速度会非常慢。因此,实际项目中一般要先用 ST_Intersects 过滤,再用 ST_Intersection 生成结果。

原理:PostGIS空间分析函数的基本分工

1. ST_Intersects:判断是否相交

ST_Intersects(geomA, geomB) 用于判断两个几何是否有任意空间接触或重叠。只要两个几何有公共部分,就返回 true

SELECT *
FROM parcels p
JOIN zones z
ON ST_Intersects(p.geom, z.geom);

这条 SQL 不会生成新的交集几何,只是找出相交的宗地和分区组合。

2. ST_Intersection:生成交集几何

ST_Intersection(geomA, geomB) 会返回两个几何的公共部分。对于两个面图层,结果通常是交集面;对于线和面,结果可能是线;对于线和线,结果可能是点或线。

SELECT
  p.id AS parcel_id,
  z.zone_id,
  ST_Intersection(p.geom, z.geom) AS geom
FROM parcels p
JOIN zones z
ON ST_Intersects(p.geom, z.geom);

这是 PostGIS 求交集最常见的写法:ST_Intersects 做连接条件,用 ST_Intersection 生成结果几何。

3. ST_Difference:求差集

ST_Difference(geomA, geomB) 表示从 A 中扣除 B 的部分。例如,计算宗地中不在生态红线内的区域,可以使用差集。

SELECT
  p.id,
  ST_Difference(p.geom, r.geom) AS geom
FROM parcels p
JOIN redline r
ON ST_Intersects(p.geom, r.geom);

4. ST_Union:合并几何

ST_Union 常用于把多个几何合并成一个或按分组合并。例如,将同一规划类型的交集结果合并。

SELECT
  zone_id,
  ST_Union(geom) AS geom
FROM result_intersection
GROUP BY zone_id;

步骤:PostGIS求交集的标准SQL写法

步骤一:确认几何字段和坐标系

先查看两个表的几何字段类型和 SRID。SRID 是空间参考标识,用于说明数据使用的坐标系。

SELECT
  f_table_name,
  f_geometry_column,
  type,
  srid
FROM geometry_columns
WHERE f_table_name IN ('parcels', 'zones');

如果两个图层 SRID 不一致,直接求交集可能报错,或结果不符合预期。可以用下面的 SQL 检查:

SELECT ST_SRID(geom) FROM parcels LIMIT 1;
SELECT ST_SRID(geom) FROM zones LIMIT 1;

步骤二:为几何字段建立空间索引

PostGIS 求交集涉及大量空间关系判断,建议先建立 GiST 空间索引。

CREATE INDEX parcels_geom_gix
ON parcels
USING GIST (geom);

CREATE INDEX zones_geom_gix
ON zones
USING GIST (geom);

建完索引后,执行:

ANALYZE parcels;
ANALYZE zones;

ANALYZE 会更新 PostgreSQL 的统计信息,帮助查询优化器选择更合适的执行计划。

步骤三:先筛选相交对象

先用 ST_Intersects 看看相交记录有多少。

SELECT COUNT(*)
FROM parcels p
JOIN zones z
ON ST_Intersects(p.geom, z.geom);

如果数量远大于预期,需要检查数据是否有重复要素、坐标系是否正确、图层范围是否异常。

步骤四:生成交集结果表

下面是一个完整的 PostGIS 求交集 SQL 示例。

CREATE TABLE parcel_zone_intersection AS
SELECT
  p.id AS parcel_id,
  p.owner,
  z.zone_id,
  z.zone_name,
  ST_Multi(
    ST_CollectionExtract(
      ST_Intersection(p.geom, z.geom),
      3
    )
  ) AS geom
FROM parcels p
JOIN zones z
ON ST_Intersects(p.geom, z.geom)
WHERE NOT ST_IsEmpty(ST_Intersection(p.geom, z.geom));

这里有两个处理细节:

  • ST_CollectionExtract(..., 3):从几何集合中提取面类型。参数 3 代表 Polygon。
  • ST_Multi(...):把结果统一为 MultiPolygon,便于后续建表和制图。

如果你的输入是线图层,ST_CollectionExtract 的类型参数应根据实际结果调整。常见类型为:1 表示点,2 表示线,3 表示面。

步骤五:添加主键、几何约束和空间索引

生成结果表后,建议补充主键和空间索引。

ALTER TABLE parcel_zone_intersection
ADD COLUMN gid serial PRIMARY KEY;

CREATE INDEX parcel_zone_intersection_geom_gix
ON parcel_zone_intersection
USING GIST (geom);

ANALYZE parcel_zone_intersection;

如果要在 QGIS 或 GeoServer 中使用结果表,建议确认几何类型和 SRID 是否明确。

SELECT
  GeometryType(geom) AS geom_type,
  ST_SRID(geom) AS srid,
  COUNT(*)
FROM parcel_zone_intersection
GROUP BY GeometryType(geom), ST_SRID(geom);

步骤六:计算交集面积

如果数据使用的是投影坐标系,单位通常是米,面积单位通常是平方米。

ALTER TABLE parcel_zone_intersection
ADD COLUMN area_m2 double precision;

UPDATE parcel_zone_intersection
SET area_m2 = ST_Area(geom);

如果数据是经纬度坐标系,例如 EPSG:4326,不建议直接用 ST_Area(geometry) 当作平方米面积。可以转为合适的投影坐标系后再计算。

UPDATE parcel_zone_intersection
SET area_m2 = ST_Area(
  ST_Transform(geom, 3857)
);

注意:EPSG:3857 适合一般 Web 地图显示,不一定适合精确面积统计。正式面积核算应使用当地适合的等面积投影或官方要求的投影坐标系。

常见坑:PostGIS求交集为什么结果为空或很慢

坑一:把 ST_Intersects 当成交集结果

错误写法:

SELECT ST_Intersects(p.geom, z.geom)
FROM parcels p, zones z;

这只会返回 truefalse,不会生成新的几何。正确做法是用 ST_Intersection 输出交集几何。

坑二:没有空间索引导致查询很慢

如果两个表各有几万条面要素,不建空间索引直接求交集,性能会明显下降。至少应为参与空间连接的几何字段建立 GiST 索引。

CREATE INDEX your_table_geom_gix
ON your_table
USING GIST (geom);

坑三:坐标系不一致

PostGIS 中两个几何的 SRID 不一致时,很多空间函数会报错,例如提示 mixed SRID。应先统一坐标系。

SELECT ST_Transform(geom, 4547)
FROM parcels;

如果只是修改元数据,不能用 ST_SetSRID 替代投影转换。ST_SetSRID 只设置 SRID 标签,不改变坐标值;ST_Transform 才会真正转换坐标。

坑四:几何无效导致求交失败

面自相交、环方向异常、重复节点等问题都可能导致 ST_Intersection 报错或产生异常结果。可以先检查几何有效性。

SELECT id, ST_IsValidReason(geom)
FROM parcels
WHERE NOT ST_IsValid(geom);

修复几何可以尝试使用:

UPDATE parcels
SET geom = ST_MakeValid(geom)
WHERE NOT ST_IsValid(geom);

坑五:交集结果出现 GeometryCollection

两个面相交时,理论上应得到面,但在边界接触、拓扑复杂或几何破碎时,结果可能包含点、线和面混合的 GeometryCollection。如果后续只需要面,应使用 ST_CollectionExtract 提取面。

ST_CollectionExtract(ST_Intersection(a.geom, b.geom), 3)

坑六:结果有极小碎片面

叠加分析后出现很小的碎片面很常见,通常来自边界不完全重合、数据采集精度不同或拓扑缝隙。可以根据业务阈值过滤。

DELETE FROM parcel_zone_intersection
WHERE ST_Area(geom) < 1;

这个阈值不能随便定,应结合数据比例尺、坐标单位和业务规则。

方法比较:ST_Intersects、ST_Intersection和其他空间分析函数

函数 作用 返回结果 典型用途
ST_Intersects 判断是否相交 布尔值 空间连接、筛选相交对象
ST_Intersection 生成公共部分 几何 求交集、叠加分析、裁剪
ST_Within 判断 A 是否完全在 B 内 布尔值 点落区、要素归属判断
ST_Contains 判断 A 是否包含 B 布尔值 行政区包含地物、面包含点
ST_Difference 从 A 中扣除 B 几何 求差集、排除限制区
ST_Union 合并多个几何 几何 按类别 dissolve、合并分区

在实际工作中,PostGIS 求交集通常不是单独使用一个函数,而是组合使用:

ST_Intersects 负责过滤候选对象
ST_Intersection 负责生成交集几何
ST_Area 负责计算交集面积
ST_MakeValid 负责修复问题几何
ST_CollectionExtract 负责清理结果类型

检查清单:写PostGIS求交集SQL前先核对这些项

  • 两个表是否都有几何字段,例如 geom
  • 两个图层的 SRID 是否一致?
  • 几何类型是否符合预期,例如 Polygon 或 MultiPolygon?
  • 几何是否有效,是否存在自相交面?
  • 是否已经建立 GiST 空间索引?
  • 是否先用 ST_Intersects 过滤,再用 ST_Intersection 求交?
  • 是否处理了空几何和 GeometryCollection?
  • 是否需要统一输出为 MultiPolygon?
  • 面积计算使用的坐标系是否适合面积统计?
  • 是否需要过滤很小的碎片面?

如果你要把结果发布到 GeoServer、加载到 QGIS 或用于 WebGIS 展示,还应确认结果表有主键、空间索引和明确的 SRID。

FAQ:PostGIS求交集常见问题

1. PostGIS求交集应该用哪个函数?

如果只是判断两个要素是否相交,用 ST_Intersects。如果要生成重叠部分的几何,用 ST_Intersection。实际项目中通常二者一起用:先筛选,再生成交集。

2. ST_Intersects和ST_Intersection有什么区别?

ST_Intersects 返回布尔值,表示是否相交;ST_Intersection 返回几何,表示相交的空间部分。前者适合做条件判断,后者适合做叠加分析结果。

3. 为什么PostGIS求交集结果为空?

常见原因包括:两个图层实际不重叠、坐标系不一致、几何无效、输入数据范围异常,或交集只发生在边界点线但你只提取了面结果。可以先用 ST_Intersects 统计相交数量,再检查 SRID 和几何有效性。

4. 为什么ST_Intersection运行很慢?

常见原因是没有空间索引、数据量太大、几何过于复杂,或没有先用空间关系过滤。建议为几何字段建立 GiST 索引,并使用 ST_Intersects 作为连接条件。

5. PostGIS求交集后怎么计算面积?

如果几何使用合适的投影坐标系,可以直接用 ST_Area(geom)。如果是 EPSG:4326 经纬度数据,应先转换到适合面积统计的投影坐标系,再计算面积。

6. 交集结果为什么变成GeometryCollection?

因为相交结果可能同时包含点、线、面,例如面与面只在边界接触,或复杂边界叠加后生成混合几何。若只需要面,可以使用 ST_CollectionExtract(result, 3) 提取面部分。

7. PostGIS可以像ArcGIS的Intersect工具一样做叠加分析吗?

可以。PostGIS 的 ST_Intersection 加上空间连接、属性选择、面积计算和结果建表,基本可以完成类似 ArcGIS Intersect 的叠加分析流程。但 PostGIS 更依赖你自己写 SQL 控制字段、几何类型和过滤逻辑。

结论:PostGIS求交集的可靠写法

PostGIS 求交集的核心写法可以概括为:统一坐标系,检查几何有效性,建立空间索引,使用 ST_Intersects 过滤候选对象,再用 ST_Intersection 生成交集几何。

对于面图层叠加分析,推荐从下面这个模板开始改:

CREATE TABLE result_intersection AS
SELECT
  a.id AS a_id,
  b.id AS b_id,
  ST_Multi(
    ST_CollectionExtract(
      ST_Intersection(a.geom, b.geom),
      3
    )
  ) AS geom
FROM table_a a
JOIN table_b b
ON ST_Intersects(a.geom, b.geom)
WHERE NOT ST_IsEmpty(ST_Intersection(a.geom, b.geom));

掌握这个模板后,再根据业务需要添加属性字段、面积字段、碎片过滤和结果索引,就能完成大多数 PostGIS 空间分析函数中的求交集任务。