mapbox测距功能重写

// 使用
import MeatureTool from "@/components/webgisMap/measureTool";

    measureDistance() {
      // ID可以自定义
      const layerId = String(new Date().getTime())
      this.meatureTool = new MeatureTool(this.mapBoxMap)
      this.meatureTool.measureDistance(layerId)
      // 防止函数冲突
      this.meatureTool.setDistance()
    },

// measureTool.js
    
// import mapboxgl from '@/third/maplibre-gl/maplibre-gl.js'
import mapboxgl from "mapbox-gl";
// import '@mapbox/mapbox-gl-draw/dist/mapbox-gl-draw.css'
import "mapbox-gl/dist/mapbox-gl.css";
// import * as turf from '@turf/turf'
import * as turf from '@/components/webgisMap/turf.min.js';
import './measureIcon.css'
import pointMark from '@/assets/mapboxgis/icon_online.png'
export default class MeatureTool {
  map = void 0
  sdMap = void 0
  constructor(sdMap) {
    this.sdMap = sdMap
    this.map = sdMap
  }

  layerDistanceList = []
  layerAreaList = []
  setDistance = () => { }
  setArea = () => { }

  /**
   * 测量距离
   * @param {*} layerId
   */
  measureDistance(layerId) {
    this.layerDistanceList.push(layerId)
    var isMeasure = true
    const map = this.map
    const sdMap = this.sdMap
    map.doubleClickZoom.disable()
    let catchMark = null
    let isEdit = false
    let Geolist = []
    let dragPointOrder = 0
    let pointOnLine = [0, 0]

    const jsonPoint = {
      type: 'FeatureCollection',
      features: [],
    }
    const jsonLine = {
      type: 'FeatureCollection',
      features: [],
    }

    // 添加测量结果弹窗
    const ele = document.createElement('div')
    ele.setAttribute('class', 'measure-move')
    ele.setAttribute('style', 'border:1px solid #f00;padding:2px 5px 32px 5px;')
    const option = {
      element: ele,
      anchor: 'left',
      offset: [8, 0],
    }
    const tooltip = new mapboxgl.Marker(option).setLngLat([0, 0]).addTo(map)

    const eleM = document.createElement('div')
    eleM.setAttribute('style', 'border:1px solid #f00;padding:2px 5px 17px 5px;')
    eleM.setAttribute('class', 'measure-move')
    const optionM = {
      element: eleM,
      anchor: 'left',
      offset: [8, 30],
    }
    eleM.innerHTML = '单击确定起点';
    let moveTip = new mapboxgl.Marker(optionM).setLngLat([0, 0]).addTo(map)
    let hoverTip = null;

    // 添加测量图层
    map.addSource('points' + layerId, {
      type: 'geojson',
      data: jsonPoint,
    })
    map.addSource('point-move' + layerId, {
      type: 'geojson',
      data: jsonPoint,
    })
    map.addSource('line' + layerId, {
      type: 'geojson',
      data: jsonLine,
    })
    map.addSource('line-move' + layerId, {
      type: 'geojson',
      data: jsonLine,
    })
    map.addSource('point-follow' + layerId, {
      type: 'geojson',
      data: jsonPoint,
    })
    map.addLayer({
      id: 'line' + layerId,
      type: 'line',
      source: 'line' + layerId,
      paint: {
        'line-color': '#ff0000',
        'line-width': 2,
        'line-opacity': 0.65,
      },
    })
    map.addLayer({
      id: 'line-move' + layerId,
      type: 'line',
      source: 'line-move' + layerId,
      paint: {
        'line-color': '#ff0000',
        'line-width': 2,
        'line-opacity': 0.65,
        'line-dasharray': [5, 2],
      },
    })
    map.addLayer({
      id: 'points' + layerId,
      type: 'circle',
      source: 'points' + layerId,
      paint: {
        'circle-color': '#ffffff',
        'circle-radius': 3.5,
        'circle-stroke-width': 1.5,
        'circle-stroke-color': '#ff0000',
      },
    })
    map.addLayer({
      id: 'point-move' + layerId,
      type: 'circle',
      source: 'point-move' + layerId,
      paint: {
        'circle-color': '#ffffff',
        'circle-radius': 3.5,
        'circle-stroke-width': 1.5,
        'circle-stroke-color': '#ff0000',
      },
    })
    // 活动点可以选择用图层,也可以选择用Mark
    map.addLayer({
      id: 'point-follow' + layerId,
      type: 'circle',
      source: 'point-follow' + layerId,
      paint: {
        'circle-color': '#199afc',
        'circle-radius': 5.5,
        'circle-stroke-width': 1.5,
        'circle-stroke-color': '#ffffff',
      },
    })

    // 清除面积测量
    this.setArea = () => {
      isMeasure = false
      map.removeLayer('point-move' + layerId)
      map.removeLayer('line-move' + layerId)

      return isMeasure
    }

    /**
     * 添加点
     * @param {*} _e
     */
    function addPointforJSON(_e) {
      if (isMeasure) {
        const point = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [_e.lngLat.lng, _e.lngLat.lat],
          },
          properties: {
            id: String(new Date().getTime()),
          },
        }
        jsonPoint.features.push(point)
        map.getSource('points' + layerId).setData(jsonPoint)
        drawLine(jsonPoint)
        addMeasureRes(jsonPoint)
      }
    }

    /**
     * 绘制线
     * @param {*} jsonPoint
     */
    function drawLine(jsonPoint) {
      if (jsonPoint.features.length > 1) {
        jsonLine.features = []
        for (let i = 0; i < jsonPoint.features.length - 1; i++) {
          const coords = jsonPoint.features[i].geometry.coordinates
          const next_coords = jsonPoint.features[i + 1].geometry.coordinates
          jsonLine.features.push({
            type: 'Feature',
            geometry: {
              type: 'LineString',
              coordinates: [coords, next_coords],
            },
          })
        }
        map.getSource('line' + layerId).setData(jsonLine)
      }
    }

    /**
     * 添加dom
     * @param {*} jsonPoint 点集
     */
    function addMeasureRes(jsonPoint) {
      if (jsonPoint.features.length > 0) {
        removedom()
        const pointList = []
        for (let i = 0; i < jsonPoint.features.length; i++) {
          const coords = jsonPoint.features[i].geometry.coordinates
          pointList.push(coords)

          const close = document.createElement('div')
          close.setAttribute('class', `measure-result ${layerId} close`)
          close.onclick = (__e) => {
            if (!isMeasure) {
              // 移除点
              __e.stopPropagation()
              removePoint(coords)
              map.off('mousemove', onMouseMove)
              map.off('mousedown', onmousedown)
              if (catchMark) {
                catchMark.remove()
              }
            }
          }

          const clear = document.createElement('div')
          clear.setAttribute('class', `measure-result ${layerId} clear`)
          clear.setAttribute('style', 'position: absolute;left:8px;top:-18px;')
          clear.onclick = (__e) => {
            // 全部删除
            __e.stopPropagation()
            removeLayer()
            map.off('mousemove', onMouseMove)
            map.off('mousedown', onmousedown)
            if (catchMark) {
              catchMark.remove()
            }
          }

          const edit = document.createElement('div')
          edit.setAttribute('class', `measure-result ${layerId} edit`)
          edit.onclick = (__e) => {
            // 编辑线
            __e.stopPropagation()
            map.off('mousemove', onMouseMove)
            map.off('mousedown', onmousedown)
            if (catchMark) {
              catchMark.remove()
            }
            editLine()
          }

          const element = document.createElement('div')
          element.setAttribute('class', 'measure-result ' + layerId)
          const option = {
            element: element,
            anchor: 'left',
            offset: [8, 0],
          }
          element.innerHTML = i === 0 ? '起点' : getLength(pointList);
          if ((jsonPoint.features.length === i + 1) & !isMeasure) {
            element.setAttribute('style', 'border:1px solid #f00;padding:2px 5px 17px 5px;margin: 20px 0 0 -10px;')
            element.innerHTML = `总长:<span style="color:red">${getLength(pointList)}</span>`
            // element.appendChild(edit)
            element.appendChild(clear)
          }
          // element.appendChild(close)
          new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
        }
      }
    }

    /**
     * 移除点
     * @param {*} coords 点坐标
     */
    function removePointID(id) {
      if (jsonPoint.features.length > 0) {
        if (jsonPoint.features.length === 2) {
          jsonPoint.features = []
          jsonLine.features = []
          map.getSource('points' + layerId).setData(jsonPoint)
          map.getSource('line' + layerId).setData(jsonLine)
          removedom()
        } else {
          for (let i = 0; i < jsonPoint.features.length; i++) {
            if (
              (jsonPoint.features[i].properties.id === id)
            ) {
              jsonPoint.features.splice(i, 1)
            }
          }
          drawLine(jsonPoint)
          addMeasureRes(jsonPoint)
          map.getSource('points' + layerId).setData(jsonPoint)
        }
      }
    }
    function removePoint(coords) {
      if (jsonPoint.features.length > 0) {
        if (jsonPoint.features.length === 2) {
          jsonPoint.features = []
          jsonLine.features = []
          map.getSource('points' + layerId).setData(jsonPoint)
          map.getSource('line' + layerId).setData(jsonLine)
          removedom()
        } else {
          for (let i = 0; i < jsonPoint.features.length; i++) {
            console.log(jsonPoint.features[i]);
            console.log(jsonPoint.features[i].geometry.coordinates[1]);
            if (
              (jsonPoint.features[i].geometry.coordinates[0] === coords[0]) &
              (jsonPoint.features[i].geometry.coordinates[1] === coords[1])
            ) {
              jsonPoint.features.splice(i, 1)
            }
          }
          drawLine(jsonPoint)
          addMeasureRes(jsonPoint)
          map.getSource('points' + layerId).setData(jsonPoint)
        }
      }
    }

    /**
     * 计算长度
     * @param {*} pointList
     * @returns
     */
    function getLength(pointList) {
      const line = turf.lineString(pointList)
      let len = turf.length(line)
      if (len < 1) {
        len = Math.round(len * 1000) + '米'
      } else {
        len = len.toFixed(2) + '公里'
      }
      return len
    }

    /**
     * 移除dom
     */
    function removedom() {
      const dom = document.getElementsByClassName('measure-result ' + layerId)
      const len = dom.length
      if (len) {
        for (let i = len - 1; i >= 0; i--) {
          if (dom[i]) dom[i].remove()
        }
      }
    }

    /**
     * 移除图层
     */
    function removeLayer() {
      jsonPoint.features = []
      jsonLine.features = []
      map.removeLayer('points' + layerId)
      map.removeLayer('line' + layerId)
      removedom()
    }

    /**
     * 鼠标move事件
     * @param {} _e
     */
    function mouseMove(_e) {
      if (isMeasure) {
        map.getCanvas().style.cursor = 'default'
        var coords = [_e.lngLat.lng, _e.lngLat.lat]
        const jsonp = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: coords,
          },
        }
        map.getSource('point-move' + layerId).setData(jsonp)

        if (jsonPoint.features.length > 0) {
          moveTip.remove();
          const pointList = []
          for (let i = 0; i < jsonPoint.features.length; i++) {
            const coord = jsonPoint.features[i].geometry.coordinates
            pointList.push(coord)
          }
          pointList.push(coords)
          const prev = jsonPoint.features[jsonPoint.features.length - 1]
          const jsonl = {
            type: 'Feature',
            geometry: {
              type: 'LineString',
              coordinates: [prev.geometry.coordinates, coords],
            },
          }
          map.getSource('line-move' + layerId).setData(jsonl)
          ele.innerHTML = `总长:<span style="color:red">${getLength(pointList)}</span></br> <span style="color:#999;">单击确定地点,双击结束</span>`
          tooltip.setLngLat(coords)
        } else {
          moveTip.setLngLat(coords)
        }
      }
    }

    /**
     * 绘制完成
     * @param {*} _e
     */
    function finish(_e) {
      if (isMeasure) {
        setTimeout(() => {
          isMeasure = false
          var coords = [_e.lngLat.lng, _e.lngLat.lat]
          removePoint(coords)
          map.removeLayer('point-move' + layerId)
          map.removeLayer('line-move' + layerId)
          map.getCanvas().style.cursor = 'default'
          tooltip.remove()
          editLine();
        }, 100);
      }
    }
    map.on('click', 'points' + layerId, (e) => {
      if (!isMeasure) {
        removePointID(e.features[0].properties.id)
      }
    });
    map.on("mouseenter", 'points' + layerId, (_e) => {
      if (!isMeasure) {
        map.getCanvas().style.cursor = "pointer";
        var coords = [_e.lngLat.lng, _e.lngLat.lat]
        const ele = document.createElement('div')
        ele.setAttribute('style', 'border:1px solid #f00;')
        ele.setAttribute('class', 'measure-move')
        const option = {
          element: ele,
          anchor: 'left',
          offset: [5, -20],
        }
        ele.innerHTML = '单击可删除此点,拖拽可调整位置';
        hoverTip = new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
      }
    });
    map.on("mouseleave", 'points' + layerId, () => {
      map.getCanvas().style.cursor = "";
      hoverTip && hoverTip.remove()
    });

    map.on('click', function (_e) {
      addPointforJSON(_e)
    })

    map.on('mousemove', function (_e) {
      mouseMove(_e)
    })

    map.on('dblclick', function (_e) {
      finish(_e)
    })

    /**
     * 编辑测量线
     */
    function editLine() {
      // catchMark = createMarker()
      UpdataGeolist()

      map.on('mousemove', onMouseMove)
      map.on('mousedown', onmousedown)
    }

    function onMouseMove(e) {
      const moveCoord = [e.lngLat.lng, e.lngLat.lat]

      if (jsonPoint.features.length > 1) {
        // 计算当前指针与线段最近的点
        pointOnLine = getNearestPointOnLine(Geolist, moveCoord) // 自己计算
        const screenOnLine = Object.values(map.project(pointOnLine)) // 线上屏幕坐标
        const screenP = [e.point.x, e.point.y]
        const screenDist = screenDistance(screenOnLine, screenP) // 距离
        if (screenDist < 15) {
          isEdit = true
          // catchMark.setLngLat(pointOnLine).addTo(map)
          // catchMark.getElement().style.display = 'block'
        } else {
          isEdit = false
          // catchMark.getElement().style.display = 'none'
        }
      } else {
        isEdit = false
        // catchMark.getElement().style.display = 'none'
        map.dragPan.enable()
      }
    }

    function onmousedown(e) {
      if (isEdit) {
        map.dragPan.disable()
        let isExist = false

        // 首先判断编辑点是否是存在(存在修改原来点,不存在新加点)
        for (let i = 0; i < jsonPoint.features.length; i++) {
          const coord = jsonPoint.features[i].geometry.coordinates
          if (coord[0] === pointOnLine[0] && coord[1] === pointOnLine[1]) {
            isExist = true
          }
        }

        // 获取编辑点在列表中的位置
        dragPointOrder = getDragCoords(pointOnLine, Geolist)

        if (!isExist) {
          // 添加编辑点
          const point = {
            type: 'Feature',
            geometry: {
              type: 'Point',
              coordinates: pointOnLine,
            },
            properties: {
              id: String(new Date().getTime()),
            },
          }
          jsonPoint.features.splice(dragPointOrder, 0, point)

          // 更新绘制要素
          updataFeature()
        }

        map.on('mousemove', onDrag)
        map.on('mouseup', onMouseup)
      }
    }

    function onDrag(e) {
      // 开始计时
      // var start = new Date().getTime()
      const movePoint = [e.lngLat.lng, e.lngLat.lat]

      // 点跟随鼠标移动
      jsonPoint.features[dragPointOrder].geometry.coordinates = movePoint

      // 更新绘制要素
      updataFeature()

      // 计时结束
      // var end1 = new Date().getTime()
      // console.log('渲染时间:', end1 - start + 'ms')
    }

    // 更新绘制要素
    function updataFeature() {
      UpdataGeolist()
      map.getSource('points' + layerId).setData(jsonPoint)
      drawLine(jsonPoint)
      addMeasureRes(jsonPoint)
    }

    function onMouseup(e) {
      map.off('mousemove', onDrag)
      map.dragPan.enable()
    }

    // 创建Marker
    function createMarker() {
      const markerParam = {
        map: sdMap.map,
        imgUrl: pointMark,
        lngLat: [0, 0],
        height: 13,
        width: 13,
        size: 13,
        isDrag: false,
        cursor: 'default',
      }
      const option = {
        element: ele,
        anchor: 'bottom-left',
      }
      ele.innerHTML = ''

      // return sdMap.createMarker(markerParam)
      return new mapboxgl.Marker(option).setLngLat([0, 0]).addTo(map)
    }

    // 更新点集
    function UpdataGeolist() {
      Geolist = []
      for (let i = 0; i < jsonPoint.features.length; i++) {
        const coord = jsonPoint.features[i].geometry.coordinates
        Geolist.push(coord)
      }
    }

    // 计算点到直线最近距离的点
    function getNearestPointOnLine(list, moveCoord) {
      var dis, point1, point2
      for (let i = 0; i < list.length - 1; i++) {
        const distance = getNearestDistance(moveCoord, list[i], list[i + 1])
        if (i === 0) {
          dis = distance
          point1 = list[i]
          point2 = list[i + 1]
        } else {
          if (distance < dis) {
            dis = distance
            point1 = list[i]
            point2 = list[i + 1]
          }
        }
      }
      const Point = getNearestPoint(moveCoord, point1, point2)
      return Point
    }

    // 计算点point到线段point1, point2最近距离
    function getNearestDistance(point, point1, point2) {
      const P = {}
      const A = {}
      const B = {}
      P.x = point[0]
      P.y = point[1]
      A.x = point1[0]
      A.y = point1[1]
      B.x = point2[0]
      B.y = point2[1]
      // 计算向量AP和向量AB的点积
      const dotProduct = (P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)
      // 计算向量AB的长度的平方
      const lengthSquare = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)
      // 计算点P到线段AB的投影点C
      const t = dotProduct / lengthSquare
      const C = { x: A.x + t * (B.x - A.x), y: A.y + t * (B.y - A.y) }
      // 如果点C在线段AB内,则点P到线段AB的最近距离为PC的长度;否则,点P到线段AB的最近距离为PA或PB中的较小值。
      const isInside = dotProduct >= 0 && dotProduct <= lengthSquare
      if (isInside) {
        return Math.sqrt((P.x - C.x) * (P.x - C.x) + (P.y - C.y) * (P.y - C.y))
      } else {
        return Math.min(
          Math.sqrt((P.x - A.x) * (P.x - A.x) + (P.y - A.y) * (P.y - A.y)),
          Math.sqrt((P.x - B.x) * (P.x - B.x) + (P.y - B.y) * (P.y - B.y))
        )
      }
    }

    // 计算点到直线最近的点 point点坐标,point1, point2直线两个端点
    function getNearestPoint(point, point1, point2) {
      var x, y, x0, y0, x1, y1, x2, y2
      x0 = point[0]
      y0 = point[1]
      x1 = point1[0]
      y1 = point1[1]
      x2 = point2[0]
      y2 = point2[1]

      if (x1 !== x2 && y1 !== y2) {
        const a = (y2 - y1) / (x2 - x1)
        const b = y1 - a * x1
        const k2 = -1 / a
        const b2 = y0 - k2 * x0
        x = (b2 - b) / (a - k2)
        y = a * x + b
      } else if (x1 === x2) {
        x = x1
        y = y0
      } else if (y1 === y2) {
        x = x0
        y = y1
      }

      // 点不能超出线段
      if (x1 < x2) {
        if (x2 < x) {
          x = x2
        } else if (x < x1) {
          x = x1
        }
      } else {
        if (x1 < x) {
          x = x1
        } else if (x < x2) {
          x = x2
        }
      }
      if (y1 < y2) {
        if (y2 < y) {
          y = y2
        } else if (y < y1) {
          y = y1
        }
      } else {
        if (y1 < y) {
          y = y1
        } else if (y < y2) {
          y = y2
        }
      }

      // 点吸附端点
      const screenX0 = Object.values(map.project([x0, y0])) // 屏幕坐标
      const screenX1 = Object.values(map.project([x1, y1])) // 屏幕坐标
      const screenX2 = Object.values(map.project([x2, y2])) // 屏幕坐标
      const screenDistX1 = screenDistance(screenX0, screenX1) // 距离
      const screenDistX2 = screenDistance(screenX0, screenX2) // 距离
      if (screenDistX1 < 10) {
        x = x1
        y = y1
      }
      if (screenDistX2 < 10) {
        x = x2
        y = y2
      }

      return [x, y]
    }

    // 屏幕距离
    function screenDistance(point1, point2) {
      const x2 = Math.pow(point1[0] - point2[0], 2)
      const y2 = Math.pow(point1[1] - point2[1], 2)
      const dist = Math.sqrt(x2 + y2)

      return dist
    }

    // 计算编辑点在线段上的添加位置
    function getDragCoords(coords, list) {
      var x, y, x1, y1, x2, y2
      let index = 0
      x = coords[0]
      y = coords[1]

      for (let i = 0; i < list.length - 1; i++) {
        x1 = list[i][0]
        y1 = list[i][1]
        x2 = list[i + 1][0]
        y2 = list[i + 1][1]

        if (x === x1 && y === y1) {
          index = i
          break
        } else {
          // 计算线段的长度
          const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
          // 计算点到线段起点的距离
          const distance1 = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2))
          // 计算点到线段终点的距离
          const distance2 = Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2))
          // 如果点到线段两个端点的距离之和等于线段的长度,则点在线段上
          if (Math.abs(length - distance1 - distance2) < 0.00001) {
            index = i + 1
            break
          }
        }
      }
      return index
    }
  }

  /**
   * 测量面积
   * @param {*} layerId
   */
  measureArea(layerId) {
    this.layerAreaList.push(layerId)
    var isMeasure = true
    const map = this.map
    const sdMap = this.sdMap
    map.doubleClickZoom.disable()
    let catchMark = null
    let isEdit = false
    let Geolist = []
    let dragPointOrder = 0
    let pointOnLine = [0, 0]

    const jsonPoint = {
      type: 'FeatureCollection',
      features: [],
    }
    const jsonLine = {
      type: 'FeatureCollection',
      features: [],
    }

    const ele = document.createElement('div')
    ele.setAttribute('class', 'measure-move')
    const option = {
      element: ele,
      anchor: 'left',
      offset: [8, 0],
    }
    const tooltip = new mapboxgl.Marker(option).setLngLat([0, 0]).addTo(map)

    map.addSource('points-area' + layerId, {
      type: 'geojson',
      data: jsonPoint,
    })
    map.addSource('point-move' + layerId, {
      type: 'geojson',
      data: jsonPoint,
    })
    map.addSource('line-area' + layerId, {
      type: 'geojson',
      data: jsonLine,
    })
    map.addSource('line-move' + layerId, {
      type: 'geojson',
      data: jsonLine,
    })
    map.addLayer({
      id: 'line-move' + layerId,
      type: 'line',
      source: 'line-move' + layerId,
      paint: {
        'line-color': '#ff0000',
        'line-width': 2,
        'line-opacity': 0.65,
        'line-dasharray': [5, 2],
      },
    })
    map.addLayer({
      id: 'line-area' + layerId,
      type: 'fill',
      source: 'line-area' + layerId,
      paint: {
        'fill-color': '#ff0000',
        'fill-opacity': 0.1,
      },
    })
    map.addLayer({
      id: 'line-area-stroke' + layerId,
      type: 'line',
      source: 'line-area' + layerId,
      paint: {
        'line-color': '#ff0000',
        'line-width': 2,
        'line-opacity': 0.65,
      },
    })
    map.addLayer({
      id: 'points-area' + layerId,
      type: 'circle',
      source: 'points-area' + layerId,
      paint: {
        'circle-color': '#ffffff',
        'circle-radius': 3.5,
        'circle-stroke-width': 1.5,
        'circle-stroke-color': '#ff0000',
      },
    })
    map.addLayer({
      id: 'point-move' + layerId,
      type: 'circle',
      source: 'point-move' + layerId,
      paint: {
        'circle-color': '#ffffff',
        'circle-radius': 3.5,
        'circle-stroke-width': 1.5,
        'circle-stroke-color': '#ff0000',
      },
    })

    this.setDistance = () => {
      isMeasure = false
      map.removeLayer('point-move' + layerId)
      map.removeLayer('line-move' + layerId)

      return isMeasure
    }

    /**
     * 添加点
     * @param {*} _e
     */
    function addPointforJSON(_e) {
      if (isMeasure) {
        const point = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [_e.lngLat.lng, _e.lngLat.lat],
          },
          properties: {
            id: String(new Date().getTime()),
          },
        }
        jsonPoint.features.push(point)
        map.getSource('points-area' + layerId).setData(jsonPoint)
        addMeasureRes(jsonPoint)
      }
    }

    /**
     * 添加dom
     * @param {*} jsonPoint 点集
     */
    function addMeasureRes(jsonPoint) {
      if (jsonPoint.features.length > 0) {
        removedom()
        const pointList = []
        for (let i = 0; i < jsonPoint.features.length; i++) {
          const coords = jsonPoint.features[i].geometry.coordinates
          pointList.push(coords)

          const close = document.createElement('div')
          close.setAttribute('class', `measure-result ${layerId} close`)
          close.onclick = (__e) => {
            // 移除点
            __e.stopPropagation()
            removePoint(coords)
            map.off('mousemove', onMouseMove)
            map.off('mousedown', onmousedown)
            if (catchMark) {
              catchMark.remove()
            }
          }
          if ((jsonPoint.features.length === i + 1) & !isMeasure) {
            const clear = document.createElement('div')
            clear.setAttribute('class', `measure-result ${layerId} clear`)
            clear.onclick = (__e) => {
              // 全部移除
              __e.stopPropagation()
              removeLayer()
              map.off('mousemove', onMouseMove)
              map.off('mousedown', onmousedown)
              if (catchMark) {
                catchMark.remove()
              }
            }

            const edit = document.createElement('div')
            edit.setAttribute('class', `measure-result ${layerId} edit`)
            edit.onclick = (__e) => {
              // 编辑
              __e.stopPropagation()
              map.off('mousemove', onMouseMove)
              map.off('mousedown', onmousedown)
              if (catchMark) {
                catchMark.remove()
              }
              editArea()
            }

            const element = document.createElement('div')
            element.setAttribute('class', 'measure-result ' + layerId)
            const option = {
              element: element,
              anchor: 'left',
              offset: [0, 0],
            }
            element.innerHTML = getArea(pointList)
            element.appendChild(edit)
            element.appendChild(clear)
            element.appendChild(close)
            new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
          } else {
            const option = {
              element: close,
              anchor: 'left',
              offset: [5, -15],
            }
            new mapboxgl.Marker(option).setLngLat(coords).addTo(map)
          }
        }
      }
    }

    /**
     * 计算面积
     * @param {*} pointList
     * @returns
     */
    function getArea(pointList) {
      pointList.push(pointList[0])
      const polygon = turf.polygon([pointList])
      let area = turf.area(polygon)
      if (area < 10000) {
        area = Math.round(area) + '平方米'
      } else {
        area = (area / 1000000).toFixed(2) + '平方公里'
      }
      return area
    }

    /**
     * 移除点
     * @param {*} coords 点坐标
     */
    function removePoint(coords) {
      if (jsonPoint.features.length > 0) {
        if (jsonPoint.features.length === 3) {
          jsonPoint.features = []
          jsonLine.features = []
          map.getSource('points-area' + layerId).setData(jsonPoint)
          map.getSource('line-area' + layerId).setData(jsonLine)
          removedom()
        } else {
          for (let i = 0; i < jsonPoint.features.length; i++) {
            if (
              (jsonPoint.features[i].geometry.coordinates[0] === coords[0]) &
              (jsonPoint.features[i].geometry.coordinates[1] === coords[1])
            ) {
              jsonPoint.features.splice(i, 1)
            }
          }
          addMeasureRes(jsonPoint)
          map.getSource('points-area' + layerId).setData(jsonPoint)

          const pointList = []
          for (let i = 0; i < jsonPoint.features.length; i++) {
            const coord = jsonPoint.features[i].geometry.coordinates
            pointList.push(coord)
          }
          const pts = pointList.concat([pointList[0]])
          const jsona = {
            type: 'Feature',
            geometry: {
              type: 'Polygon',
              coordinates: [pts],
            },
          }
          map.getSource('line-area' + layerId).setData(jsona)
        }
      }
    }

    /**
     * 移除dom
     */
    function removedom() {
      const dom = document.getElementsByClassName('measure-result ' + layerId)
      const len = dom.length
      if (len) {
        for (let i = len - 1; i >= 0; i--) {
          if (dom[i]) dom[i].remove()
        }
      }
    }

    /**
     * 移除图层
     */
    function removeLayer() {
      jsonPoint.features = []
      jsonLine.features = []
      map.removeLayer('points-area' + layerId)
      map.removeLayer('line-area' + layerId)
      map.removeLayer('line-area-stroke' + layerId)
      removedom()
    }

    /**
     * 鼠标move事件
     * @param {} _e
     */
    function mouseMove(_e) {
      if (isMeasure) {
        map.getCanvas().style.cursor = 'default'
        const coords = [_e.lngLat.lng, _e.lngLat.lat]
        const jsonp = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: coords,
          },
        }
        map.getSource('point-move' + layerId).setData(jsonp)

        if (jsonPoint.features.length > 0) {
          if (jsonPoint.features.length === 1) {
            const prev = jsonPoint.features[jsonPoint.features.length - 1]
            const jsonl = {
              type: 'Feature',
              geometry: {
                type: 'LineString',
                coordinates: [prev.geometry.coordinates, coords],
              },
            }
            map.getSource('line-move' + layerId).setData(jsonl)
          } else {
            const json = {
              type: 'FeatureCollection',
              features: [],
            }
            map.getSource('line-move' + layerId).setData(json)

            const pointList = []
            for (let i = 0; i < jsonPoint.features.length; i++) {
              const coord = jsonPoint.features[i].geometry.coordinates
              pointList.push(coord)
            }
            pointList.push(coords)
            const pts = pointList.concat([pointList[0]])
            const jsona = {
              type: 'Feature',
              geometry: {
                type: 'Polygon',
                coordinates: [pts],
              },
            }
            map.getSource('line-area' + layerId).setData(jsona)
            ele.innerHTML = getArea(pointList)
            tooltip.setLngLat(coords)
          }
        }
      }
    }

    /**
     * 绘制完成
     * @param {*} _e
     */
    function finish(_e) {
      if (isMeasure) {
        isMeasure = false
        const coords = [_e.lngLat.lng, _e.lngLat.lat]
        removePoint(coords)
        map.removeLayer('point-move' + layerId)
        map.removeLayer('line-move' + layerId)
        map.getCanvas().style.cursor = 'default'
        tooltip.remove()
      }
    }

    map.on('click', function (_e) {
      addPointforJSON(_e)
    })

    map.on('dblclick', function (_e) {
      finish(_e)
    })

    map.on('mousemove', function (_e) {
      mouseMove(_e)
    })

    /**
     * 编辑测量面
     */
    function editArea() {
      catchMark = createMarker()
      UpdataGeolist()

      map.on('mousemove', onMouseMove)
      map.on('mousedown', onmousedown)
    }

    function onMouseMove(e) {
      const moveCoord = [e.lngLat.lng, e.lngLat.lat]

      if (jsonPoint.features.length > 1) {
        // 计算当前指针与线段的距离
        pointOnLine = getNearestPointOnLine(Geolist, moveCoord) // 线上实际坐标
        const screenOnLine = Object.values(map.project(pointOnLine)) // 线上屏幕坐标
        const screenP = [e.point.x, e.point.y]
        const screenDist = screenDistance(screenOnLine, screenP) // 距离
        if (screenDist < 15) {
          isEdit = true
          catchMark.setLngLat(pointOnLine).addTo(map)
          catchMark.getElement().style.display = 'block'
        } else {
          isEdit = false
          catchMark.getElement().style.display = 'none'
        }
      } else {
        isEdit = false
        catchMark.getElement().style.display = 'none'
        map.dragPan.enable()
      }
    }

    function onmousedown(e) {
      if (isEdit) {
        map.dragPan.disable()
        let isExist = false

        // 首先判断编辑点是否是存在(存在修改原来点,不存在新加点)
        for (let i = 0; i < jsonPoint.features.length; i++) {
          const coord = jsonPoint.features[i].geometry.coordinates
          if (coord[0] === pointOnLine[0] && coord[1] === pointOnLine[1]) {
            isExist = true
          }
        }

        // 获取编辑点在列表中的位置
        dragPointOrder = getDragCoords(pointOnLine, Geolist)

        if (!isExist) {
          // 添加编辑点
          const point = {
            type: 'Feature',
            geometry: {
              type: 'Point',
              coordinates: pointOnLine,
            },
            properties: {
              id: String(new Date().getTime()),
            },
          }
          jsonPoint.features.splice(dragPointOrder, 0, point)

          // 更新绘制要素
          updataFeature()
        }

        map.on('mousemove', onDrag)
        map.on('mouseup', onMouseup)
      }
    }

    function onDrag(e) {
      const movePoint = [e.lngLat.lng, e.lngLat.lat]

      // 点跟随鼠标移动
      jsonPoint.features[dragPointOrder].geometry.coordinates = movePoint

      // 更新绘制要素
      updataFeature()
    }

    // 更新绘制要素
    function updataFeature() {
      UpdataGeolist()
      const pts = Geolist
      const jsona = {
        type: 'Feature',
        geometry: {
          type: 'Polygon',
          coordinates: [pts],
        },
      }
      map.getSource('line-area' + layerId).setData(jsona)
      map.getSource('points-area' + layerId).setData(jsonPoint)
      addMeasureRes(jsonPoint)
    }

    function onMouseup(e) {
      map.off('mousemove', onDrag)
      map.dragPan.enable()
    }

    // 创建Marker
    function createMarker() {
      const markerParam = {
        map: sdMap.map,
        imgUrl: pointMark,
        lngLat: [0, 0],
        height: 13,
        width: 13,
        size: 13,
        isDrag: false,
        cursor: 'default',
      }
      return sdMap.createMarker(markerParam)
    }

    // 更新点集
    function UpdataGeolist() {
      Geolist = []
      for (let i = 0; i < jsonPoint.features.length; i++) {
        const coord = jsonPoint.features[i].geometry.coordinates
        Geolist.push(coord)
      }
      Geolist.push(Geolist[0])
    }

    // 计算点到直线最近距离的点
    function getNearestPointOnLine(list, moveCoord) {
      var dis, point1, point2
      for (let i = 0; i < list.length - 1; i++) {
        const distance = getNearestDistance(moveCoord, list[i], list[i + 1])
        if (i === 0) {
          dis = distance
          point1 = list[i]
          point2 = list[i + 1]
        } else {
          if (distance < dis) {
            dis = distance
            point1 = list[i]
            point2 = list[i + 1]
          }
        }
      }
      const Point = getNearestPoint(moveCoord, point1, point2)
      return Point
    }

    // 计算点point到线段point1, point2最近距离
    function getNearestDistance(point, point1, point2) {
      const P = {}
      const A = {}
      const B = {}
      P.x = point[0]
      P.y = point[1]
      A.x = point1[0]
      A.y = point1[1]
      B.x = point2[0]
      B.y = point2[1]
      // 计算向量AP和向量AB的点积
      const dotProduct = (P.x - A.x) * (B.x - A.x) + (P.y - A.y) * (B.y - A.y)
      // 计算向量AB的长度的平方
      const lengthSquare = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y)
      // 计算点P到线段AB的投影点C
      const t = dotProduct / lengthSquare
      const C = { x: A.x + t * (B.x - A.x), y: A.y + t * (B.y - A.y) }
      // 如果点C在线段AB内,则点P到线段AB的最近距离为PC的长度;否则,点P到线段AB的最近距离为PA或PB中的较小值。
      const isInside = dotProduct >= 0 && dotProduct <= lengthSquare
      if (isInside) {
        return Math.sqrt((P.x - C.x) * (P.x - C.x) + (P.y - C.y) * (P.y - C.y))
      } else {
        return Math.min(
          Math.sqrt((P.x - A.x) * (P.x - A.x) + (P.y - A.y) * (P.y - A.y)),
          Math.sqrt((P.x - B.x) * (P.x - B.x) + (P.y - B.y) * (P.y - B.y))
        )
      }
    }

    // 计算点到直线最近的点 point点坐标,point1, point2直线两个端点
    function getNearestPoint(point, point1, point2) {
      var x, y, x0, y0, x1, y1, x2, y2
      x0 = point[0]
      y0 = point[1]
      x1 = point1[0]
      y1 = point1[1]
      x2 = point2[0]
      y2 = point2[1]

      if (x1 !== x2 && y1 !== y2) {
        const a = (y2 - y1) / (x2 - x1)
        const b = y1 - a * x1
        const k2 = -1 / a
        const b2 = y0 - k2 * x0
        x = (b2 - b) / (a - k2)
        y = a * x + b
      } else if (x1 === x2) {
        x = x1
        y = y0
      } else if (y1 === y2) {
        x = x0
        y = y1
      }

      // 点不能超出线段
      if (x1 < x2) {
        if (x2 < x) {
          x = x2
        } else if (x < x1) {
          x = x1
        }
      } else {
        if (x1 < x) {
          x = x1
        } else if (x < x2) {
          x = x2
        }
      }
      if (y1 < y2) {
        if (y2 < y) {
          y = y2
        } else if (y < y1) {
          y = y1
        }
      } else {
        if (y1 < y) {
          y = y1
        } else if (y < y2) {
          y = y2
        }
      }

      // 点吸附端点
      const screenX0 = Object.values(map.project([x0, y0])) // 屏幕坐标
      const screenX1 = Object.values(map.project([x1, y1])) // 屏幕坐标
      const screenX2 = Object.values(map.project([x2, y2])) // 屏幕坐标
      const screenDistX1 = screenDistance(screenX0, screenX1) // 距离
      const screenDistX2 = screenDistance(screenX0, screenX2) // 距离
      if (screenDistX1 < 10) {
        x = x1
        y = y1
      }
      if (screenDistX2 < 10) {
        x = x2
        y = y2
      }

      return [x, y]
    }

    // 屏幕距离
    function screenDistance(point1, point2) {
      const x2 = Math.pow(point1[0] - point2[0], 2)
      const y2 = Math.pow(point1[1] - point2[1], 2)
      const dist = Math.sqrt(x2 + y2)

      return dist
    }

    // 计算编辑点在线段上的添加位置
    function getDragCoords(coords, list) {
      var x, y, x1, y1, x2, y2
      let index = 0
      x = coords[0]
      y = coords[1]

      for (let i = 0; i < list.length - 1; i++) {
        x1 = list[i][0]
        y1 = list[i][1]
        x2 = list[i + 1][0]
        y2 = list[i + 1][1]

        if (x === x1 && y === y1) {
          index = i
          break
        } else {
          // 计算线段的长度
          const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
          // 计算点到线段起点的距离
          const distance1 = Math.sqrt(Math.pow(x - x1, 2) + Math.pow(y - y1, 2))
          // 计算点到线段终点的距离
          const distance2 = Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y - y2, 2))
          // 如果点到线段两个端点的距离之和等于线段的长度,则点在线段上
          if (Math.abs(length - distance1 - distance2) < 0.00001) {
            index = i + 1
            break
          }
        }
      }
      return index
    }
  }

  /**
   * 清除所有测量要素
   */
  clearMeasureAll() {
    const dom = document.getElementsByClassName('measure-result')
    const len = dom.length
    if (len) {
      for (let i = len - 1; i >= 0; i--) {
        if (dom[i]) dom[i].remove()
      }
    }
    if (this.layerDistanceList) {
      for (let i = 0; i < this.layerDistanceList.length; i++) {
        const layerid = this.layerDistanceList[i]
        try {
          this.map.removeLayer('points' + layerid)
          this.map.removeLayer('line' + layerid)
          this.map.removeLayer('point-move' + layerid)
          this.map.removeLayer('line-move' + layerid)
        } catch (error) {
          console.log(error)
        }
      }
    }
    this.layerDistanceList = []
    if (this.layerAreaList) {
      for (let i = 0; i < this.layerAreaList.length; i++) {
        const layerid = this.layerAreaList[i]
        try {
          this.map.removeLayer('points-area' + layerid)
          this.map.removeLayer('line-area' + layerid)
          this.map.removeLayer('line-area-stroke' + layerid)
          this.map.removeLayer('point-move' + layerid)
          this.map.removeLayer('line-move' + layerid)
        } catch (error) {
          console.log(error)
        }
      }
    }
    this.layerAreaList = []
    this.map.doubleClickZoom.enable()
  }
}


// css

.measure-move {
    background-color: white;
    border-radius: 3px;
    height: 16px;
    line-height: 16px;
    padding: 0 3px;
    font-size: 12px;
    box-shadow: 0 0 0 1px #ccc;
    float: right;
    cursor: default;
}

.measure-result {
    background-color: white;
    border-radius: 3px;
    height: 16px;
    line-height: 16px;
    padding: 0 3px;
    font-size: 12px;
    box-shadow: 0 0 0 1px #ccc;
    float: right;
    cursor: default;
    z-index: 10;
}

.close {
    width: 3px;
    height: 14px;
    text-align: center;
    border-radius: 3px;
    padding-top: 0px;
    padding-right: 10px;
    box-shadow: 0 0 0 0px #ccc;
    cursor: pointer;
    background: url('~@/assets/mapboxgis/close.png') no-repeat center;
    border: 1px solid rgba(100, 100, 100, 0)
}

.clear {
    width: 3px;
    height: 14px;
    text-align: center;
    border-radius: 3px;
    padding-right: 10px;
    box-shadow: 0 0 0 0px #ccc;
    cursor: pointer;
    float: right;
    background: url('~@/assets/mapboxgis/close.png') no-repeat center;
    border: 1px solid rgba(100, 100, 100, 0)
}

.edit {
    width: 3px;
    height: 14px;
    text-align: center;
    border-radius: 3px;
    padding-right: 10px;
    box-shadow: 0 0 0 0px #ccc;
    cursor: pointer;
    float: right;
    /* background: url(../measureicon/edit.png) no-repeat center; */
    border: 1px solid rgba(100, 100, 100, 0)
}

.close:hover {
    border: 1px solid rgba(52, 98, 152, 1);
}

.clear:hover {
    border: 1px solid rgba(52, 98, 152, 1);
}

.edit:hover {
    border: 1px solid rgba(52, 98, 152, 1);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值