第13章-拓扑处理
第13章:拓扑处理
13.1 PostGIS 拓扑概述
PostGIS Topology 扩展提供了拓扑数据模型的支持,用于管理具有共享边界的空间数据。
13.1.1 拓扑概念
┌─────────────────────────────────────────────────────────────┐
│ 拓扑数据模型 │
├─────────────────────────────────────────────────────────────┤
│ 基本元素 │
│ ├── Node(节点):点,边的端点 │
│ ├── Edge(边):线段,连接两个节点 │
│ └── Face(面):由边围成的区域 │
│ │
│ 拓扑关系 │
│ ├── 边有起点和终点节点 │
│ ├── 边有左面和右面 │
│ └── 面由有向边环构成 │
│ │
│ 优势 │
│ ├── 共享边界只存储一次 │
│ ├── 保证数据一致性 │
│ └── 支持拓扑编辑操作 │
└─────────────────────────────────────────────────────────────┘
13.1.2 启用拓扑扩展
-- 创建拓扑扩展
CREATE EXTENSION postgis_topology;
-- 验证安装
SELECT postgis_topology_scripts_installed();
13.2 创建拓扑
13.2.1 创建拓扑模式
-- 创建拓扑(创建新的 schema)
SELECT topology.CreateTopology('my_topo', 4326, 0.00001);
-- 参数:拓扑名称, SRID, 容差
-- 查看已创建的拓扑
SELECT * FROM topology.topology;
-- 删除拓扑
SELECT topology.DropTopology('my_topo');
13.2.2 拓扑表结构
-- 创建拓扑后会自动创建以下表
-- my_topo.node - 节点表
-- my_topo.edge - 边表
-- my_topo.face - 面表
-- my_topo.relation - 拓扑几何关系表
-- 查看节点表结构
\d my_topo.node
-- node_id, containing_face, geom
-- 查看边表结构
\d my_topo.edge
-- edge_id, start_node, end_node, next_left_edge, abs_next_left_edge,
-- next_right_edge, abs_next_right_edge, left_face, right_face, geom
-- 查看面表结构
\d my_topo.face
-- face_id, mbr
13.3 添加拓扑要素
13.3.1 添加节点
-- 添加独立节点
SELECT topology.ST_AddIsoNode('my_topo', NULL, ST_Point(0, 0));
-- 参数:拓扑名称, 面ID(NULL表示宇宙面), 点几何
-- 批量添加节点
INSERT INTO my_topo.node (geom)
SELECT ST_SetSRID(ST_MakePoint(x, y), 4326)
FROM generate_series(0, 10) x, generate_series(0, 10) y;
13.3.2 添加边
-- 添加独立边(两端是现有节点)
SELECT topology.ST_AddIsoEdge('my_topo', 1, 2,
ST_MakeLine(ST_Point(0, 0), ST_Point(1, 0)));
-- 从几何自动添加边(会自动创建节点和分割现有边)
SELECT topology.TopoGeo_AddLineString('my_topo',
ST_GeomFromText('LINESTRING(0 0, 1 1, 2 0)', 4326));
-- 添加边并分割面
SELECT topology.ST_AddEdgeModFace('my_topo', 1, 2,
ST_MakeLine(ST_Point(0, 0), ST_Point(1, 0)));
-- 添加边创建新面
SELECT topology.ST_AddEdgeNewFaces('my_topo', 1, 2,
ST_MakeLine(ST_Point(0, 0), ST_Point(1, 0)));
13.3.3 从几何构建拓扑
-- 从多边形添加拓扑
SELECT topology.TopoGeo_AddPolygon('my_topo',
ST_GeomFromText('POLYGON((0 0, 1 0, 1 1, 0 1, 0 0))', 4326));
-- 从点添加拓扑
SELECT topology.TopoGeo_AddPoint('my_topo',
ST_GeomFromText('POINT(0.5 0.5)', 4326));
-- 从线添加拓扑
SELECT topology.TopoGeo_AddLineString('my_topo',
ST_GeomFromText('LINESTRING(0 0, 2 2)', 4326));
-- 批量从几何加载
SELECT topology.TopoGeo_AddPolygon('my_topo', geom)
FROM districts;
13.4 拓扑几何类型
13.4.1 创建 TopoGeometry 列
-- 创建带拓扑几何的表
CREATE TABLE land_parcels (
id SERIAL PRIMARY KEY,
name VARCHAR(100)
);
-- 添加拓扑几何列
SELECT topology.AddTopoGeometryColumn('my_topo', 'public', 'land_parcels', 'topo_geom', 'POLYGON');
-- 查看注册的拓扑几何列
SELECT * FROM topology.layer;
13.4.2 创建 TopoGeometry
-- 从几何创建拓扑几何
INSERT INTO land_parcels (name, topo_geom)
SELECT name, topology.toTopoGeom(geom, 'my_topo', 1)
FROM source_polygons;
-- 参数:几何, 拓扑名称, layer_id
-- 从拓扑元素创建
INSERT INTO land_parcels (name, topo_geom)
VALUES ('Parcel A', topology.CreateTopoGeom('my_topo', 3, 1,
ARRAY[[1, 3]]::topology.TopoElementArray));
-- 参数:拓扑名称, 类型(1=点,2=线,3=面), layer_id, 元素数组
-- 获取拓扑几何的几何表示
SELECT name, topo_geom::geometry AS geom FROM land_parcels;
13.4.3 TopoGeometry 类型
| 类型值 | 说明 |
|---|---|
| 1 | 点类型 (POINT/MULTIPOINT) |
| 2 | 线类型 (LINESTRING/MULTILINESTRING) |
| 3 | 面类型 (POLYGON/MULTIPOLYGON) |
| 4 | 集合类型 (GEOMETRYCOLLECTION) |
13.5 拓扑编辑
13.5.1 节点编辑
-- 移动节点
SELECT topology.ST_MoveIsoNode('my_topo', 1, ST_Point(0.5, 0.5));
-- 删除孤立节点
SELECT topology.ST_RemIsoNode('my_topo', 1);
-- 改变边的节点
SELECT topology.ST_ModEdgeSplit('my_topo', 1, ST_Point(0.5, 0.5));
13.5.2 边编辑
-- 移动边
SELECT topology.ST_ChangeEdgeGeom('my_topo', 1,
ST_GeomFromText('LINESTRING(0 0, 0.5 0.5, 1 0)', 4326));
-- 删除边
SELECT topology.ST_RemEdgeModFace('my_topo', 1);
-- 分割边
SELECT topology.ST_ModEdgeSplit('my_topo', 1, ST_Point(0.5, 0));
-- 合并边
SELECT topology.ST_ModEdgeHeal('my_topo', 1, 2);
-- 创建边
SELECT topology.ST_AddEdgeModFace('my_topo', 1, 2,
ST_MakeLine(ST_Point(0, 0), ST_Point(1, 0)));
13.5.3 面编辑
-- 获取面的边界
SELECT topology.ST_GetFaceGeometry('my_topo', 1);
-- 获取面的边
SELECT topology.ST_GetFaceEdges('my_topo', 1);
-- 添加面孔洞
-- 通过添加内环边实现
13.6 拓扑查询
13.6.1 查询节点
-- 获取所有节点
SELECT node_id, ST_AsText(geom) FROM my_topo.node;
-- 查询特定位置的节点
SELECT node_id FROM my_topo.node
WHERE ST_DWithin(geom, ST_Point(0, 0), 0.01);
-- 获取边的起点和终点
SELECT edge_id, start_node, end_node FROM my_topo.edge;
13.6.2 查询边
-- 获取所有边
SELECT edge_id, ST_AsText(geom) FROM my_topo.edge;
-- 获取面的边界边
SELECT e.edge_id, e.left_face, e.right_face, ST_AsText(e.geom)
FROM my_topo.edge e
WHERE e.left_face = 1 OR e.right_face = 1;
-- 获取两个面的共享边
SELECT e.edge_id
FROM my_topo.edge e
WHERE (e.left_face = 1 AND e.right_face = 2)
OR (e.left_face = 2 AND e.right_face = 1);
13.6.3 查询面
-- 获取所有面
SELECT face_id, mbr FROM my_topo.face;
-- 获取面的几何
SELECT face_id, topology.ST_GetFaceGeometry('my_topo', face_id)
FROM my_topo.face
WHERE face_id > 0; -- 排除宇宙面
-- 获取与几何相交的面
SELECT face_id
FROM my_topo.face
WHERE mbr && ST_MakeEnvelope(0, 0, 1, 1);
13.7 拓扑验证
13.7.1 验证拓扑
-- 验证整个拓扑
SELECT topology.ValidateTopology('my_topo');
-- 返回错误列表
SELECT * FROM topology.ValidateTopology('my_topo');
-- 返回: error, id1, id2
-- 常见错误类型
-- edge crosses node
-- edge not simple
-- edge end node geometry mis-match
-- face has no rings
13.7.2 修复拓扑错误
-- 重新生成面
SELECT topology.Polygonize('my_topo');
-- 修复边几何
UPDATE my_topo.edge
SET geom = ST_SnapToGrid(geom, 0.00001)
WHERE edge_id = 1;
-- 删除重复节点
DELETE FROM my_topo.node n1
USING my_topo.node n2
WHERE n1.node_id > n2.node_id
AND ST_DWithin(n1.geom, n2.geom, 0.00001);
13.8 拓扑与几何转换
13.8.1 TopoGeometry 转 Geometry
-- 转换单个拓扑几何
SELECT topo_geom::geometry FROM land_parcels WHERE id = 1;
-- 使用 GetTopoGeom 函数
SELECT topology.GetTopoGeometry(topo_geom) FROM land_parcels;
-- 批量转换
SELECT id, name, topo_geom::geometry AS geom
FROM land_parcels;
13.8.2 Geometry 转 TopoGeometry
-- 转换几何到拓扑几何
UPDATE land_parcels
SET topo_geom = topology.toTopoGeom(new_geom, 'my_topo', 1)
WHERE id = 1;
-- 使用容差
SELECT topology.toTopoGeom(geom, 'my_topo', 1, 0.00001)
FROM source_table;
13.9 实际应用
13.9.1 行政区划管理
-- 创建行政区拓扑
SELECT topology.CreateTopology('admin_topo', 4326, 0.00001);
-- 创建行政区表
CREATE TABLE admin_areas (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
level INT -- 1=省, 2=市, 3=区
);
SELECT topology.AddTopoGeometryColumn('admin_topo', 'public', 'admin_areas', 'topo_geom', 'POLYGON');
-- 导入数据
INSERT INTO admin_areas (name, level, topo_geom)
SELECT name, level, topology.toTopoGeom(geom, 'admin_topo', 1)
FROM source_admin_areas;
-- 合并相邻行政区
UPDATE admin_areas
SET topo_geom = topology.ST_ModEdgeSplit(...)
WHERE ...;
13.9.2 地籍管理
-- 创建地籍拓扑
SELECT topology.CreateTopology('cadastre_topo', 4527, 0.001);
-- 分割地块
SELECT topology.ST_AddEdgeNewFaces('cadastre_topo',
start_node, end_node, split_line);
-- 合并地块
SELECT topology.ST_RemEdgeModFace('cadastre_topo', shared_edge_id);
13.10 本章小结
本章详细介绍了 PostGIS 拓扑:
- 基本概念:节点、边、面
- 创建拓扑:CreateTopology
- 添加要素:节点、边、面的添加
- TopoGeometry:拓扑几何类型
- 拓扑编辑:移动、删除、分割、合并
- 拓扑查询:节点、边、面查询
- 拓扑验证:ValidateTopology
- 实际应用:行政区划、地籍管理
13.11 下一步
在下一章中,我们将学习三维与曲线几何,包括:
- 3D 几何支持
- 曲线几何
- SFCGAL 函数
- 3D 分析
相关资源:

浙公网安备 33010602011771号