leaflet学习笔记-缓冲区绘制(六)

发布时间:2024年01月10日

前言

在GIS开发中,缓冲区的绘制和使用是非常广泛的,一般情况下就是对缓冲区范围内的要素做分析使用,也会有一些其他的操作,下面我就记录一下使用leaflet+turf.js完成缓冲区的绘制操作

turf.js简介

Turf.js 是一个用于地理空间计算的 JavaScript 库。它提供了许多地理空间操作的函数,如点线面的创建、缓冲区计算、距离计算、区域合并等,方便在前端应用中处理地理空间数据和实现地图相关功能。Turf.js 不依赖于任何地图库,可以在任何 JavaScript 环境中使用。

绘制缓冲区主要使用buffer方法函数返回缓冲后的几何数据,官网例子如下

var point = turf.point([-90.548630, 14.616599]);
var buffered = turf.buffer(point, 500, {units: 'miles'});

其中需要传入的参数

geojson:要进行缓冲的输入

radius:绘制缓冲区的距离(允许负值)

options:{

units:turf库单位支持的任何选项(“meters” | “millimeters” | “centimeters” | “kilometers” | “acres” | “miles” | “nauticalmiles” | “inches” | “yards” | “feet” | “radians” | “degrees” | “hectares”

steps:频数

}

原理

绘制缓冲区的操作流程是按压拖拽鼠标,监听鼠标移动轨迹并绘制polyline,当松开鼠标的时候就进行缓冲区的绘制;所以我们在这个操作流程中需要获取拖拽产生的drawLine,并将它作为(geojson)输入项传到turf.js的buffer函数中,添加缓冲距离(radius)和单位(units)最后得到缓冲后的数据,并将它加入一些样式后添加到地图上显示。

代码

直接上代码,按上面的操作流程和代码注释很容易理解

UseBuffer.js完整代码

/**
 * @ClassName UseBuffer.js
 * @Description 用于缓冲区的操作使用
 * @Author ZhangJun
 * @Date  2024/1/8 13:12
 **/
import 'leaflet.pm'
import 'leaflet.pm/dist/leaflet.pm.css'
import * as turf from '@turf/turf'
import { onUnmounted, reactive, ref } from 'vue'

export default function useBuffer(mainMap, distance = 0, units = 'kilometers', geometryType = 'Line', drawLayer=null) {

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

  // 记录当前状态为按住状态
  let isDragging = true

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

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

  //缓冲后的feature
  let bufferFeature = reactive(null)

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

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

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

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

      //按下鼠标开始拖拽
      mainMap.on('mousedown', (e) => {
        isDragging = true
        //清空原来的绘制路径
        drawPath = []

        //添加绘制line
        drawLine = L.polyline(drawPath, { color: 'red' }).addTo(mainMap)
      })

      mainMap.on('mousemove', (e) => {
        if (isDragging) {
          let { lat, lng } = e.latlng
          drawPath = [...drawPath, [lat, lng]]
          drawLine?.setLatLngs(drawPath)
        }
        mouseEventPopup?.setLatLng(e.latlng)?.setContent(isDragging ? '鼠标抬起完成绘制' : '鼠标按压拖拽绘制')

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

      //松开鼠标结束拖拽(绘制结束)
      mainMap.on('mouseup', (e) => {
        status.value = 'end'
        isDragging = false
        // mainMap.off('mousemove')

        mainMap?.removeLayer(drawLine)

        //通过绘制的polyline获取缓冲的feature
        refreshBuffer()
      })
    }

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

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

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

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

    /**
     * @Description 刷新缓冲区
     * @Param distance1 缓冲距离
     * @Param units1 缓冲区单位
     * @Author ZhangJun
     * @Date  2024-01-08 05:43:01
     * @return void
     **/
    let refreshBuffer = (distance1 = distance, units1 = units) => {
      if (distance1 !== distance) {
        distance = distance1
      }
      if (units1 !== units) {
        units = units1
      }

      let sourceGeoJSON = drawLine?.toGeoJSON()
      if (sourceGeoJSON) {
        bufferFeature?.remove()
        bufferFeature = generationBuffer(sourceGeoJSON, distance1, units1)
        drawLayer?.clearLayers()
        addLayersToDrawLayer([bufferFeature])
      }
    }

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

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

    //获取缓冲区的坐标集合
    let getBufferCoords = () => {
      if (bufferFeature) {
        //获取输入 feature 并将它们的所有坐标从 [x, y] 翻转为 [y, x]。
        let featureCollection = turf.flip(bufferFeature.toGeoJSON())
        return featureCollection?.features?.map(feature => turf.getCoords(feature))
      }
      return []
    }

    return { status, refreshBuffer, getBufferCoords, closeDraw, drawLayer, startDraw }
  }

  return {}
}

/**
 * @Description 生成缓冲区的geoJson
 * @Param sourceGeoJSON 需要被进行缓冲分析的geoJson
 * @Param distance 缓冲距离
 * @Param units 缓冲范围的距离单位("meters" | "millimeters" | "centimeters" | "kilometers" | "acres" | "miles" | "nauticalmiles" | "inches" | "yards" | "feet" | "radians" | "degrees" | "hectares")
 * @Param bufferStyle 缓冲后的样式
 * @Author ZhangJun
 * @Date  2024-01-08 02:37:05
 **/
function generationBuffer(sourceGeoJSON, distance = 0, units = 'meters', bufferStyle = {
  color: 'green',
  dashArray: [5, 5],
  weight: 2,
  fillColor: 'green',
  fillOpacity: 0.2,
}) {
  if (sourceGeoJSON) {
    //生成缓冲后的geoJSON
    const buffered = turf.buffer(sourceGeoJSON, distance, {
      units,
    })

    if (buffered) {
      return L.geoJSON(buffered, {
        style: function(feature) {
          return bufferStyle
        },
      })
    }
  }
  return null
}

代码结构是我学习vue3的自定义hook的写法,用起来很好用

UseBuffer.js使用

代码如下

<template>
  <el-form ref="ruleFormRef" class="p-2" :model="ruleForm" status-icon label-width="80px">
    <el-form-item label="影响半径" prop="winRadius">
      <el-input-number v-model="ruleForm.winRadius" :min="0" class="mr-1"></el-input-number>
      <el-text>公里</el-text>
    </el-form-item>
    <el-form-item>
      <el-button type="success" :disabled="status.value !== 'end'" @click="submitForm(ruleFormRef)">
        <el-icon :size="20" style="margin-right: 5px">
          <CircleCheckFilled />
        </el-icon>
        应用
      </el-button>
    </el-form-item>
  </el-form>
</template>

<script setup>
import { ref, reactive, watch, onUnmounted } from 'vue'
import { useStore } from 'vuex'
import { CircleCheckFilled } from '@element-plus/icons-vue'
import useBuffer from '/public/js/UseBuffer'

let store = useStore()

const ruleFormRef = ref(null) // 注意:一定要定义 form 表单中 ref 的 ruleFormRef 的值,否则会一直报错;

const ruleForm = reactive({
  winRadius: 10,
})

let status = ref('')

let getBufferCoords = reactive(null)

//添加缓冲区的函数
let addBuffer = reactive(null)

let closeDraw = null

// 此时是:提交表单的操作;
const submitForm = () => {
  if (!ruleFormRef.value) return
  ruleFormRef.value.validate(valid => {
    // 注意:此时使用的是 ruleFormRef.value,而仅写 ruleFormRef 是拿不到值且会报错的;
    if (valid) {
      // 注意:只有当所有的规则都满足后,此时的 valid 的值才为 true,才能执行下面的值;
      console.log('submit!')
      alert(JSON.stringify(getBufferCoords()))
    } else {
      console.log('error submit!')
      return false
    }
  })
}

const wiz_map = store.getters.GET_WIZ_MAP
if (wiz_map?.map) {
  let { refreshBuffer, status: temp, getBufferCoords: getCoords, closeDraw: close, startDraw } = useBuffer(wiz_map?.map, ruleForm.winRadius)
  //当前绘制状态(是否完成绘制)
  status.value = temp
  //刷新绘制缓冲区
  addBuffer = refreshBuffer
  //获取缓冲区的坐标集合
  getBufferCoords = getCoords
  //关闭绘制功能函数
  closeDraw = close

  startDraw()
}

//监听缓冲半径的变化,进行缓冲区刷新
watch(
  () => ruleForm.winRadius,
  (newVal, oldVal) => {
    if (newVal !== oldVal) {
      addBuffer(newVal)
    }
  },
)
</script>

<style lang="scss" scoped>
.el-input-group {
  width: 130px !important;
}
</style>

由于为了提示操作方式,我添加了自定义的popup,并添加了一个class给这个popup

 let mouseEventPopup = new L.popup({ className: 'customPopup', closeButton: false })

所以需要添加一个全局的样式,改变默认的popup的样式

<style lang='scss'>
/*自定义leafLet的popup的样式*/
.customPopup {
  .leaflet-popup-content-wrapper {
    background: rgba(255, 255, 255, 0.8);
    border-radius: 4px;

    .leaflet-popup-content {
      margin: 6px;
    }
  }

  .leaflet-popup-tip {
    background: rgba(255, 255, 255, 0.8);
  }
}
</style>

效果如下

可以直接调整影响半径,缓冲区会动态绘制


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

文章来源:https://blog.csdn.net/u012917880/article/details/135508008
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。