leaflet学习笔记-贝塞尔曲线绘制(八)

前言

两点之间的连线是很常见的,但是都是直直的一条线段,为了使连线更加平滑,我们可以使用曲线进行连线,本功能考虑使用贝塞尔曲线进行连线绘制,最后将线段的两端节点连接,返回一个polygon。

贝塞尔简介

给定不同的点 P0 和 P1,线性贝塞尔曲线只是这两点之间的一条线。

相当于线性插值。

leaflet学习笔记-贝塞尔曲线绘制(八)_第1张图片

Turf.bezierSpline()简介

接受一条线,通过应用贝塞尔样条算法返回一个弯曲的版本

官方例子

var line = turf.lineString([
  [-76.091308, 18.427501],
  [-76.695556, 18.729501],
  [-76.552734, 19.40443],
  [-74.61914, 19.134789],
  [-73.652343, 20.07657],
  [-73.157958, 20.210656]
]);

var curved = turf.bezierSpline(line);

参数说明

line:input LineString

options:{

resolution:点之间的时间(毫秒)

sharpness:衡量样条路径应该有多弯曲的一个度量

}

具体可以查看官方的bezierSpline函数

UseBezierSpline.js完整代码

import { onBeforeUnmount, reactive, ref } from 'vue'
import * as turf from '@turf/turf'

/**
 * @Description 接受一条线,通过应用贝塞尔样条算法返回一个弯曲的版本(贝塞尔曲线)
 * @param mainMap 地图对象
 * @param drawComplete 完成绘制的回调函数
 * @Param bezierOptions
 * @Param bezierOptions.resolution 点之间的时间(毫秒)
 * @Param bezierOptions.sharpness 衡量样条路径应该有多弯曲的一个度量
 * @param drawLayer 绘制图形的layer
 * @Author ZhangJun
 * @Date  2024-01-11 09:55:12
 * @return void
 **/
export default function useBezierSpline(mainMap, drawComplete = null, bezierOptions = {}, drawLayer = undefined) {
  //默认的贝塞尔配置参数合并
  let bezierOptions_config = reactive({
    resolution: 1000,
    sharpness: 0.85,
    ...bezierOptions,
  })

  //绘制事件状态
  let status = ref('start')

  // 记录当前状态是否可以拖动绘制
  let isDragging = true

  //拖动绘制的坐标
  let drawPath = []

  //拖动绘制的线
  let drawLine = reactive(null)

  //鼠标正在移动的点
  let movePoint

  //最终绘制的polygon,最终返回的就是它的轮廓坐标
  let polygon_draw = reactive(null)

  //鼠标提示
  let mouseEventPopup = new L.popup({ className: 'customPopup', closeButton: false })

  if (mainMap) {
    //关闭的时候一定要销毁
    onBeforeUnmount(() => {
      closeDraw()
      mainMap?.removeLayer(drawLayer)
    })

    if (!drawLayer) {
      drawLayer = L.featureGroup([])
      drawLayer.addTo(mainMap)
    }

    //初始化事件
    let initEvents = () => {
      isDragging = false

      //按下鼠标确定需要添加的节点(暂停中)
      mainMap.on('mousedown', (e) => {
        isDragging = false
        let { lat, lng } = e.latlng
        drawPath = [...drawPath, [lng, lat]]

        //如果才开始点击第一次,就创建一个polyline,后面需要动态需改它的path
        if (drawPath?.length === 1) {
          status.value = 'start'
          //添加绘制line
          drawLine = L.polyline(drawPath, { color: 'red' }).addTo(mainMap)
        }
      })

      //松开鼠标开始拖动绘制(开始绘制)
      mainMap.on('mouseup', (e) => {
        isDragging = true
      })

      //鼠标移动绘制(绘制中)
      mainMap.on('mousemove', (e) => {
        if (isDragging) {
          let { lat, lng } = e.latlng
          movePoint = [lng, lat]
          //动态生成贝塞尔曲线的feature
          let splineFeature = generationBezierSpline(drawPath, movePoint)
          if (splineFeature) {
            let tempCoords = turf.getCoords(turf.flip(splineFeature))
            //将生成的贝塞尔曲线的坐标传给polyline,在地图上刷新渲染
            drawLine?.setLatLngs(tempCoords)
          }
        }
        mouseEventPopup?.setLatLng(e.latlng)?.setContent('右键结束绘制')

        //如果还没有添加就直接先添加一下
        if (!mainMap.hasLayer(mouseEventPopup)) {
          //打开方向的popup
          mouseEventPopup?.openOn(mainMap)
        }
      })

      //右键结束(结束绘制)
      mainMap.on('contextmenu', (e) => {
        let coords = turf.getCoords(turf.flip(drawLine.toGeoJSON()))
        //生成polygon
        polygon_draw = L.polygon(coords, { color: 'green' })

        clearDrawLayer()
        addLayersToDrawLayer([polygon_draw])
        //移除曲线
        mainMap?.removeLayer(drawLine)

        status.value = 'end'
        //绘制完成的回调
        if (typeof drawComplete === 'function') {
          let result = getResult(polygon_draw)
          drawComplete(result)
        }
      })

    }

    //移除事件
    let removeEvents = () => {
      //按下鼠标
      mainMap?.off('mousedown')
      //抬起鼠标
      mainMap?.off('mouseup')
      //拖拽事件
      mainMap?.off('mousemove')
      //右键事件
      mainMap?.off('contextmenu')
    }

    //开始绘制
    let startDraw = () => {
      //禁止拖动地图
      mainMap?.dragging?.disable()
      //初始化事件
      initEvents()
    }

    //清除原来绘制的内容
    let clearDrawLayer = () => {
      drawPath = []
      drawLayer?.clearLayers()
    }

    //添加要素到drawLayer
    let addLayersToDrawLayer = (features = []) => {
      features?.forEach(feature => {
        drawLayer.addLayer(feature)
      })
    }

    /**
     * @Description 生成曲线
     * @Param originalPath 已经确定的点坐标集合
     * @Param lastPoint 最后一个坐标点,一般为移动的点坐标
     * @Author ZhangJun
     * @Date  2024-01-11 10:36:11
     * @return void
     **/
    let generationBezierSpline = (originalPath = drawPath, lastPoint = movePoint) => {
      if (originalPath?.length > 0) {
        //加入最后一个点
        let line = turf.lineString([...originalPath, lastPoint], bezierOptions_config)
        return turf.bezierSpline(line)
      }

      return null
    }

    //关闭绘制功能
    let closeDraw = () => {
      //清空绘制的几何
      clearDrawLayer()
      //一定要移除事件,否则事件之间会有干扰
      removeEvents()

      //移除popup
      mainMap?.closePopup(mouseEventPopup)

      //激活拖拽功能
      mainMap?.dragging?.enable()
    }

    //获取最终的polygon的轮廓坐标
    let getResult = (feature = polygon_draw) => {
      if (feature) {
        //获取输入 feature 并将它们的所有坐标从 [x, y] 翻转为 [y, x]。
        let featureCollection = turf.flip(feature.toGeoJSON())
        return turf.getCoords(featureCollection)
      }
      return []
    }

    return { status, getResult, closeDraw, drawLayer, bezierOptions_config, startDraw }
  }

  return {}
}

UseBezierSpline.js使用

if (wizMap?.map) {
  let { startDraw, closeDraw, status: temp, getResult } = useBezierSpline(wizMap?.map)
  getCoords = getResult
  //当前绘制状态(是否完成绘制)
  status.value = temp

  onMounted(() => {
    //需要这种处理,不然会有异常
    nextTick(() => {
      startDraw()
    })
  })
}

效果

贝塞尔曲线


本文为学习笔记,仅供参考

你可能感兴趣的:(leaflet学习笔记,学习,笔记,贝塞尔曲线,leaflet)