PostGIS常用函数有哪些?ST_Buffer怎么写?

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

PostGIS常用函数有哪些?ST_Buffer怎么写? 这是很多刚从桌面 GIS 转到空间数据库的同学会遇到的问题:知道 PostGIS 很强,但打开 SQL 编辑器后,不清楚哪些函数最常用,也不知道缓冲区分析的 SQL 应该怎样写才算正确。

本文以实际 GIS 工作流为主线,整理 PostGIS 中最常用的一组空间函数,并重点讲清楚 ST_Buffer 的基本写法、单位问题、投影要求、常见错误和结果检查方法。适合 GIS 学生、初级 GIS 工程师、空间数据分析人员和正在学习 PostgreSQL/PostGIS 的读者。

PostGIS常用函数和ST_Buffer缓冲区分析SQL流程图
PostGIS 常用函数通常围绕几何创建、空间关系判断、空间分析和结果输出展开,ST_Buffer 是最常见的分析函数之一。

引言:先把 PostGIS常用函数 分成几类

学习 PostGIS 不建议从函数大全开始背。更实用的方法是按 GIS 工作流分类:先看数据是什么,再判断空间关系,然后做分析,最后输出结果。

常见工作中,PostGIS 常用函数大致可以分为以下几类:

  • 几何信息函数:查看几何类型、坐标系、边界范围。
  • 几何构造函数:从 WKT、经纬度、GeoJSON 等创建几何对象。
  • 空间关系函数:判断相交、包含、距离内、相离等关系。
  • 空间分析函数:缓冲区、叠加、裁剪、合并、差集等。
  • 测量函数:计算长度、面积、距离。
  • 坐标转换函数:处理不同坐标系之间的转换。
  • 输出转换函数:把结果转成 WKT、GeoJSON、MVT 等格式。

其中 ST_Buffer 属于空间分析函数,常用于道路影响范围、点位服务半径、河流保护带、建筑退让线、管线安全距离等分析场景。

背景:为什么 ST_Buffer 写错后结果经常不对

很多人第一次写 ST_Buffer 时,会直接使用下面这种 SQL:

SELECT ST_Buffer(geom, 100)
FROM roads;

这条语句语法上可能没有问题,但结果是否正确,取决于 geom 的坐标系单位。如果数据是投影坐标系,单位通常是米,那么 100 大概率表示 100 米;如果数据是经纬度坐标系,单位是度,那么 100 就不是 100 米,而是 100 度,结果会非常夸张。

所以,写 ST_Buffer 前必须先确认三件事:

  • 空间字段是不是有效几何。
  • SRID 是否正确设置。
  • 坐标系单位是否适合直接做距离分析。

可以先用下面的 SQL 检查表中的几何类型和 SRID:

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

如果返回的 SRID 是 4326,通常表示 WGS 84 经纬度坐标。此时不建议直接使用 ST_Buffer(geom, 100) 来表达 100 米缓冲区。

原理:PostGIS常用函数 的基本逻辑

PostGIS 的核心对象是几何字段,常见字段名为 geom。多数 PostGIS 常用函数都遵循类似结构:

函数名(几何字段或几何对象, 参数)

例如:

用途 常用函数 说明
查看坐标系 ST_SRID(geom) 返回几何对象的 SRID
坐标转换 ST_Transform(geom, 目标SRID) 把几何转换到另一个坐标系
空间相交 ST_Intersects(a.geom, b.geom) 判断两个几何是否相交
距离内查询 ST_DWithin(a.geom, b.geom, 距离) 判断两个几何是否在指定距离内
缓冲区 ST_Buffer(geom, 距离) 生成指定距离的缓冲区面
面积计算 ST_Area(geom) 计算面几何面积
长度计算 ST_Length(geom) 计算线几何长度
结果输出 ST_AsGeoJSON(geom) 输出 GeoJSON 字符串

ST_Buffer 的原理是:以点、线或面为输入,根据指定距离向外或向内生成一个新的面几何。点缓冲区通常是圆形,线缓冲区是沿线两侧扩展的面,面缓冲区可以向外扩张,也可以用负值向内收缩。

但要注意:PostGIS 中的 geometry 类型按坐标单位计算距离。坐标单位是度,缓冲距离就是度;坐标单位是米,缓冲距离才是米。

步骤:ST_Buffer怎么写 才适合实际项目

步骤一:确认空间表和字段

假设有一张道路表 roads,主键为 id,道路名称字段为 name,空间字段为 geom

先检查表结构和空间字段情况:

SELECT
  id,
  name,
  ST_GeometryType(geom) AS geom_type,
  ST_SRID(geom) AS srid
FROM roads
LIMIT 10;

如果 geom_type 返回 ST_LineStringST_MultiLineString,说明道路数据适合做线缓冲区。

步骤二:投影坐标系下直接写 ST_Buffer

如果道路数据已经是以米为单位的投影坐标系,例如某个城市使用的高斯克吕格、UTM 或地方投影坐标系,可以直接写:

SELECT
  id,
  name,
  ST_Buffer(geom, 100) AS geom
FROM roads;

这表示为每条道路生成 100 米缓冲区。结果字段仍然命名为 geom,便于 QGIS、ArcGIS Pro 或 WebGIS 端直接识别为空间结果。

如果希望保存为新表:

CREATE TABLE road_buffer_100m AS
SELECT
  id,
  name,
  ST_Buffer(geom, 100) AS geom
FROM roads;

创建新表后,建议补充空间索引:

CREATE INDEX road_buffer_100m_geom_idx
ON road_buffer_100m
USING GIST (geom);

步骤三:经纬度数据先转换坐标系再缓冲

如果原始数据是 EPSG:4326 经纬度坐标,不要直接用 ST_Buffer(geom, 100) 表示 100 米。更稳妥的做法是先用 ST_Transform 转到合适的投影坐标系,再做 ST_Buffer。

例如数据位于 UTM 50N 区域,可转换到 EPSG:32650:

SELECT
  id,
  name,
  ST_Transform(
    ST_Buffer(
      ST_Transform(geom, 32650),
      100
    ),
    4326
  ) AS geom
FROM roads;

这段 SQL 的含义是:

  1. 先把 geom 从 EPSG:4326 转到 EPSG:32650。
  2. 在以米为单位的投影坐标系中生成 100 米缓冲区。
  3. 再把缓冲区结果转回 EPSG:4326,便于 WebGIS 或通用地图服务使用。

如果你的项目区域不在 EPSG:32650,需要根据实际位置选择合适的投影坐标系。不要机械照抄 SRID。

步骤四:控制 ST_Buffer 的圆滑程度和端点样式

ST_Buffer 可以带第三个参数,用来控制缓冲区样式。例如:

SELECT
  id,
  name,
  ST_Buffer(
    geom,
    100,
    'quad_segs=8 endcap=round join=round'
  ) AS geom
FROM roads;

常见参数包括:

参数 示例 作用
quad_segs quad_segs=8 控制圆弧平滑程度,值越大越圆滑,几何也越复杂
endcap endcap=round 控制线缓冲区端点样式
join join=round 控制折线拐角连接样式
side side=left 生成线的一侧缓冲区

例如只生成道路左侧 50 米缓冲区:

SELECT
  id,
  name,
  ST_Buffer(
    geom,
    50,
    'side=left endcap=flat join=round'
  ) AS geom
FROM roads;

这类写法常用于道路红线、河道一侧保护范围、管线影响范围等业务场景。

步骤五:用 ST_Buffer 配合空间查询

ST_Buffer 经常和 ST_Intersects 搭配使用。例如查询距离道路 100 米范围内的学校:

SELECT DISTINCT
  s.id,
  s.name,
  s.geom
FROM schools s
JOIN roads r
ON ST_Intersects(
  s.geom,
  ST_Buffer(r.geom, 100)
);

如果数据量较大,更推荐使用 ST_DWithin,它通常更适合做距离范围查询,并且更容易利用空间索引:

SELECT DISTINCT
  s.id,
  s.name,
  s.geom
FROM schools s
JOIN roads r
ON ST_DWithin(s.geom, r.geom, 100);

简单理解:如果只是判断“是否在 100 米范围内”,优先考虑 ST_DWithin;如果确实需要生成一个缓冲区面图层,再使用 ST_Buffer

常见坑:PostGIS ST_Buffer 最容易出错的地方

坑一:把经纬度的度当成米

这是 ST_Buffer 最常见的问题。EPSG:4326 下的坐标单位是度,不是米。直接写 ST_Buffer(geom, 100) 会得到非常大的缓冲区。

正确思路是:先转换到合适的投影坐标系,再缓冲,必要时再转回经纬度。

坑二:SRID 为空或设置错误

如果 ST_SRID(geom) 返回 0,说明几何没有明确坐标系。此时不能盲目使用 ST_Transform,因为 PostGIS 不知道它原来属于哪个坐标系。

如果你确认数据本身就是 EPSG:4326,但只是缺少 SRID,可以使用:

UPDATE roads
SET geom = ST_SetSRID(geom, 4326)
WHERE ST_SRID(geom) = 0;

注意:ST_SetSRID 只是声明坐标系,不会改变坐标值;ST_Transform 才会真正转换坐标。

坑三:缓冲区结果几何太复杂

quad_segs 设置过大,会让缓冲区边界非常平滑,但也会导致节点数量增加,查询和渲染变慢。WebGIS 展示时尤其明显。

如果只是用于范围筛选,不一定要生成非常圆滑的缓冲区。可以适当降低 quad_segs

SELECT
  id,
  ST_Buffer(geom, 100, 'quad_segs=4') AS geom
FROM roads;

坑四:对无效几何直接做缓冲

如果输入几何存在自相交、空几何或拓扑异常,ST_Buffer 可能报错,也可能生成意外结果。可以先检查几何有效性:

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

必要时可以使用 ST_MakeValid 修复:

SELECT
  id,
  ST_Buffer(ST_MakeValid(geom), 10) AS geom
FROM parcels;

坑五:负缓冲导致面消失

对面数据使用负值缓冲可以实现向内收缩:

SELECT
  id,
  ST_Buffer(geom, -5) AS geom
FROM parcels;

但如果面很窄或面积很小,向内收缩后可能变成空几何。建议加上空几何过滤:

SELECT
  id,
  buf.geom
FROM (
  SELECT
    id,
    ST_Buffer(geom, -5) AS geom
  FROM parcels
) AS buf
WHERE NOT ST_IsEmpty(buf.geom);

方法比较:ST_Buffer、ST_DWithin、ST_Intersects 该怎么选

PostGIS 常用函数之间不是互相替代的关系,而是适用于不同目的。下面这个表可以帮助你快速判断。

需求 推荐函数 原因
生成可视化缓冲区图层 ST_Buffer 可以得到真实的面几何,适合制图和输出
查询某对象距离另一对象多少米以内 ST_DWithin 更适合范围判断,通常性能更好
判断两个图层是否相交 ST_Intersects 适合叠加筛选、点落区、线面相交判断
计算两个对象实际距离 ST_Distance 返回距离数值,可用于排序和统计
裁剪一个图层到另一个范围内 ST_Intersection 返回重叠部分的新几何
合并多个缓冲区 ST_Union 适合生成整体影响范围

例如,如果要生成“地铁站 800 米服务范围图”,可以用 ST_Buffer;如果只是要找出 800 米范围内的小区,优先使用 ST_DWithin;如果要输出服务范围和行政区的重叠区域,则可能需要 ST_Intersection。

检查清单:写 PostGIS ST_Buffer 前后要确认什么

在实际项目中,建议按下面清单检查,能避免大多数 ST_Buffer 结果异常问题。

  • 检查 SRID:使用 ST_SRID(geom) 确认坐标系是否正确。
  • 确认单位:投影坐标系通常按米计算,经纬度坐标系通常按度计算。
  • 选择投影:跨城市、跨省或跨国家数据,不要随意使用一个局部投影。
  • 检查几何有效性:使用 ST_IsValid 排查异常几何。
  • 控制复杂度:合理设置 quad_segs,避免结果过重。
  • 建立空间索引:对大表的空间字段创建 GiST 索引。
  • 区分分析和查询:需要面结果用 ST_Buffer,只做距离筛选优先用 ST_DWithin。
  • 验证结果:把结果加载到 QGIS 或 ArcGIS Pro 中抽查范围是否符合预期。

常用空间索引写法如下:

CREATE INDEX roads_geom_idx
ON roads
USING GIST (geom);

如果表数据更新较多,可以执行统计信息更新:

ANALYZE roads;

FAQ:PostGIS常用函数有哪些?ST_Buffer怎么写?

1. PostGIS 常用函数需要全部记住吗?

不需要。初学阶段建议先掌握 ST_SRIDST_TransformST_IntersectsST_DWithinST_BufferST_AreaST_LengthST_AsGeoJSON 这几类函数。它们已经能覆盖大量日常 GIS 数据处理任务。

2. ST_Buffer 的距离单位是什么?

对于 geometry 类型,ST_Buffer 的距离单位取决于数据坐标系单位。投影坐标系如果单位是米,缓冲距离就是米;EPSG:4326 经纬度坐标系下,单位是度,不是米。

3. 经纬度数据能不能直接做 ST_Buffer?

技术上可以执行,但通常不适合表达米级缓冲区。实际项目中应先用 ST_Transform 转换到合适的投影坐标系,再执行 ST_Buffer。

4. ST_Buffer 和 ST_DWithin 有什么区别?

ST_Buffer 会生成一个新的面几何,适合制图和输出缓冲区结果。ST_DWithin 用于判断两个几何是否在指定距离内,适合空间查询和筛选。如果不需要真正输出缓冲区面,优先考虑 ST_DWithin。

5. 为什么 ST_Buffer 之后地图加载变慢?

缓冲区会增加几何节点数量,尤其是线数据和大范围面数据。可以降低 quad_segs,简化结果,或者只在服务端做范围判断,不把复杂缓冲区直接传到前端。

6. ST_Buffer 可以做内缓冲吗?

可以。对面几何使用负距离即可,例如 ST_Buffer(geom, -10) 表示向内收缩 10 个坐标单位。但如果面太小,结果可能为空,需要使用 ST_IsEmpty 检查。

7. ST_Buffer 生成的结果如何保存为新图层?

可以使用 CREATE TABLE AS SELECT 创建新表,再为结果表建立 GiST 空间索引。之后可在 QGIS、ArcGIS Pro 或 GeoServer 中直接加载这张结果表。

结论:先理解单位,再写 ST_Buffer

PostGIS常用函数有哪些?ST_Buffer怎么写? 这个问题的关键不在于背多少函数,而在于理解空间数据库的基本分析逻辑。日常工作中,先掌握坐标检查、坐标转换、空间关系判断、缓冲区分析和结果输出,就能解决大部分 PostGIS 入门到进阶场景。

写 ST_Buffer 时请记住一个原则:先确认坐标系和单位,再决定缓冲距离。如果是经纬度数据,通常要先转换到合适的投影坐标系;如果只是做距离范围查询,不一定要生成缓冲区,使用 ST_DWithin 往往更直接。

把这些 PostGIS 常用函数组合起来,你就可以在数据库中完成道路影响范围、设施服务半径、点线面空间筛选、面积长度统计等常见 GIS 分析任务,而不必每一步都依赖桌面软件手工处理。