91名师指路-头部
91名师指路

使用ST_AsGeobuf配合geobuf解决geojson过大的问题

由于某些原因,现在不支持支付宝支付,如需要购买源码请加博主微信进行购买,微信号:13248254750

背景介绍:

我们公司的业务场景是几个区域进行调整,可以增大也可以缩小,拿上海举例,上海市包含长宁区,黄浦区,静安区,徐汇区,嘉定区,宝山区等等,我们可以人为的将长宁区和黄浦区合并成一个区域,这样合并后geojson就会变得很大,一般在20M左右,随着不断的合并区域可能会达到50M - 100M 甚至更多,导致前端调后端接口经常超过30s,这是我们人类无法忍受的,为了解决这个问题,我尝试了多种方法,下面一个个的来说明。最终采用方案四来实现。请大家根据自己的需求来选择合理的方案。我在解决问题时,也有很多热心的网友帮忙,这里将我遇到的问题和解决方案一一整理出来,希望能帮助到各位,赠人玫瑰,收有余香。


方案一:使用抽稀函数 st_simplify,但是抽稀完后相邻的2个区域之间会出现空隙,如下图所示,产品说这是决定不允许的,只能放弃这种方案。


方案二:同事说采用Visvalingam-Whyatt 算法来解决抽稀导致的空隙问题,就是说,抽稀完后有空隙,可以通过这个算法来将这些空隙给填补上,我个人没试验过,我个人觉得不是很好,虽然说是抽稀,但是也不能抽太多,只要边界足够大,还是不能解决geojson 过大的问题。

大家如果对Visvalingam-Whyatt 算法感兴趣的话可以了解下:https://zhuanlan.zhihu.com/p/355323735


方案三:采用ST_AsMVT,前端使用MapBox来进行解析。

3.1)ST_AsMVT:聚合函数用于将基于MapBox VectorTile坐标空间的几何图形转换为MapBox VectorTile二进制矢量切片。

bytea ST_AsMVT(anyelement set row);

bytea ST_AsMVT(anyelement row, text name);

bytea ST_AsMVT(anyelement row, text name, integer extent);

bytea ST_AsMVT(anyelement row, text name, integer extent, text geom_name);

bytea ST_AsMVT(anyelement row, text name, integer extent, text geom_name, text feature_id_name);

参数解释:

1)row:至少包含一个geometry列。

2)name:图层的名称。默认是字符串“default”。

3)extent:由MVT规范定义的屏幕空间 (MVT坐标空间) 中的矢量切片范围。

4)geom_name:geometry列的数据,默认是第一个geometry类型的列。

5)feature_id_name:是行数据中要素 ID 列的名称。如果为 NULL 或负数,则未设置功能 ID。匹配名称和有效类型(smallint、integer、bigint)的第一列将用作特征 ID,任何后续列将作为属性添加。不支持 JSON 属性。


更详细的解释可以见官网,我这里翻译的可能不是特别准确,官网地址:http://postgis.net/docs/ST_AsMVT.html

官网截图如下:


3.2)ST_AsMVTGeom:将几何图形转换为与图层对应的一组行的Mapbox Vector Tile的坐标空间。尽最大努力保持甚至更正有效性,并可能在此过程中将几何折叠成较低的维度。

ST_AsMVTGeom(geometry geom, box2d bounds, integer extent=4096, integer buffer=256, boolean clip_geom=true)

参数解释:

1)geom:需要转化的geometry字段。

2)bounds:不带缓冲区的几何边界

3)extent:按照规范定义的平铺坐标空间中的平铺范围。如果为NULL, 默认为4096

4)buffer:平铺坐标空间中任意修剪几何图形的缓冲区距离, 如果为NULL, 默认为256

5)clip_geom:是一个boolean值,用于控制几何图形是否应按原样裁剪或编码。如果为NULL, 默认为true


更详细的解释可以见官网,我这里翻译的可能不是特别准确,官网地址:http://postgis.net/docs/ST_AsMVTGeom.html

官网截图如下:


3.3)我们了解了ST_AsMVT和ST_AsMVTGeom函数的用法,接下来我们将项目下载下来进行改造。

ST_AsMVT + MapBox项目下载地址:https://github.com/zhaoquanfeng064/vector-title

3.1.1)更改数据库连接地址。

3.1.2)在mapbox官网申请token。在html页面会用到,如下图所示。

在mapbox官网申请token:http://www.mapbox.cn/mapbox-gl-js/api/#accesstoken

token用到的地方:


3.1.3)下载下来的pom.xml有错,需要加上<plugin>标签


3.1.4)  我们访问页面看到地图是英文的,我们需要先汉化它。

汉化方法:

1)添加js

<script src='https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-language/v0.10.0/mapbox-gl-language.js'></script>

2) 添加js 语句块。

mapboxgl.setRTLTextPlugin(
"https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-rtl-text/v0.1.0/mapbox-gl-rtl-text.js"
);
map.addControl(
new MapboxLanguage({
defaultLanguage: "zh"
})
);

汉化后的效果图:

3.1.5)创建表

CREATE TABLE "retail"."vector_tile" (
"id" int4 NOT NULL,
"name" varchar(32) COLLATE "pg_catalog"."default",
"geom" "public"."geometry",
"precinct_color" varchar(30) COLLATE "pg_catalog"."default"
)
;

-- ----------------------------
-- Primary Key structure for table vector_tile
-- ----------------------------
ALTER TABLE "retail"."vector_tile" ADD CONSTRAINT "vector_tile_pkey" PRIMARY KEY ("id");

表里面插入了2条sql,2个边界效果图如下:


访问页面的效果图如下所示:

区域一和区域二其实是不同的颜色,我们根据数据库里面 precinct_color 来显示对应的颜色。

1)在sql添加precinct_color

2)在页面添加,我们将颜色穷举出来,我们一共只有这几种颜色,当然了你也可以通过调接口来获取颜色,将颜色放到配置表里面,然后在查询出来。


我们再次访问页面,效果图如下所示,现在的效果就和我们存在数据库的效果是一模一样的了。非常完美。


3.1.6)设置中心点

1)我们在页面上看到mapbox需要设置一个中心点,这个点是通过ST_Centroid 或 ST_extent函数来获取


ST_Centroid 语法如下

select ST_Centroid(geom) from retail.vector_tile

ST_extent 语法如下

select ST_extent(geom) from retail.vector_tile


3.1.7)测试性能

1)vector_tile表中有8条数据,这8条数据加起来的geom大小为63M,我们单纯的从postgresql中查询需要6.6s

2)我们访问项目,看geom在页面上展示出来需要多少秒。测试了多次发现基本稳定在2s以内,我们可以通过下面的请求发现mapbox是分批加载来展示geom,没有用mapbox之前加载出来,需要30s左右,用了mapbox之后2s左右就可以展示出来了,效果非常的明显,但是我们最终还是没有采用这个方案,因为我们项目是基于高德地图做的,如果替换成mapbox改动非常的大,虽然效果非常的好,但是我们最后还是放弃了这种方案。


方案四:采用ST_AsGeobuf,前端采用geobuf进行解析。

4.1)上面走了一大堆的弯路后,决定开始研究高德地图的api,看看是否有类似mapbox支持pbf格式。翻遍了高德地图的api,发现WMTS和XYZ栅格图层 貌似可以满足我的需求。

我们点进去看demo,发现WMTS和XYZ栅格图层都是将geom转化成图片来展示,这样会有比较明显的2个问题:

问题一:如果把geom转化成图片,图片被误删,硬盘损坏,或程序bug导致图片转换不完整都将导致最终的geom在页面上展示不完整。

问题二:图片被放大后,会导致失真。

到这里我的心态几乎要崩溃了,心中一万头草泥马飘过。

WMTS


XYZ栅格图层


有网友给我提供了一个解决方案,使用geobuf,github地址如下:

https://github.com/mapbox/geobuf

github介绍如下,大意是采用geobuf可以使geojson缩小6 - 8倍,即使是采用gzip压缩了,也比它小 2 - 2.5倍,能够非常快速的压缩和解压缩,其实geobuf的作用是在数据库对geojson进行压缩,然后在前端进行解压缩,来减少网络的传输。看介绍貌似就是我想要的效果。



4.2)申请高德地图token

我们使用的是高德地图,如果你是第一次使用高德地图,需要到高德开放平台去申请token:https://lbs.amap.com/

4.3) 在页面引入相关的js和css

<link rel="stylesheet" href="https://a.amap.com/jsapi_demos/static/demo-center/css/demo-center.css" /><!-- 高德 -->
<script src="https://webapi.amap.com/maps?v=1.4.15&key=6b1e381ceedca029225c07e9247c5823"></script><!-- 高德 -->
<script src="https://unpkg.com/geobuf@3.0.2/dist/geobuf.js"></script><!-- geobuf -->
<script src="https://unpkg.com/geobuf@3.0.2/dist/geobuf-dev.js"></script><!-- geobuf -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script><!-- axios -->
<script src="https://unpkg.com/pbf@3.0.5/dist/pbf.js"></script><!-- pbf -->

相关的代码,这个可以参考高德地图的api

axios({
url: 'http://localhost:8050/zgis/vector/geobuf',
method: 'get',
data:{},
responseType: 'arraybuffer'
})
.then(response => {
var geojson = geobuf.decode(new Pbf(new Uint8Array(response.data)));
var json = new AMap.GeoJSON({
geoJSON: geojson,
// 还可以自定义getMarker和getPolyline
getPolygon: function(json, lnglats) {
return new AMap.Polygon({
path: lnglats,
fillOpacity: 0.3,
fillColor: json.properties.precinct_color
});
}
});
map.add(json);
console.log('GeoJSON 数据加载完成');
})

最终的效果图:


备注:数据库里面存的geom一共是63M,传输到前端页面是4.1M,展示出来大概2s以内,然后页面解析出来是47.1M,效果非常的满意。


传输到前端页面4.1M, 2s内展示出来。


前端解压出来是47.1M

4.4 )完整的项目下载地址:见文章最后的 下载源码 按钮。如果你觉得这篇文章帮助到你了,可以点击下载源码给作者晚餐加一个鸡腿。

1)说明:里面包含mapbox和geobuf,还包含63M的geom和176k的geom测试数据。

项目截图:



最后感谢很多热心的网友,虽然素未谋面,但是非常热心的帮助。

感谢QQ群:730425145 里面的:小刘先森, 彩虹马, polong, 孙菱志

感谢钉钉群:Ganos时空云计算,里面的图贲,Harry,焕智,向王,李卓

没有他们的帮助,不可能这么顺利,写这篇文章希望能给各位it同仁添砖加瓦。


参考资料:

https://blog.csdn.net/qq_18298439/article/details/91491771

https://blog.csdn.net/zhaoquanfeng/article/details/81874270

https://blog.csdn.net/terrychinaz/article/details/113736579



2021-06-22 13:40:09     阅读(2834)

名师出品,必属精品    https://www.91mszl.com

用户登录遮罩层
x

账号登录

91名师指路-底部