Bootstrap

关于mapbox 中拖拽point,line ,polygon实现原理

最近闲来无事开始对mapbox-gl-draw.js工具做了一些剖析,在绘制完一个line或者polygon 和point 鼠标按下移动鼠标可以改变图层位置。当时我就在想,他是怎样实现的。所以了解源码之后我的小小结论就出来。
怎样让一个line图层或者polygon图层跟着你的鼠标移动呢?这里point就不作探讨了,mapbox官网有相关案例。一看就明白那种,所以我们就重点讨论怎样实现line或者polygon的拖动。
首先要理解line 和polygon的拖动,最基本要理解数学二维直角坐标系和三角形相关的知识和概念。在直角坐标系中,知道一点(x,y)和夹角 和一条边的长度就可以计算出未知的目标点,如图所示
在这里插入图片描述
所以在mapbox中移动图层就是运用这样的数学逻辑,那怎样去拖拽line或polygon改变其位置呢。所以你就要理解line或者polygon在概念和数据组织上是一个个point组成的 ,所以通过已知图层数据到鼠标点击或者鼠标按下的坐标点,就可以计算出该鼠标点到各个点间的距离和夹角。那么是不是再进行鼠标移动过程中,就可以通过原始各个点的距离和夹角来计算出移动后的各个点的坐标呢。所以梳理原理之后,那么就开始实现,以下是在vue2中对线的实现代码

    /**
     * 
     * 拖动原理的实现
     * ① 首先实现需要三个个鼠标事件  mousedown   , mousemove ,mouseup,还有turf.js(that.$turf)工具(辅助计算长度和距离),其实这里我有一个大胆想法不用turf直接把放在二维直角坐标系中使用三角公式一样可以做
     *    1:mousedown 用来计算初始layer 各个点到鼠标点的距离和夹角,这是在后续拖动图层可以计算新的各个点的坐标
     *    2:mousemove 使用拖动的坐标点来计算各个点的坐标
     *    3: mouseup   结束拖动  , 关闭鼠标事件  
     * 
     */
    drewDrag(key){
      let that = this;
      that.index = key;
      let layerId = 'drew_line'
      //添加鼠标按下事件
      that.map.on('mousedown',layerId, mousedownEvent);
      //添加触控启动事件
      that.map.on('touchstart', layerId,touchstartEvent)
    
      function mousemoveEvent(e) {
        let coords =[e.lngLat.lng,e.lngLat.lat] //当前坐标 

        //根据点,距离和角度计算目标点
        that.json_line.features.forEach((_e)=>{
          var s_destination = that.$turf.destination(
          that.$turf.point([e.lngLat.lng,e.lngLat.lat]), 
          _e.properties.distance[0], 
          _e.properties.angle[0], {units: 'degrees'});
          console.log(s_destination)
          var e_destination = that.$turf.destination(
          that.$turf.point([e.lngLat.lng,e.lngLat.lat]), 
          _e.properties.distance[1], 
          _e.properties.angle[1], {units: 'degrees'});
          console.log(e_destination)
          _e.geometry.coordinates=[s_destination.geometry.coordinates,e_destination.geometry.coordinates]
        })
        that.map.getSource('drew_line').setData(that.json_line)
        

        console.log(e+coords);
        
      }

      function onUp(e) {
        that.map.off('mousemove', mousemoveEvent);
        that.map.off('touchmove', mousemoveEvent);
        that.map.off('mousedown', mousedownEvent);
        that.map.off('touchstart', touchstartEvent);
        that.map.off('touchend', onUp);
        console.log(e)
      }

      function mousedownEvent(e) {        
        e.preventDefault();//防止默认地图拖动行为。
        //使用turf 计算各个坐标点的长度 和角度
        let data = that.map.getSource('drew_line')._data.features
        console.log(data)
        that.json_line_point.features = []
        that.json_line.features = []
        data.forEach((_e,index,row)=>{
          console.log(index+row)
          //计算距离
          var start_distance = that.$turf.rhumbDistance(
            that.$turf.point([e.lngLat.lng,e.lngLat.lat]), 
            that.$turf.point(_e.properties.start), {units: 'degrees'}); 
          var end_distance = that.$turf.rhumbDistance(
            that.$turf.point([e.lngLat.lng,e.lngLat.lat]), 
            that.$turf.point(_e.properties.end), {units: 'degrees'}); 
          console.log(start_distance+"--"+end_distance)
          _e.properties.distance[0] = start_distance  
          _e.properties.distance[1] = end_distance

          // 计算两点间的夹角
          var s_bearing = that.$turf.bearing(that.$turf.point([e.lngLat.lng,e.lngLat.lat]), that.$turf.point(_e.properties.start));
          var e_bearing = that.$turf.bearing(that.$turf.point([e.lngLat.lng,e.lngLat.lat]), _e.properties.end);
          _e.properties.angle[0]=s_bearing
          _e.properties.angle[1]=e_bearing
          that.json_line.features.push({
            type: _e.type,
            geometry:_e.geometry,
            properties:_e.properties
          });     
        })


        var feature = e.features[0];
        that.map.setFilter('pointlayerhighlight', ['in', 'id', feature.properties.id]);
        //鼠标移动
        that.map.on('mousemove',mousemoveEvent);
        //鼠标抬起
        that.map.once('mouseup', onUp);
        console.log(e);
      }

      function touchstartEvent(e){
        //防止默认地图拖动行为
        e.preventDefault();
        //触控移动
        that.map.on('touchmove', mousemoveEvent);
        //触控端起
        that.map.once('touchend', onUp);
        console.log(e)
      }
    }

在这里插入图片描述

以上是实现line的拖拽,polygon的拖拽原理差不多,当然可以直接使用turf.js 提供的工具实现这样的效果。想看完整案例,请移步手术台 案例

;