前言
相信经常和地理数据打交道的小伙伴都有过标绘地理数据的经历:使用Arcgis或者Qgis创建矢量图层入库,或者修改已有的图层。基于mapbox也可以实现类似的效果。今天特地记录一下,由于笔者使用的是基于mapbox二次封装的sdk,而且业务组件很多,因此以分享思路为主,代码加以说明。
1 react中使用mapbox-gl-draw绘制
绘制多边形用到了mapbox-gl-draw这个插件,原生用法参考mapbox-gl-draw示例,在mapbox在react是一般这样子使用:
const drawRef = useRef<Draw>();
useEffect(() => {
if (!map) return;
});
drawRef.current = new MapboxDraw();
map.addControl(drawRef.current);
// drawRef.current = map.addDraw(); // 封装版
map.on('draw.create', (e) => {
// 业务代码····
);
map.on('draw.uodate', (e) => {
// 业务代码····
);
map.on('draw.delete', (e) => {
// 业务代码····
);
// 封装版
// drawRef.current.on('draw.create', (e) => {
// });
}, [map]);
当mapbox创建的地图容器变化时触发以上函数,一般map对象只有在第一次加载时才会改变。同时创建draw控件添加到map实例,每当图形创建、更新、删除时都可以取到最新绘制的元素:
在feature中存储了绘制出的geojson对象,当然也能像arcgis那样编辑属性的存储到properties,不过这属于绘制以外的功能了,不多提。绘制成功:
2 已有图层编辑
绘制成功的多边形入库后就变成了一个新的图层了,但是如果我发现已经发布的图层标绘错了想要重新编辑该如何是好呢?比如这样,我想重新编辑已有图层。
很容易想到,重新创建一个绘制后的多边形对象,让它的范围和已有图层中标绘的多边形一样,然后重新编辑更新不就可以了吗,带着这个想法,看一下这个draw控件长什么样子,果然发现有几个方法看起来就很有戏的样子:
结合官网的说明和示例可以发现,add方法就是我们需要的,只需要将目标的geojson添加给draw即可:
// editingFeatureGeoJson是从后端取到的geojson
drawRef.current.add(editingFeatureGeoJson);
目的达成,可以重新编辑了。
3 绘图控件的控制
截至目前都算比较顺利,但是需求是难以预测的,比如我现在要求在新建图形和编辑已有图层时,draw控件的绑定实现必须触发不同的函数,比如:
map.on('draw.create', (e) => {
if ('这是个新建图层'){
...
}
if ('这是入库了的图层'){
...
}
...
没办法,监听编辑态呗,两个思路,我采用的是第一种,(以下为伪代码)。
- 编辑态改变时候,重新挂载draw控件
- 编辑态改变时候,重新绑定函数
// 方法一
// 当edit改变别忘记调用一下deleteAll清除绘制中的图形,我写在别的组件函数里了
useEffect(() => {
if (!map) return;
if (drawRef.current){
// map.removeDraw();
// 卸载控件,原生mapbox中可使用removeControl
map.removeControl(drawRef.current)
drawRef.current = undefined
}
// 重新挂载到map
drawRef.current = new MapboxDraw();
map.map.addControl(drawRef.current);
// drawRef.current = map.addDraw();
map.on('draw.create', (e) => {
edit ?
func1()
:
func2()
});
}, [map, edit]);
//方法二,分享下思路,我也没用这个
useEffect(() => {
map.off('draw.create',edit ? func2 : func1)
map.on('draw.create',
edit ?
func1(e)
:
func2(e)
);
}, [edit]);
4 数据缓存造成的一个bug
问题描述
出现编辑后的新多边形在某些缩放层级下仍然显示原先的图形,在其他缩放层级下则不会。
原因
这是由于mapbox瓦片地图的后端存储所导致。由于数据较大,地理数据一般都设置缓存,当缓存方式采用cache-control时,由于缓存还未过期,它会直接请求缓存,而在之前没有展现过的某些zoom层级则没有缓存,会拿到最新的瓦片数据。因为瓦片的请求url是要在最后拼接xy行列号和zoom层级的。
解决方案
- 为自定义的图层关闭缓存。
- 采用Etag作为缓存方案,不需要单独只为某些项目控制,但是相比cache-contro的简单粗暴会稍微增加一点后端的运行压力,不过应该问题不大。
总结
以上为这两天工作的简单记录,耐心定位问题后多参考文档一般都能解决。但其实在整个过程中最耗时的是捋整体的逻辑流程,毕竟不是自己从头开发的项目,尤其是要在加需求的同时改遗留的bug,不过这也算是一种锻炼了,加油吧。