第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 拓扑:

  1. 基本概念:节点、边、面
  2. 创建拓扑:CreateTopology
  3. 添加要素:节点、边、面的添加
  4. TopoGeometry:拓扑几何类型
  5. 拓扑编辑:移动、删除、分割、合并
  6. 拓扑查询:节点、边、面查询
  7. 拓扑验证:ValidateTopology
  8. 实际应用:行政区划、地籍管理

13.11 下一步

在下一章中,我们将学习三维与曲线几何,包括:

  • 3D 几何支持
  • 曲线几何
  • SFCGAL 函数
  • 3D 分析

相关资源

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