第10章-空间分析函数

第10章:空间分析函数

10.1 空间分析概述

空间分析函数是 PostGIS 最强大的功能之一,用于执行各种几何运算和空间分析操作。

10.1.1 函数分类

┌─────────────────────────────────────────────────────────────┐
│                    空间分析函数分类                          │
├─────────────────────────────────────────────────────────────┤
│  几何叠加操作                                                │
│  ├── ST_Intersection    - 求交                              │
│  ├── ST_Union           - 求并                              │
│  ├── ST_Difference      - 求差                              │
│  ├── ST_SymDifference   - 对称差                            │
│  └── ST_Split           - 分割                              │
│                                                             │
│  几何生成操作                                                │
│  ├── ST_Buffer          - 缓冲区                            │
│  ├── ST_ConvexHull      - 凸包                              │
│  ├── ST_ConcaveHull     - 凹包                              │
│  └── ST_OffsetCurve     - 偏移曲线                          │
│                                                             │
│  几何简化与修复                                              │
│  ├── ST_Simplify        - 简化                              │
│  ├── ST_SimplifyPreserveTopology - 保持拓扑的简化           │
│  ├── ST_MakeValid       - 修复无效几何                      │
│  └── ST_Snap            - 捕捉                              │
│                                                             │
│  几何变换                                                    │
│  ├── ST_Translate       - 平移                              │
│  ├── ST_Scale           - 缩放                              │
│  ├── ST_Rotate          - 旋转                              │
│  └── ST_Affine          - 仿射变换                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

10.2 几何叠加操作

10.2.1 ST_Intersection(求交)

-- 两个多边形的交集
SELECT ST_AsText(ST_Intersection(
    ST_MakeEnvelope(0, 0, 2, 2),
    ST_MakeEnvelope(1, 1, 3, 3)
));
-- POLYGON((1 1,1 2,2 2,2 1,1 1))

-- 线与多边形的交集
SELECT ST_AsText(ST_Intersection(
    ST_GeomFromText('LINESTRING(0 0.5, 3 0.5)'),
    ST_MakeEnvelope(1, 0, 2, 1)
));
-- LINESTRING(1 0.5,2 0.5)

-- 计算道路在某区域内的部分
SELECT r.name, ST_AsText(ST_Intersection(r.geom, d.geom)) AS road_in_district
FROM roads r, districts d
WHERE d.name = '东城区'
  AND ST_Intersects(r.geom, d.geom);

-- 计算重叠面积
SELECT 
    a.name AS area_a,
    b.name AS area_b,
    ST_Area(ST_Intersection(a.geom, b.geom)::geography) AS overlap_area_m2
FROM districts a, districts b
WHERE a.id < b.id
  AND ST_Intersects(a.geom, b.geom);

10.2.2 ST_Union(求并)

-- 两个几何的并集
SELECT ST_AsText(ST_Union(
    ST_MakeEnvelope(0, 0, 2, 2),
    ST_MakeEnvelope(1, 1, 3, 3)
));
-- POLYGON((0 0,0 2,1 2,1 3,3 3,3 1,2 1,2 0,0 0))

-- 聚合合并多个几何
SELECT ST_Union(geom) AS merged_geom
FROM districts
WHERE province = '北京';

-- 合并某类别的所有 POI 缓冲区
SELECT category, ST_Union(ST_Buffer(geom::geography, 500)::geometry) AS service_area
FROM poi
GROUP BY category;

-- ST_UnaryUnion:合并单个几何集合(去除重叠)
SELECT ST_UnaryUnion(ST_Collect(geom)) FROM overlapping_polygons;

-- 使用 ST_MemUnion 处理大量几何(内存效率更高)
SELECT ST_MemUnion(geom) FROM large_polygon_set;

10.2.3 ST_Difference(求差)

-- 从 A 中减去 B
SELECT ST_AsText(ST_Difference(
    ST_MakeEnvelope(0, 0, 3, 3),
    ST_MakeEnvelope(1, 1, 2, 2)
));
-- POLYGON((0 0,0 3,3 3,3 0,0 0),(1 1,2 1,2 2,1 2,1 1))

-- 从区域中挖去湖泊
UPDATE land_areas SET geom = ST_Difference(geom, (
    SELECT ST_Union(geom) FROM lakes WHERE ST_Intersects(lakes.geom, land_areas.geom)
));

-- 计算 A 独有的部分
SELECT 
    a.name,
    ST_Area(ST_Difference(a.geom, COALESCE(ST_Union(b.geom), 'POLYGON EMPTY'::geometry))::geography) AS unique_area
FROM districts a
LEFT JOIN other_areas b ON ST_Intersects(a.geom, b.geom)
GROUP BY a.id, a.name, a.geom;

10.2.4 ST_SymDifference(对称差)

-- 对称差:(A - B) ∪ (B - A)
SELECT ST_AsText(ST_SymDifference(
    ST_MakeEnvelope(0, 0, 2, 2),
    ST_MakeEnvelope(1, 1, 3, 3)
));
-- 返回两个不重叠的部分

-- 找出两个版本数据的差异
SELECT ST_SymDifference(old_version.geom, new_version.geom) AS changed_area
FROM old_version, new_version
WHERE old_version.id = new_version.id;

10.2.5 ST_Split(分割)

-- 用线分割多边形
SELECT (ST_Dump(ST_Split(
    ST_MakeEnvelope(0, 0, 2, 2),
    ST_GeomFromText('LINESTRING(0 1, 2 1)')
))).geom AS split_result;

-- 用点分割线
SELECT (ST_Dump(ST_Split(
    ST_GeomFromText('LINESTRING(0 0, 2 2)'),
    ST_GeomFromText('POINT(1 1)')
))).geom AS split_result;

-- 用道路分割区域
SELECT d.name, (ST_Dump(ST_Split(d.geom, r.geom))).geom AS sub_area
FROM districts d, roads r
WHERE ST_Intersects(d.geom, r.geom)
  AND r.name = '长安街';

10.3 缓冲区分析

10.3.1 ST_Buffer

-- 基本缓冲区
SELECT ST_Buffer(ST_MakePoint(0, 0), 1);

-- Geography 类型缓冲区(米为单位)
SELECT ST_Buffer(
    ST_SetSRID(ST_MakePoint(116.4, 39.9), 4326)::geography,
    1000  -- 1000米
)::geometry;

-- 缓冲区样式
-- quad_segs: 四分之一圆的段数
SELECT ST_Buffer(ST_MakePoint(0, 0), 1, 'quad_segs=2');  -- 八边形
SELECT ST_Buffer(ST_MakePoint(0, 0), 1, 'quad_segs=8');  -- 32边形(更圆)

-- 端点样式(线缓冲区)
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 0)'), 0.2, 'endcap=round');  -- 圆头
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 0)'), 0.2, 'endcap=flat');   -- 平头
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 0)'), 0.2, 'endcap=square'); -- 方头

-- 连接样式
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 1, 2 0)'), 0.2, 'join=round'); -- 圆角
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 1, 2 0)'), 0.2, 'join=mitre'); -- 尖角
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 1, 2 0)'), 0.2, 'join=bevel'); -- 斜角

-- 单侧缓冲区
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 0, 1 1)'), 0.2, 'side=left');
SELECT ST_Buffer(ST_GeomFromText('LINESTRING(0 0, 1 0, 1 1)'), 0.2, 'side=right');

-- 负缓冲区(内缩)
SELECT ST_Buffer(ST_MakeEnvelope(0, 0, 4, 4), -0.5);

-- 服务区分析
SELECT poi.name, ST_Buffer(poi.geom::geography, 1000)::geometry AS service_area
FROM poi
WHERE category = '医院';

-- 多环缓冲区
WITH buffers AS (
    SELECT generate_series(500, 2000, 500) AS radius
)
SELECT radius, ST_Buffer(ST_MakePoint(116.4, 39.9)::geography, radius)::geometry AS buffer
FROM buffers;

10.3.2 ST_OffsetCurve(偏移曲线)

-- 生成平行线(只对线有效)
SELECT ST_OffsetCurve(ST_GeomFromText('LINESTRING(0 0, 1 0, 1 1)'), 0.1);  -- 左偏移
SELECT ST_OffsetCurve(ST_GeomFromText('LINESTRING(0 0, 1 0, 1 1)'), -0.1); -- 右偏移

-- 道路中心线生成边线
SELECT 
    ST_OffsetCurve(geom, road_width / 2) AS left_edge,
    ST_OffsetCurve(geom, -road_width / 2) AS right_edge
FROM roads;

10.4 几何简化

10.4.1 ST_Simplify(道格拉斯-普克简化)

-- 基本简化
SELECT ST_NPoints(geom), ST_NPoints(ST_Simplify(geom, 0.001)) AS simplified_points
FROM complex_polygons;

-- 不同容差的效果
SELECT 
    ST_NPoints(geom) AS original,
    ST_NPoints(ST_Simplify(geom, 0.0001)) AS simplified_0001,
    ST_NPoints(ST_Simplify(geom, 0.001)) AS simplified_001,
    ST_NPoints(ST_Simplify(geom, 0.01)) AS simplified_01
FROM districts;

-- 按地图缩放级别简化
SELECT 
    CASE zoom_level
        WHEN 5 THEN ST_Simplify(geom, 0.1)
        WHEN 10 THEN ST_Simplify(geom, 0.01)
        WHEN 15 THEN ST_Simplify(geom, 0.001)
        ELSE geom
    END AS simplified_geom
FROM districts;

10.4.2 ST_SimplifyPreserveTopology

-- 保持拓扑关系的简化
SELECT ST_SimplifyPreserveTopology(geom, 0.001) FROM complex_polygons;

-- 与 ST_Simplify 的区别
-- ST_Simplify 可能产生无效几何(自相交)
-- ST_SimplifyPreserveTopology 保证结果有效

SELECT 
    ST_IsValid(ST_Simplify(geom, 0.01)) AS simplify_valid,
    ST_IsValid(ST_SimplifyPreserveTopology(geom, 0.01)) AS preserve_valid
FROM complex_polygons;

10.4.3 ST_SimplifyVW (Visvalingam-Whyatt)

-- Visvalingam-Whyatt 简化算法
-- 基于面积的简化,视觉效果通常更好
SELECT ST_SimplifyVW(geom, 0.001) FROM complex_polygons;

-- 比较不同算法
SELECT 
    'Douglas-Peucker' AS algorithm, ST_AsText(ST_Simplify(geom, 0.01))
FROM test_line
UNION ALL
SELECT 
    'Visvalingam-Whyatt', ST_AsText(ST_SimplifyVW(geom, 0.01))
FROM test_line;

10.5 几何修复

10.5.1 ST_MakeValid

-- 修复无效几何
SELECT ST_AsText(ST_MakeValid(
    ST_GeomFromText('POLYGON((0 0, 2 0, 0 2, 2 2, 0 0))')  -- 自相交的多边形
));

-- 批量修复
UPDATE spatial_data
SET geom = ST_MakeValid(geom)
WHERE NOT ST_IsValid(geom);

-- 查找并修复无效几何
SELECT id, ST_IsValidReason(geom) AS reason
FROM spatial_data
WHERE NOT ST_IsValid(geom);

10.5.2 ST_Snap(捕捉)

-- 将几何 A 捕捉到几何 B(在容差范围内)
SELECT ST_Snap(
    ST_GeomFromText('LINESTRING(0 0, 1.01 1.01)'),
    ST_GeomFromText('POINT(1 1)'),
    0.1  -- 容差
);

-- 修复拓扑间隙
UPDATE polygons p1
SET geom = ST_Snap(p1.geom, p2.geom, 0.0001)
FROM polygons p2
WHERE p1.id != p2.id
  AND ST_DWithin(p1.geom, p2.geom, 0.001);

10.5.3 ST_SnapToGrid

-- 将坐标捕捉到网格
SELECT ST_AsText(ST_SnapToGrid(
    ST_GeomFromText('POINT(1.123456 2.654321)'),
    0.01  -- 网格大小
));
-- POINT(1.12 2.65)

-- 指定原点和不同轴的网格大小
SELECT ST_SnapToGrid(geom, 0, 0, 0.001, 0.001) FROM poi;

-- 减少精度以减小数据量
UPDATE spatial_data
SET geom = ST_SnapToGrid(geom, 0.000001);

10.6 几何变换

10.6.1 ST_Translate(平移)

-- 平移几何
SELECT ST_AsText(ST_Translate(
    ST_GeomFromText('POINT(0 0)'),
    1, 2  -- dx, dy
));
-- POINT(1 2)

-- 3D 平移
SELECT ST_Translate(geom, 100, 200, 50) FROM spatial_3d;

-- 批量平移
UPDATE spatial_data
SET geom = ST_Translate(geom, offset_x, offset_y);

10.6.2 ST_Scale(缩放)

-- 相对于原点缩放
SELECT ST_AsText(ST_Scale(
    ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
    2, 2  -- x_factor, y_factor
));

-- 相对于指定点缩放
SELECT ST_Scale(
    geom,
    2, 2,
    ST_Centroid(geom)  -- 以质心为中心缩放
) FROM polygons;

-- 3D 缩放
SELECT ST_Scale(geom, 2, 2, 0.5) FROM spatial_3d;

10.6.3 ST_Rotate(旋转)

-- 相对于原点旋转(弧度)
SELECT ST_Rotate(
    ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
    pi() / 4  -- 45度
);

-- 相对于指定点旋转
SELECT ST_Rotate(
    geom,
    pi() / 2,  -- 90度
    ST_Centroid(geom)  -- 以质心为中心旋转
) FROM polygons;

-- 使用 ST_RotateX, ST_RotateY, ST_RotateZ 进行 3D 旋转
SELECT ST_RotateZ(geom, pi() / 4) FROM spatial_3d;

10.6.4 ST_Affine(仿射变换)

-- 通用仿射变换
-- ST_Affine(geom, a, b, c, d, e, f, g, h, i, xoff, yoff, zoff)
-- [a b c xoff]   [x]
-- [d e f yoff] * [y]
-- [g h i zoff]   [z]
-- [0 0 0    1]   [1]

-- 2D 仿射变换
SELECT ST_Affine(
    ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
    cos(pi()/4), -sin(pi()/4),  -- a, b
    sin(pi()/4), cos(pi()/4),   -- d, e
    10, 20                       -- xoff, yoff
);  -- 旋转45度并平移(10,20)

-- 镜像变换
SELECT ST_Affine(geom, -1, 0, 0, 1, 0, 0) FROM polygons;  -- 沿Y轴镜像
SELECT ST_Affine(geom, 1, 0, 0, -1, 0, 0) FROM polygons;  -- 沿X轴镜像

10.6.5 ST_TransScale

-- 平移和缩放的组合
SELECT ST_TransScale(
    ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))'),
    10, 20,  -- 平移 (dx, dy)
    2, 2     -- 缩放 (xfactor, yfactor)
);

10.7 高级分析

10.7.1 ST_ClusterDBSCAN

-- DBSCAN 聚类
SELECT 
    ST_ClusterDBSCAN(geom, eps := 0.01, minpoints := 3) OVER () AS cluster_id,
    *
FROM poi;

-- 分析聚类结果
WITH clusters AS (
    SELECT 
        ST_ClusterDBSCAN(geom, eps := 0.005, minpoints := 5) OVER () AS cluster_id,
        geom
    FROM poi
)
SELECT 
    cluster_id,
    COUNT(*) AS point_count,
    ST_ConvexHull(ST_Collect(geom)) AS cluster_boundary
FROM clusters
WHERE cluster_id IS NOT NULL
GROUP BY cluster_id;

10.7.2 ST_ClusterKMeans

-- K-Means 聚类
SELECT 
    ST_ClusterKMeans(geom, 5) OVER () AS cluster_id,
    *
FROM poi;

-- 计算聚类中心
WITH clusters AS (
    SELECT 
        ST_ClusterKMeans(geom, 5) OVER () AS cluster_id,
        geom
    FROM poi
)
SELECT 
    cluster_id,
    ST_Centroid(ST_Collect(geom)) AS cluster_center,
    COUNT(*) AS point_count
FROM clusters
GROUP BY cluster_id;

10.7.3 ST_Subdivide

-- 将大几何分割为小块(提高查询效率)
SELECT (ST_Dump(ST_Subdivide(geom, 256))).geom AS sub_geom
FROM large_polygons;

-- 创建分割后的表
CREATE TABLE subdivided_districts AS
SELECT id, name, (ST_Dump(ST_Subdivide(geom, 256))).geom AS geom
FROM districts;

CREATE INDEX idx_subdivided_geom ON subdivided_districts USING GIST (geom);

-- 使用分割后的表进行查询
SELECT DISTINCT d.name
FROM subdivided_districts d, poi p
WHERE ST_Intersects(d.geom, p.geom);

10.7.4 ST_VoronoiPolygons(泰森多边形)

-- 生成泰森多边形
SELECT (ST_Dump(ST_VoronoiPolygons(ST_Collect(geom)))).geom AS voronoi
FROM poi
WHERE category = '医院';

-- 带边界的泰森多边形
SELECT (ST_Dump(ST_VoronoiPolygons(
    ST_Collect(geom),
    0.0,  -- 容差
    (SELECT ST_Envelope(ST_Collect(geom)) FROM poi)  -- 边界
))).geom AS voronoi
FROM poi;

-- 服务区划分
SELECT 
    p.name AS hospital,
    ST_Intersection(v.voronoi, d.geom) AS service_area
FROM (
    SELECT id, name, geom FROM poi WHERE category = '医院'
) p,
(
    SELECT (ST_Dump(ST_VoronoiPolygons(ST_Collect(geom)))).geom AS voronoi
    FROM poi WHERE category = '医院'
) v,
districts d
WHERE ST_Intersects(p.geom, v.voronoi);

10.8 本章小结

本章详细介绍了 PostGIS 的空间分析函数:

  1. 几何叠加:ST_Intersection、ST_Union、ST_Difference
  2. 缓冲区分析:ST_Buffer、ST_OffsetCurve
  3. 几何简化:ST_Simplify、ST_SimplifyPreserveTopology
  4. 几何修复:ST_MakeValid、ST_Snap
  5. 几何变换:ST_Translate、ST_Scale、ST_Rotate
  6. 高级分析:聚类、分割、泰森多边形

10.9 下一步

在下一章中,我们将学习空间测量函数,包括:

  • 距离计算
  • 面积计算
  • 长度计算
  • 角度测量

相关资源

posted @ 2025-12-29 10:53  我才是银古  阅读(6)  评论(0)    收藏  举报