Newer
Older
urbanLifeline_YanAn / public / static / libs / mapbox / extend / MeatureTool.js
@zhangqy zhangqy on 3 Oct 52 KB first commit
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined'
    ? (module.exports = factory())
    : typeof define === 'function' && define.amd
    ? define(factory)
    : ((global = typeof globalThis !== 'undefined' ? globalThis : global || self),
      (global.mapboxgl1.MeatureTool = factory(global.mapboxgl1)));
})(window, function (mapboxgl) {
  const css = `
    /* Override default control style */
.mapbox-gl-draw_ctrl-bottom-left,
.mapbox-gl-draw_ctrl-top-left {
  margin-left:0;
  border-radius:0 4px 4px 0;
}
.mapbox-gl-draw_ctrl-top-right,
.mapbox-gl-draw_ctrl-bottom-right {
  margin-right:0;
  border-radius:4px 0 0 4px;
}

.mapbox-gl-draw_ctrl-draw-btn {
  border-color:rgba(0,0,0,0.9);
  color:rgba(255,255,255,0.5);
  width:30px;
  height:30px;
}

.mapbox-gl-draw_ctrl-draw-btn.active,
.mapbox-gl-draw_ctrl-draw-btn.active:hover {
  background-color:rgb(0 0 0/5%);
}
.mapbox-gl-draw_ctrl-draw-btn {
  background-repeat: no-repeat;
  background-position: center;
}

.mapbox-gl-draw_point {
  background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m10 2c-3.3 0-6 2.7-6 6s6 9 6 9 6-5.7 6-9-2.7-6-6-6zm0 2c2.1 0 3.8 1.7 3.8 3.8 0 1.5-1.8 3.9-2.9 5.2h-1.7c-1.1-1.4-2.9-3.8-2.9-5.2-.1-2.1 1.6-3.8 3.7-3.8z"/>%3C/svg>');
}
.mapbox-gl-draw_polygon {
  background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m15 12.3v-4.6c.6-.3 1-1 1-1.7 0-1.1-.9-2-2-2-.7 0-1.4.4-1.7 1h-4.6c-.3-.6-1-1-1.7-1-1.1 0-2 .9-2 2 0 .7.4 1.4 1 1.7v4.6c-.6.3-1 1-1 1.7 0 1.1.9 2 2 2 .7 0 1.4-.4 1.7-1h4.6c.3.6 1 1 1.7 1 1.1 0 2-.9 2-2 0-.7-.4-1.4-1-1.7zm-8-.3v-4l1-1h4l1 1v4l-1 1h-4z"/>%3C/svg>');
}
.mapbox-gl-draw_line {
  background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m13.5 3.5c-1.4 0-2.5 1.1-2.5 2.5 0 .3 0 .6.2.9l-3.8 3.8c-.3-.1-.6-.2-.9-.2-1.4 0-2.5 1.1-2.5 2.5s1.1 2.5 2.5 2.5 2.5-1.1 2.5-2.5c0-.3 0-.6-.2-.9l3.8-3.8c.3.1.6.2.9.2 1.4 0 2.5-1.1 2.5-2.5s-1.1-2.5-2.5-2.5z"/>%3C/svg>');
}
.mapbox-gl-draw_trash {
  background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="M10,3.4 c-0.8,0-1.5,0.5-1.8,1.2H5l-1,1v1h12v-1l-1-1h-3.2C11.5,3.9,10.8,3.4,10,3.4z M5,8v7c0,1,1,2,2,2h6c1,0,2-1,2-2V8h-2v5.5h-1.5V8h-3 v5.5H7V8H5z"/>%3C/svg>');
}
.mapbox-gl-draw_uncombine {
  background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="m12 2c-.3 0-.5.1-.7.3l-1 1c-.4.4-.4 1 0 1.4l1 1c.4.4 1 .4 1.4 0l1-1c.4-.4.4-1 0-1.4l-1-1c-.2-.2-.4-.3-.7-.3zm4 4c-.3 0-.5.1-.7.3l-1 1c-.4.4-.4 1 0 1.4l1 1c.4.4 1 .4 1.4 0l1-1c.4-.4.4-1 0-1.4l-1-1c-.2-.2-.4-.3-.7-.3zm-7 1c-1 0-1 1-.5 1.5.3.3 1 1 1 1l-1 1s-.5.5 0 1 1 0 1 0l1-1 1 1c.5.5 1.5.5 1.5-.5v-4zm-5 3c-.3 0-.5.1-.7.3l-1 1c-.4.4-.4 1 0 1.4l4.9 4.9c.4.4 1 .4 1.4 0l1-1c.4-.4.4-1 0-1.4l-4.9-4.9c-.1-.2-.4-.3-.7-.3z"/>%3C/svg>');
}
.mapbox-gl-draw_combine {
  background-image: url('data:image/svg+xml;utf8,%3Csvg xmlns="http://www.w3.org/2000/svg" width="20" height="20">%3Cpath d="M12.1,2c-0.3,0-0.5,0.1-0.7,0.3l-1,1c-0.4,0.4-0.4,1,0,1.4l4.9,4.9c0.4,0.4,1,0.4,1.4,0l1-1 c0.4-0.4,0.4-1,0-1.4l-4.9-4.9C12.6,2.1,12.3,2,12.1,2z M8,8C7,8,7,9,7.5,9.5c0.3,0.3,1,1,1,1l-1,1c0,0-0.5,0.5,0,1s1,0,1,0l1-1l1,1 C11,13,12,13,12,12V8H8z M4,10c-0.3,0-0.5,0.1-0.7,0.3l-1,1c-0.4,0.4-0.4,1,0,1.4l1,1c0.4,0.4,1,0.4,1.4,0l1-1c0.4-0.4,0.4-1,0-1.4 l-1-1C4.5,10.1,4.3,10,4,10z M8,14c-0.3,0-0.5,0.1-0.7,0.3l-1,1c-0.4,0.4-0.4,1,0,1.4l1,1c0.4,0.4,1,0.4,1.4,0l1-1 c0.4-0.4,0.4-1,0-1.4l-1-1C8.5,14.1,8.3,14,8,14z"/>%3C/svg>');
}

.mapboxgl-map.mouse-pointer .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: pointer;
}
.mapboxgl-map.mouse-move .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: move;
}
.mapboxgl-map.mouse-add .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: crosshair;
}
.mapboxgl-map.mouse-move.mode-direct_select .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: grab;
  cursor: -moz-grab;
  cursor: -webkit-grab;
}
.mapboxgl-map.mode-direct_select.feature-vertex.mouse-move .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: move;
}
.mapboxgl-map.mode-direct_select.feature-midpoint.mouse-pointer .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: cell;
}
.mapboxgl-map.mode-direct_select.feature-feature.mouse-move .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: move;
}
.mapboxgl-map.mode-static.mouse-pointer  .mapboxgl-canvas-container.mapboxgl-interactive {
  cursor: grab;
  cursor: -moz-grab;
  cursor: -webkit-grab;
}

.mapbox-gl-draw_boxselect {
    pointer-events: none;
    position: absolute;
    top: 0;
    left: 0;
    width: 0;
    height: 0;
    background: rgba(0,0,0,.1);
    border: 2px dotted #fff;
    opacity: 0.5;
}
    .measure-move {
    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 {
    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(../measureicon/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(../measureicon/delete.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);
}
`;
  document.write(`<style type='text/css'>${css}</style>`);

  return class {
    constructor(map) {
      this.map = map;
    }

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

    /**
     * 测量距离
     * @param {*} layerId
     */
    measureDistance(layerId, isP = true, callback = () => {}) {
      this.layerDistanceList.push(layerId);
      var isMeasure = true;
      const map = this.map;
      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]);
      if (isP) tooltip.addTo(map);

      // 添加测量图层
      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;
        if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
        if (map.getLayer('line-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 (isP && 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();
              }
            };

            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();
              }
              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.appendChild(edit);
              element.appendChild(clear);
            }
            element.appendChild(close);
            new mapboxgl.Marker(option).setLngLat(coords).addTo(map);
          }
        }
      }

      /**
       * 移除点
       * @param {*} coords 点坐标
       */
      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++) {
              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 = [];
        if (map.getLayer('points' + layerId)) map.removeLayer('points' + layerId);
        if (map.getLayer('line' + 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) {
            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 = getLength(pointList);
            tooltip.setLngLat(coords);
          }
        }
      }

      /**
       * 绘制完成
       * @param {*} _e
       */
      function finish(_e) {
        if (isMeasure) {
          isMeasure = false;
          var coords = [_e.lngLat.lng, _e.lngLat.lat];
          removePoint(coords);
          if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
          if (map.getLayer('line-move' + layerId)) map.removeLayer('line-move' + layerId);
          map.getCanvas().style.cursor = 'default';
          tooltip.remove();
          let features = map.getSource('line' + layerId)._data.features;
          let coordinates = features.map(i => turf.coordAll(i)[0]);
          if (coordinates.length == 1) coordinates = turf.coordAll(features[0]);
          callback(turf.lineString(coordinates));
        }
      }

      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: this.map,
          lngLat: [0, 0],
          height: 13,
          width: 13,
          size: 13,
          isDrag: false,
          cursor: 'default',
        };
        return new mapboxgl.Marker().setLngLat(markerParam.lngLat).setDraggable(false).addTo(markerParam.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, isP = true, callback = () => {}) {
      this.layerAreaList.push(layerId);
      var isMeasure = true;
      const map = this.map;
      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]);
      if (isP) tooltip.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;
        if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
        if (map.getLayer('line-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 (isP && 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(5) + '平方公里';
        }
        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 = [];
        if (map.getLayer('points-area' + layerId)) map.removeLayer('points-area' + layerId);
        if (map.getLayer('line-area' + layerId)) map.removeLayer('line-area' + layerId);
        if (map.getLayer('line-area-stroke' + 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);
          if (map.getLayer('point-move' + layerId)) map.removeLayer('point-move' + layerId);
          if (map.getLayer('line-move' + layerId)) map.removeLayer('line-move' + layerId);
          map.getCanvas().style.cursor = 'default';
          tooltip.remove();
          callback(map.getSource('line-area' + layerId)._data);
        }
      }

      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: this.map,
          lngLat: [0, 0],
          height: 13,
          width: 13,
          size: 13,
          isDrag: false,
          cursor: 'default',
        };
        return new mapboxgl.Marker().setLngLat(markerParam.lngLat).setDraggable(false).addTo(markerParam.map);
      }

      // 更新点集
      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;
      }
    }

    drawPoint(layerId, callback = () => {}) {
      this.layerPointList.push(layerId);
      const map = this.map;
      map.doubleClickZoom.disable();
      const jsonPoint = {
        type: 'FeatureCollection',
        features: [],
      };
      map.addSource('points' + layerId, {
        type: 'geojson',
        data: jsonPoint,
      });
      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',
        },
      });

      /**
       * 添加点
       * @param {*} _e
       */
      function addPointforJSON(_e) {
        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);
        map.off('click', addPointforJSON);
        callback(point);
      }
      map.on('click', addPointforJSON);
    }

    drawRectangle(layerId, callback = () => {}) {
      this.layerPointList.push(layerId);
      const map = this.map;
      map.doubleClickZoom.disable();
      const jsonPoint = {
        type: 'FeatureCollection',
        features: [],
      };
      const jsonArea = {
        type: 'FeatureCollection',
        features: [],
      };
      map.addSource('points' + layerId, {
        type: 'geojson',
        data: jsonPoint,
      });
      map.addSource('line-area' + layerId, {
        type: 'geojson',
        data: jsonArea,
      });
      map.addLayer({
        id: 'line-area' + layerId,
        type: 'fill',
        source: 'line-area' + layerId,
        paint: {
          'fill-color': '#ff0000',
          'fill-opacity': 0.1,
        },
      });
      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',
        },
      });

      /**
       * 添加点
       * @param {*} _e
       */
      function addPointforJSON(_e) {
        const point = {
          type: 'Feature',
          geometry: {
            type: 'Point',
            coordinates: [_e.lngLat.lng, _e.lngLat.lat],
          },
          properties: {
            id: String(new Date().getTime()),
          },
        };
        jsonPoint.features.push(point);
        if (jsonPoint.features.length == 2) {
          map.off('click', addPointforJSON);
          map.off('mousemove', mouseRectangle);
          callback(jsonArea.features[0]);
        }
        map.getSource('points' + layerId).setData(jsonPoint);
      }

      function mouseRectangle(_e) {
        if (!jsonPoint.features.length) {
          const point = {
            type: 'Feature',
            geometry: {
              type: 'Point',
              coordinates: [_e.lngLat.lng, _e.lngLat.lat],
            },
            properties: {
              id: String(new Date().getTime()),
            },
          };
          return map.getSource('points' + layerId).setData({ type: 'FeatureCollection', features: [point] });
        }
        let coordinates = jsonPoint.features[0].geometry.coordinates;
        let bbox = coordinates.concat([_e.lngLat.lng, _e.lngLat.lat]).sort((a, b) => a - b);
        let polygon = turf.bboxPolygon([bbox[2], bbox[0], bbox[3], bbox[1]]);
        jsonArea.features = [polygon];
        map.getSource('line-area' + layerId).setData(jsonArea);
      }

      map.on('click', addPointforJSON);

      map.on('mousemove', mouseRectangle);
    }

    /**
     * 清除所有测量要素
     */
    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 {
            if (this.map.getLayer('points' + layerid)) this.map.removeLayer('points' + layerid);
            if (this.map.getLayer('line' + layerid)) this.map.removeLayer('line' + layerid);
            if (this.map.getLayer('point-move' + layerid)) this.map.removeLayer('point-move' + layerid);
            if (this.map.getLayer('line-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 {
            if (this.map.getLayer('points-area' + layerid)) this.map.removeLayer('points-area' + layerid);
            if (this.map.getLayer('line-area' + layerid)) this.map.removeLayer('line-area' + layerid);
            if (this.map.getLayer('line-area-stroke' + layerid)) this.map.removeLayer('line-area-stroke' + layerid);
            if (this.map.getLayer('point-move' + layerid)) this.map.removeLayer('point-move' + layerid);
            if (this.map.getLayer('line-move' + layerid)) this.map.removeLayer('line-move' + layerid);
          } catch (error) {
            console.log(error);
          }
        }
      }
      this.layerAreaList = [];
      if (this.layerPointList) {
        for (let i = 0; i < this.layerPointList.length; i++) {
          const layerid = this.layerPointList[i];
          try {
            if (this.map.getLayer('points' + layerid)) this.map.removeLayer('points' + layerid);
            if (this.map.getLayer('line-area' + layerid)) this.map.removeLayer('line-area' + layerid);
          } catch (error) {
            console.log(error);
          }
        }
      }
      this.layerPointList = [];
      this.map.doubleClickZoom.enable();
    }
  };
});