leaflet 实现模态框点位选取线路

🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:gis实践
🌈 若有帮助,还请关注点赞收藏,不行的话我再努努力💪💪💪

需求背景

需要封装一个可在模态框里实现点位选取线路的组件

  1. 在模态框里,需要随时打开和关闭,选择轻量的leaflet框架
  2. 需要对点位解析选取编号
  3. 对选好的路线进行连线
  4. 需要通过删除,和拖拽改变路线

实现效果

index.vue

/**
* @author: liuk
* @date: 2025-03-01
* @describe:地图点位选取路线
*/
<template>
  <div class="leafletMapSelectLine-wrap">
    <el-form-item label="巡检路点位" label-width="100" prop="pointList">
      <el-tag v-for="(item,index) in lineArr" :key="item.id" closable @close="closeFn(item)"
              :draggable="true" @dragenter="dragenter(index)"
              @dragend="dragend(item,index)" style="margin-right: 10px">
        <span class="index">{{ index + 1 }}</span>
        <span>{{ item.name }}</span>
      </el-tag>
    </el-form-item>
    <div ref="leafletMapRef" class="leafletMap"></div>
  </div>
</template>

<script lang="ts" setup>
import {reactive, toRefs, onMounted, ref, defineEmits, computed,watch, defineProps, defineExpose,nextTick} from "vue";
import L from "leaflet"
import "leaflet/dist/leaflet.css"
import 'leaflet-draw/dist/leaflet.draw.css'
import 'leaflet-draw'
import 'leaflet-fullscreen'
import 'leaflet-fullscreen/dist/leaflet.fullscreen.css'

// Props
const props = defineProps(['modelValue', 'irrigatedAreaId', 'type'])

// Emit
const emit = defineEmits(['update:modelValue'])

// ref
const leafletMapRef = ref(null)


const model = reactive({
  posData: [
    {id: "efdcb5294c7e46059d0367e8f81c18d9",name:"站点1", longitude: 112.276154, latitude: 30.09151},
    {id: "957ecf37afbe45ae87d236e0b221c71e",name:"站点2", longitude: 112.307053, latitude: 30.116229},
    {id: "25c7176818bf442e8a7f1aa31d944ebb",name:"站点3", longitude: 112.183456, latitude: 30.07503},
    {id: "11b18ecc9d3048a9bab0973de277405d",name:"站点4", longitude: 112.127838, latitude: 30.107989}
  ],
  moveIndex: 0,
  lineArr:[]
})
const {lineArr} = toRefs(model)

onMounted(() => {
  nextTick(() => {
    initLeaflet()
  })
})

watch(() => [props.type, props.irrigatedAreaId], () => {
  getlist()
})

const dragenter = (index) => {
  model.moveIndex = index
}

const dragend = (val, index) => {
  lineArr.value.splice(index, 1)
  lineArr.value.splice(model.moveIndex, 0, val)
  lineArr.value.forEach((item, index) => item.index = index + 1)
  nextTick(() => {
    updateMarkers()
  })
}

const getlist = async () => {
  layerGroup?.remove()
  addEntity()
  if (lineArr.value) updateMarkers()
}

const closeFn = (row) => {
  lineArr.value = lineArr.value.filter(item => item.id !== row.id).map(item => ({
    ...item, index: row.index < item.index ? item.index - 1 : item.index
  }))
  nextTick(() => {
    updateMarkers()
  })
}

// 地图逻辑
let map, layerGroup, polyline
const initLeaflet = () => {
  map = L.map(leafletMapRef.value,
      {
        crs: L.CRS.EPSG4326,
        attributionControl: false,
        fullscreenControl: {
          pseudoFullscreen: false
        }
      }
  )
  L.tileLayer('http://t0.tianditu.gov.cn/DataServer?T=vec_c&x={x}&y={y}&l={z}&tk=' + import.meta.env.VITE_APP_TDT_KEY, {
    tileSize: 256,
    zoomOffset: 1
  }).addTo(map);
  L.tileLayer('http://t0.tianditu.gov.cn/DataServer?T=cva_c&x={x}&y={y}&l={z}&tk=' + import.meta.env.VITE_APP_TDT_KEY, {
    tileSize: 256,
    zoomOffset: 1
  }).addTo(map);

  map.setView([30.0609391, 112.2240645], 10)
  getlist()
}

const addEntity = () => {
  const markers = model.posData.map(item => {
    const className = 'leaflet-div-icon' + item.id
    const myIcon = L.divIcon({
      className,
      iconSize: [20, 32],
      iconAnchor: [10, 32]
    });
    const popup = L.popup({
      minWidth: 30,
      autoPanPadding: [1, 1],
      className: 'leaflet-div-icon-popup',
      closeButton: false
    }).setContent(item.name)
    return L.marker([+item.latitude, +item.longitude], {
      id: item.id,
      name: item.name,
      longitude: item.longitude,
      latitude: item.latitude,
      icon: myIcon,
      divIconClassName: className
    })
        .bindPopup(popup)
        .on('click', (e) => {
          const {id, name, longitude, latitude} = e.target.options
          if (lineArr.value.find(item=>item.id === id)) return
          e.target.options.index = lineArr.value.length + 1
          lineArr.value.push({id, name, longitude, latitude, index: lineArr.value.length + 1})
          updateMarkers()
        });
  })
  layerGroup = L.layerGroup(markers).addTo(map)
}

const updateMarkers = () => {
  const layers = layerGroup.getLayers()
  layers.forEach(layer => {
    const {divIconClassName, id} = layer.options
    const index = lineArr.value.find(item => id === item.id)?.index
    if (!index) layer.options.index = ''
    const doms = [...document.getElementsByClassName(divIconClassName)]
    doms.forEach(dom => dom.innerHTML = index || '')
  })
  polyline?.remove()
  if (lineArr.value.length < 2) return
  polyline = L.polyline(lineArr.value.map(item => [item.latitude, item.longitude]), {color: '#409eff'}).addTo(map);
}

</script>

<style lang="scss" scoped>
.leafletMapSelectLine-wrap {
  width: 100%;
  height: 100%;
  position: relative;

  .leafletMap {
    width: 100%;
    height: calc(100% - 50px);

    &.isFullScreen {
      position: fixed;
      left: 0;
      top: 0;
      width: 100vw;
      height: 100vh;
    }
  }

  .index {
    color: blue;
    margin-right: 5px;
  }
}
</style>

<style lang="scss">
.leaflet-marker-icon {
  width: 20px;
  height: 30px;
  cursor: pointer;
  font-size: 20px;
  line-height: 32px;
  text-align: center;
  color: #000;
  background: url("@/assets/images/icons/marker-icon.png") no-repeat center/cover;
}

.leaflet-div-icon-popup {
  margin-bottom: 50px;

  .leaflet-popup-content-wrapper {
    border-radius: 5px;

    .leaflet-popup-content {
      margin: 5px;
      text-align: center;
    }
  }

}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值