原 使用ST_AsGeobuf配合geobuf解决geojson过大的问题
版权声明:本文为博主原创文章,请尊重他人的劳动成果,转载请附上原文出处链接和本声明。
本文链接:https://www.91mszl.com/zhangwuji/article/details/1331
方案一:使用抽稀函数 st_simplify,但是抽稀完后相邻的2个区域之间会出现空隙,如下图所示,产品说这是决定不允许的,只能放弃这种方案。
大家如果对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/geobuf4.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