这是我在掘金上更新的第一篇文章,新手上路,还请多多指教。
一、结局
首先把结局写在前面,毕竟读者也不希望读了我的文章,花了精力做好了加载却不满足自己的实际业务需求吧。
结论如下:MVT格式由postGIS动态生成时,在数据量过大时仍会带来明显的加载卡顿。
解决办法:改用wms服务发布postgre中的数据,更新数据直接更新数据库里的,同时解决了加载效率和实时更新问题
二、背景
最近在业务上遇到了数十万量级的地理图形数据加载的需求,面对这样的需求,我抬头望向了我的好兄弟:geojson传输方式,发现他在墙角瑟瑟发抖,在我的一番强迫下,他跑出了接口超时的好成绩。
行吧,既然geojson行不通,我们还是老老实实另请高明吧。这个业务里,首先最明确的一个特点就是数据量偏大 ,针对这一特点,出现在我脑海里的第一想法就是切片 ,将完整的地理数据改为由多个瓦片的形式一部分一部分的加载可以大大提升加载效率。回顾以往业务,最为常用的瓦片地图加载方式就是WMTS,可这让我犯难了,因为这次的业务除了数据量的问题外,还有一个重要的需求:实时修改更新数据,使用WMTS加载确实快了,可这每次更新完数据之后都要用工具重新切片,重新发布,这可咋整?差点忘了WMTS还有个好兄弟WMS,WMS较WMTS来说,从预先生成瓦片改为了每次请求时才生成图片,虽然慢了些,但胜在灵活,只需要修改发布后的数据源就可以实现数据的更新。
WMS失败了
(尝试过程中最接近正确答案的一集)
由于WMS在GIS中太常见了,这里对于其加载方式不做过多的赘述,只记得我请来了WMS,递给他一份shapeFile(划重点!),让他发布一下咱测试测试性能,只见他看着我,沉思1.5s后,递给了我一张图片,接着又过了2.33s,递给我了下一张图片,此刻我的控制台里已经堆满了排队中的请求,为了服务器压力和我的心理压力着想,我只好关闭了应用。
三、正片开始
网上一番研究,我发现了MVT。仔细一看,又是矢量,又是切片,这可不就是我要的东西吗,赶紧动手。
什么是MVT
MVT是由Mapbox定义的一种矢量瓦片标准。
以我粗浅的理解,MVT就和他的名字一样,矢量瓦片,将矢量制作成瓦片,在兼顾矢量的特点的前提下,同时拥抱瓦片加载的能力。矢量瓦片生成后是一个.mvt格式的文件,前端根据一定的规则来进行地图展示。
方案
基于我们需要更新数据的需求前提下,我们选择了postGIS动态生成MVT的方式,而前端则采用了openlayers进行MVT的加载与展示。
postGIS生成MVT
postGIS听了我们的方案,掏出两个函数,ST_AsMVTGeom 、ST_AsMVT
"得此二者,可得天下"
ST_AsMVTGeom用于将Geometry字段转成MVTGeom的格式,官网文档参考如下:
geometry **ST_AsMVTGeom**(geometry geom, box2d bounds, integer extent=4096, integer buffer=256, boolean clip_geom=true);
- geom:待转换的geometry,也就是数据库存储的地理数据
- bounds:此次生成的这片mvt瓦片的矩形边界,切片的zxy参数就靠这里接收了,这里使用这个方法来生成
ST_TileEnvelope({z}, {x}, {y})
- extent:瓦片范围大小,默认为4096
- buffer:瓦片缓冲区距离(这个参数和下面那个参数有直接关联)
- clip_geom:处于瓦片缓冲区部分的图形是被裁剪掉还是直接加在这片瓦片里,默认为减裁掉
ST_AsMVT的职责是将查询出来的包含MVTGeom的结果转换为MVT文件,参考如下:
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);
- row:包含geometry属性字段的行,也就是我们上面经过ST_AsMVTGeom的表格内容
- name:图层名
- extent:同上
- geom_name:表格内有多个geometry的情况下,指定用于生成MVT的字段
- feature_id_name:生成的feature的id字段名
sql
SELECT ST_AsMVT(tiletable.*, 'gb_title', 4096) as tile FROM
(SELECT id, ST_AsMVTGeom(ST_Transform(ST_setSRID(geomData, 4326), 3857), ST_Transform(ST_TileEnvelope({z}, {x}, {y}), 3857), 4096) as geom from
(select * from myTable) ta) as tiletable where tiletable.geom is not null;
前端加载
拿到了上面postGIS生成的数据后,我将他拿到了前端来,前端openlayers官网很贴心的送来了一个示例:Mapbox Vector Tiles (openlayers.org)
于是我们依葫芦画瓢:
jsx
import MVT from 'ol/format/MVT.js';
import Map from 'ol/Map.js';
import VectorTileLayer from 'ol/layer/VectorTile.js';
import VectorTileSource from 'ol/source/VectorTile.js';
import View from 'ol/View.js';
import {Fill, Icon, Stroke, Style, Text} from 'ol/style.js';
const map = new Map({
layers: [
new VectorTileLayer({
declutter: true,
source: new VectorTileSource({
format: new MVT(),
url:
'你的服务地址'
}),
style: new Style(),//你的自定义style
}),
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2,
}),
});
启动!可以看到数据已经出现在地图上了,MVT加载成功。
PS:为什么没有后端服务部分
因为这段部分代码不是我写的,我也不好意思来献丑了。
四、尾声
好慢,下一个
又完成了一个需求,稳了稳了,让我浏览一下看看效果吧。只见MVT眉头一皱,沉思1.5s后,递给了我一个矢量数据,接着又过了2.33s,递给我了下一个矢量数据。这似曾相识的感觉是怎么回事?
还得是WMS,但是数据库
仔细研究一番我们的gisserver,硬着头皮还是上了WMS,毕竟加载起来老简单了,也不用后端老哥忙里忙外,等下,不卡了是怎么回事???网上多番查找,原来WMS通过shapefile加载和通过数据库加载有明显的性能区别,之前想着测试就给了shapefile,而我们数据本来就是放postgre里的。
我陷入了沉默。。。
彳亍