mapbox测距功能重写

news2024/9/23 15:28:42

// 使用
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);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1545534.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动态内存管理-传值调用错题解析

首先我们来看这个错误代码 首先我们看代码逻辑&#xff0c;首先main函数调用test&#xff0c;test接收的是void类型&#xff0c;设置一个指针变量&#xff0c;指向null&#xff0c;传递给get函数&#xff0c;也就是传递一个空指针给getmemory函数&#xff0c;这个函数接收了&a…

单片机入门到精通:一站式在线学习平台!

介绍&#xff1a;单片机&#xff0c;也称为微控制器&#xff08;MCU&#xff09;&#xff0c;是一种集成了中央处理器&#xff08;CPU&#xff09;、随机存储器&#xff08;RAM&#xff09;、只读存储器&#xff08;ROM&#xff09;以及输入/输出接口于单一芯片上的微型计算机。…

设计模式学习笔记 - 设计模式与范式 -结构型:2.桥接模式:如何实现支持不同类型和渠道的消息推送系统?

概述 今天学习另外一种结构型模式&#xff1a;桥接模式。桥接模式的代码实现非常简单&#xff0c;但是理解起来稍微优点难度&#xff0c;并且应用场景也比较局限&#xff0c;所以&#xff0c;相对于代理模式来说&#xff0c;桥接模式在实际的项目中并没有那么常用&#xff0c;…

Spring文件配置以及获取

前言 我们都知道很多应用都是有配置文件的,可以对应用的一些参数进行配置,如conf... 本篇我们讲解关于Spring的配置文件以及程序怎么获取其中写入的参数 Spring中的配置文件主要有三种 还有yml和ymal文件 下面我们将介绍关于常用的两种 preoperties 和 yml文件的格式和读取…

2024年适合个人和普通企业用户的阿里云服务器推荐,最低仅需61元1年

现在不论是个人还是企业&#xff0c;只要有建站&#xff0c;做APP&#xff0c;存储数据等需要就需要一台云服务器。通常来说&#xff0c;购买阿里云服务器的用户主要分为三类&#xff1a;一是个人用户&#xff0c;二是普通企业用户&#xff0c;三是对云服务器性能有特殊需求的集…

libVLC 视频缩放

libvlc是一个常用的开源多媒体框架&#xff0c;它可以用来播放和处理各种类型的音频和视频文件。如果想要缩放视频&#xff0c;可以通过libvlc提供的API来实现。 //设置视频的缩放比例。 libvlc_video_set_scale() 以下是如何使用 libVLC 设置视频缩放的基本步骤&#xff1a;…

【研发管理】研发管理规范

研发管理规范 目的定义工作职责产品经理项目经理运维负责人研发负责人研发工程师 基本原则研发过程描述需求分析分析设计研发实现测试验收发布上线线上监控 目的 软件研发相关管理&#xff0c;有效控制技术风险&#xff0c;提高研发和运行质量 定义 包括需求分析、分析设计…

智慧公厕的全域感知、全网协同、全业务融合和全场景智慧赋能

公共厕所是城市的重要组成部分&#xff0c;为市民提供基本的生活服务。然而&#xff0c;传统的公厕管理模式存在诸多问题&#xff0c;如排队等候时间长、卫生状况差、空气质量差等&#xff0c;严重影响市民的出行和生活质量。为了解决这些问题&#xff0c;智慧公厕应运而生&…

WebClient上载文件——实现将本地文件同步到远端服务器上

问题描述 用户上传产品示例图片到服务器端上&#xff0c;客户端在请求图片资源时&#xff0c;当服务端架设了多个节点的情况下&#xff0c;由于没有负载均衡请求到保存图片资源的服务器&#xff0c;出现图片访问404的问题。 这里保存上传文件时&#xff0c;同时需要将该文件保…

【学习心得】神经网络知识中的符号解释

这里我对我学到的神经网络知识中&#xff0c;常见的符号做一下记录和总结&#xff0c;方便自己在后面学习中复习。下图二分类识别图像识别猫为例。为了保存一张图片&#xff0c;需要三个矩阵&#xff0c;它们分别对应图片中的红、绿、蓝三种颜色通道&#xff0c;如果图片大小为…

vitepress builld报错

问题&#xff1a;build时报错&#xff1a;document/window is not defined。 背景&#xff1a;使用vitepress展示自定义的组件&#xff0c;之前build是没有问题了&#xff0c;由于新增了qr-code以及quill富文本组件&#xff0c;导致打包时报错。 原因&#xff1a;vitepress官…

邮件接口与第三方平台的集成的方式有哪些?

邮件接口如何实现高效通信&#xff1f;怎么有效地利用邮件接口&#xff1f; 邮件接口与第三方平台的集成已经成为了企业提升工作效率、优化用户体验的关键环节。那么&#xff0c;邮件接口与第三方平台的集成方式究竟有哪些呢&#xff1f;接下来&#xff0c;AokSend就来探讨一下…

力扣由浅至深 每日一题.15 删除排序链表中的重复元素

没关系的&#xff0c;昨天的暴雨不会淋湿今天的自己 —— 24.3.26 删除排序链表中的重复元素 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出…

VR全景展示:传统制造业如何保持竞争优势?

在结束不久的两会上&#xff0c;数字化经济和创新技术再度成为了热门话题。我国制造产业链完备&#xff0c;但是目前依旧面临着市场需求不足、成本传导压力加大等因素影响&#xff0c;那么传统制造业该如何保持竞争优势呢&#xff1f; 在制造行业中&#xff0c;VR全景展示的应用…

markdown 编辑工具Typora的使用

简介 Typora是一款由Abner Lee开发的轻量级Markdown编辑器&#xff0c;它以其简洁美观的界面、实时预览的功能以及强大的Markdown语法支持而受到用户的喜爱。 Typora的编辑方式与众不同&#xff0c;它采用了所见即所得的编辑方式&#xff0c;这意味着用户在输入Markdown语法标…

OpenCV4.9关于矩阵上的掩码操作

返回&#xff1a;OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇:如何使用OpenCV扫描图像、查找表和时间测量 下一篇:OpenCV4.9的是如何进行图像操作 引言&#xff1a; 矩阵上的掩码操作非常简单。这个想法是&#xff0c;我们根据掩码矩阵&#xff08…

简易电路设计,PW1605芯片实现24V/30V/48V限流过压保护功能

一般描述 PW1605 是一款电流限制开关&#xff0c;具有可编程输入过压保护和输出电压箝位功能。集成保护 N 沟道 FET 具有极低的 RDS&#xff08;ON&#xff09; 功能&#xff0c;PW1605有助于降低正常工作期间的功率损耗。可编程软启动时间控制启动期间输出电压的压摆率。独立的…

本周四Techtalk技术交流社区邀请吕海波老师为大家带来精彩技术分享

欢迎您关注我的公众号【尚雷的驿站】 **************************************************************************** 公众号&#xff1a;尚雷的驿站 CSDN &#xff1a;https://blog.csdn.net/shlei5580 墨天轮&#xff1a;https://www.modb.pro/u/2436 PGFans&#xff1a;ht…

Docker - 哲学 默认网络和 自定义网络 与 linux 网络类型 和 overlay2

默认网络&#xff1a;不指定 --nerwork 不指定 网络 run 一个容器时&#xff0c;会直接使用默认的网络桥接器 &#xff08;docker0&#xff09; 自定义网络&#xff1a;指定 --nerwork 让这两台容器互相通信 的前提 - 共享同一个网络 关于 ip addr 显示 ens160 储存驱动 ov…

智慧公厕,运用大数据提升公共厕所管理水平

在现代社会&#xff0c;科技的发展给我们带来了诸多便利&#xff0c;而智慧公厕就是其中之一。智慧公厕运用数据和技术&#xff0c;提升公共厕所的管理水平&#xff0c;为社会生活服务。本文将以智慧公厕源头实力厂家广州中期科技有限公司&#xff0c;遍布全国的众多标杆性案例…