mapbox-gl图形绘制并编辑已有图层


前言

相信经常和地理数据打交道的小伙伴都有过标绘地理数据的经历:使用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,不过这也算是一种锻炼了,加油吧。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
Mapbox-GL-Draw是一款基于Mapbox GL JS的开源绘图库。它提供了一系列的绘图工具,可以在地图上绘制点、线、面等要素。同时,它也支持自定义绘图控件,可以满足不同的绘图需求。 要绘制箭头控件,可以使用Mapbox-GL-Draw的自定义绘图控件功能。具体步骤如下: 1. 创建一个绘图控件,继承自`mapboxgl.Draw`: ```javascript var ArrowControl = mapboxgl.Draw.extend({ // ... }); ``` 2. 在控件的`onAdd`方法中创建绘图工具,并添加箭头绘制模式: ```javascript ArrowControl.prototype.onAdd = function(map) { // ... this.addMode('arrow', ArrowMode); // ... } ``` 3. 定义箭头绘制模式: ```javascript var ArrowMode = { // ... onSetup: function() { // ... }, onDrag: function() { // ... }, onClick: function() { // ... }, // ... }; ``` 4. 在`onSetup`方法中创建箭头要素,并添加到地图上: ```javascript onSetup: function() { var arrow = { type: 'Feature', geometry: { type: 'LineString', coordinates: [] }, properties: { 'arrow': 'true' } }; this.addFeature(arrow); this.arrow = arrow; // ... }, ``` 5. 在`onDrag`方法中更新箭头要素的坐标: ```javascript onDrag: function(event) { var arrow = this.arrow; var coords = arrow.geometry.coordinates; if (coords.length === 0) { coords.push(event.lngLat.toArray()); coords.push(event.lngLat.toArray()); this.updateUIClasses({mouse: 'add'}); } else { coords[1] = event.lngLat.toArray(); this.updateUIClasses({mouse: 'mousemove'}); } arrow.geometry.coordinates = coords; this.map.fire('draw.update', {action: 'change', feature: arrow}); }, ``` 6. 在`onClick`方法中结束绘制: ```javascript onClick: function(event) { var arrow = this.arrow; var coords = arrow.geometry.coordinates; if (coords.length === 2) { this.updateUIClasses({mouse: 'pointer'}); this.changeMode('simple_select', {featureIds: [arrow.id]}); } }, ``` 7. 最后,在地图上添加箭头控件: ```javascript map.addControl(new ArrowControl()); ``` 以上就是绘制箭头控件的基本流程,具体实现可以参考Mapbox-GL-Draw的官方文档。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值