基于PostGIS拓扑进行shape面要素抽稀

      抽稀,是指减少图形的节点密度。目前在做大数据展示时,基本上都需行政区划数据将shp转geojson格式在web端进行加载渲染,如果图形节点数能达到几万个,这样的图形在渲染和空间关系判断等环节中效率会很慢,所以在一些要求不是很严格的情况下,可以使用抽稀方法减少图形的节点密度,加快处理效率。

      对于抽稀的要求,最重要的是要基本保持原图形的形状,这个大部分软件arcgis、qgis等抽稀算法都可以达到。但对于质量比较高的情况下,抽稀后相邻图形不能出现重叠和缝隙,利用上述软件很难实现。

图1原始行政区划shape数据

1、 PostGIS本身有一个函数ST_Simplify方法对要素进行抽稀的结果,可以看到比较多的重叠和缝隙:

图2 抽稀后要素之间有空隙和叠加

2、接下来咱们通过建立要素严格拓扑关系的情况下,进行抽稀方法可达到这种效果。(原理:首先将shape行政区划建立拓扑,找到相邻要素的公共边;其次在不改变拓扑关系下对公共边进行抽稀;最后将抽稀后的公共边反写到shape图层几何字段上。)

(1)创建POSTGRESQL数据库

可在已有的数据中建立一个新的架构模式,如果数据已经入库,则可以跳过该操作。

(2)创建POSTGIS和TOPOLOGY扩展

1

2

CREATE EXTENSION postgis;

CREATE EXTENSION postgis_topology;

(3)导入数据


如果处理的PostgreSQL数据库中已有数据,则跳过该操作。

1)这里使用GDAL提供的ogr2ogr命令行工具来做数据导入Shapefile格式的数据:

1

ogr2ogr -f "PostgreSQL" PG:"host=localhost port=5432 dbname=test user=postgres password=1234" /test/原始数据/xzqh.shp -lco GEOMETRY_NAME=geom -lco FID=pk_uid -nlt PROMOTE_TO_MULTI -progress

2)使用postgis自带的shape数据导入工具

3)利用Qgis的数据库管理器导入

(4)复制数据

由于处理过程中将会给数据添加字段,为了不破坏数据,将会复制一份数据进行拓扑抽稀操作。

1

create table test_bak as select * from test --test为shape数据

(5)创建拓扑(只是添加拓扑模式,并未真实空间数据的拓扑)

创建拓扑需要传入拓扑模式名称和数据的SRID,拓扑模式名称使用的是数据名称后面加上“_topo”标识,数据的SRID通过geometry_columns视图看到是4326。

1

SELECT topology.CreateTopology('test_topo', 4490); --4490为数据的srid,当然也可以用Find_SRID()函数

创建拓扑之后,在数据库中生成一个新的模式,用来存储拓扑信息:

geometry_columns中添加了相应的拓扑表信息:

 理论拓扑关系

 topo模式表存储的关系

1)dege_data

这个存储弧段信息,弧段id,弧段是分方向的,弧度方向的左弧段、右弧段,弧段左边多边形、右边多边形,弧段坐标信息等。

start_node

end_node

edge_id

next_left_edge

abs_next_left_edge

next_right_edge

abs_next_right_edge

left_face

right_face

geom

开始节点

结束节点

边id

下一个左边

绝对下一个左边

下一个右边

绝对下一个右边

左面

右面

几何信息

2)face

face_id

bmr

多边形id

多边形外包矩形坐标信息

3)node

node_id

contiaining_face

geom

节点id

包含多边形

节点坐标信息

4)relation

topogeo_id

layer_id

element_id

element_type

拓扑id,建立的拓扑的id,存储在topology表中

图层id,建立的拓扑图层的id,存储在layer表中

要素id,对应空间数据表的gid

空间数据类型

(1:[multi]point, 2:[multi]line, 3:[multi]poly, 4:collection)

注:一般安装过postgis_topology,在数据库里都会一个topology模式,里面表有layer、topology表,此表里的信息不会自动更新,如果多次生成,记得清除之前存在的信息。

layer

topology_id

layer_id

schema_name

table_name

feature_column

level

child_id

拓扑id,与topology一致

图层id

模式名称

表名

要素列明

级别

子id

topology

id

name

srid

precision

hasz

拓扑id

拓扑名称

边id

精度

(6)为空间数据添加拓扑几何字段(只是添加拓扑几何字段,未生成信息)

添加拓扑几何字段需要传入的参数是:拓扑模式名称、数据所在模式名称、数据名称、拓扑几何字段名称和几何类型。这里将拓扑几何字段添加到复制的表(test_bak)上。

1

SELECT topology.AddTopoGeometryColumn('test_topo', 'public', 'test_bak', 'topo_geom', 'MULTIPOLYGON');

select * from test_bak;

(7)对空间数据拓扑几何字段生成拓扑信息(为每个要素建立拓扑关系)

构建拓扑传入的参数是:普通几何字段名称、拓扑模式名称、layer_id(为topolopy模式layer表图层id)、数据容差(经纬度数据使用默认容差0.000000008983153,平面数据使用0.001):

1

UPDATE chn_adm1_bak SET topo_geom = topology.toTopoGeom(geom, 'test_topo', 1, 0.000000008983153);

这个处理过程和图形有关,由于空间数据节点非常密集,时间也比较长,处理完成之后:

 (8)拓扑弧段抽稀

接下来使用Sandro Santilli提供的SimplifyEdgeGeom函数进行拓扑弧段抽稀。

首先将SimplifyEdgeGeom函数添加到数据库中:

​​CREATE OR REPLACE FUNCTION SimplifyEdgeGeom(atopo varchar, anedge int, maxtolerance float8)
RETURNS float8 AS $$
DECLARE
  tol float8;
  sql varchar;
BEGIN
  tol := maxtolerance;
  LOOP
    sql := 'SELECT topology.ST_ChangeEdgeGeom(' || quote_literal(atopo) || ', ' || anedge
      || ', ST_Simplify(geom, ' || tol || ')) FROM '
      || quote_ident(atopo) || '.edge WHERE edge_id = ' || anedge;
    BEGIN
      RAISE DEBUG 'Running %', sql;
      EXECUTE sql;
      RETURN tol;
    EXCEPTION
     WHEN OTHERS THEN
      RAISE WARNING 'Simplification of edge % with tolerance % failed: %', anedge, tol, SQLERRM;
      tol := round( (tol/2.0) * 1e8 ) / 1e8; -- round to get to zero quicker
      IF tol = 0 THEN RAISE EXCEPTION '%', SQLERRM; END IF;
    END;
  END LOOP;
END
$$ LANGUAGE 'plpgsql' STABLE STRICT;

然后调用SimplifyEdgeGeom函数进行拓扑弧段抽稀,传入的参数是:拓扑模式名称、edge_id(为topo表edge里的edge字段,循环对每条记录抽稀)、抽稀容差(大地坐标系为经纬度距离:赤道上1度大约为111.3214km,1秒约为30.92米,如果想抽稀到200米一个点,约为0.001797度;投影坐标系:直接写200米):

1

SELECT SimplifyEdgeGeom('test_topo', edge_id, 0.001797) FROM test_topo.edge;

(9)将抽稀结果更新普通几何字段中

1

UPDATE test_bak SET geom = topo_geom::geometry;

(10)抽稀结果检查

通过SQL查询抽稀前后朝阳区界的节点数:

1

select ST_NumPoints(ST_ExteriorRing(ST_GeometryN(geom,1))) from bjbj where name = '朝阳'

原始数据朝阳行政边界节点数9533个

抽稀后朝阳行政边界节点数287个

1

select ST_NumPoints(ST_ExteriorRing(ST_GeometryN(geom,1))) from bjbj_bak where name = '朝阳'

通过工具查看抽稀后省界间已不存在重叠和缝隙:

基于拓扑关系抽稀后数据

(11)后续工作

如果需要将抽稀后的结果导出,可以继续使用ogr2ogr工具进行导出。

整合后语句:

create table bj_bak as select * from bjbj; --bjbj为原始空间数据,bj_为空间数据备份

SELECT topology.CreateTopology('bj_topo', 4490); --4490为空间数据坐标系

SELECT topology.AddTopoGeometryColumn('bj_topo', 'public', 'bj_bak', 'topo_geom', 'MULTIPOLYGON'); --拓扑模式、数据所在模式、空间数据、拓扑几何列名,多面

UPDATE bj_bak SET topo_geom = topology.toTopoGeom(geom, 'bj_topo', 1, 0.000000008983153); --bj_bak空间数据,空间数据几何信息列,拓扑模式、图层id,容差

SELECT SimplifyEdgeGeom('bj_topo', edge_id, 0.001797) FROM bj_topo.edge; --topo模式,edge表edge_id列名,容差

UPDATE bj_bak SET geom = topo_geom::geometry; --bj_bak空间数据

下一次再抽稀其他空间数据时应用下列语句。

DROP SCHEMA bj_topo CASCADE; --级联清除topo模式

TRUNCATE layer CASCADE; --清空建立拓扑的图层记录

TRUNCATE topology CASCADE; --清空建立的拓扑记录

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值