leaflet结合其它插件实现webgis测面测距功能

519 阅读3分钟

最近处理了个webgis相关的功能(拖拽标记点位,测量面积,测量距离)

用的leaflet库

使用的是离线地图

地图是谷歌二维瓦片数据

其他插件:

leaflet-draw(地图测距和测面时绘制线段的功能)
leaflet-measure-path(处理绘制完成的线或者面的显示功能)
leaflet-editable(绘制完成后编辑线段或图形的功能)

以下是对应版本:

   "leaflet": "^1.9.4",   
   "leaflet-draw": "^1.0.4",  
   "leaflet-measure-path": "^1.5.0",   
   "leaflet-editable": "^1.2.0",

效果图如下:

以下便是所有代码:

<template>
  <div class="container">
    <div id="map" @dragover.prevent @drop="onDrop"></div>
    <div class="leftbox">
      <div
        draggable="true"
        class="smallbox"
        @mousedown="onMousedown($event)"
        @dragstart="onDragstart($event)"
        @dragend="onDragEnd($event)"
      >
        按住拖拽
      </div>
      <div class="smallbox" @click="measureDistance">测距</div>
      <div class="smallbox" @click="measureArea">测面</div>
    </div>
  </div>
</template>

<script>
import L from "leaflet"
// 编辑
import "leaflet-editable"
import "leaflet/dist/leaflet.css"
// 绘制
import "leaflet-draw"
import "leaflet-draw/dist/leaflet.draw.css"
// 鹰眼图
import "./assets/minimap/Control.MiniMap.min.css"
import "./assets/minimap/Control.MiniMap.min.js"
// leaflet-measure-path
import "./assets/path/leaflet-measure-path.js"
import "./assets/path/leaflet-measure-path.css"
export default {
  name: "app",
  data() {
    return {
      coordinates: [],
      map: null,
      marker: "",
      lat: "",
      lng: "",
      polyline: null,
      polygon: null,
      drawGroupPolyline: null,
      drawGroupPolygon: null,
      isDrawLine: false,
      isDrawPolygon: false,
      mainMap: null
    }
  },
  computed: {},
  mounted() {
    this.initMap()
    // 获取delBtn按钮
    // 绑定点击事件,使用onclick
  },
  methods: {
    onDrop(event) {
      //获取鼠标所在的经纬度
      let latlng = this.map.mouseEventToLatLng(event)
      console.log(latlng)
      this.addIcon(latlng)
    },
    onMousedown(event) {
      console.log("onMousedown")
    },
    onDragstart(event) {
      console.log("onDragstart")
    },
    onDragEnd(event) {},
    initMap() {
      // 创建地图实例,将其绑定到 id 参数为 "map" 的 DOM 元素上
      this.map = L.map("map", {
        zoomControl: false,
        attributionControl: false,
        editable: true
      })
      // 设置地图中心点坐标(维度,经度)和初始缩放级别
      this.map.setView([22.26, 114.19], 14)
      // 创建图层并添加到地图上
      var stamenLayer = L.tileLayer(
        "http://127.0.0.1:8888/map/{z}/{x}/{y}.png", //自行下载地图瓦片数据后通过nginx部署到服务器或者本地
        {
          attribution: "地图", // 图层属性信息
          minZoom: 12, // 最小缩放级别
          maxZoom: 18 // 最大缩放级别
        }
      ).addTo(this.map)

      // 默认添加一个多边形框选
      let polygon = L.polygon(
        [
          [22.26, 114.19],
          [22.26, 114.2],
          [22.27, 114.2],
          [22.27, 114.19]
        ],
        {
          showMeasurements: true,
          weight: 1
        }
      ).addTo(this.map)
      polygon.enableEdit()
      // 多边形的右击事件
      polygon.on("contextmenu", (e) => {
        L.popup({
          closeButton: false
        })
          .setLatLng(e.latlng)
          .setContent("<button id='delBtn'>删除</button>")
          .openOn(this.map)

        const delBtn = document.querySelector("#delBtn")
        console.log(delBtn)
        delBtn.onclick = () => {
          this.map.removeLayer(e.target)
          // 关闭弹框
          this.map.closePopup()
        }
      })
      // 监听多边形的拖拽编辑事件
      this.map.on(
        "editable:vertex:drag editable:vertex:deleted",
        polygon.updateMeasurements,
        polygon
      )
      // 鹰眼图
      // 定义一个底图,鹰眼图的layer不能和地图上共用一个,否则无法加载
      const minilayer = L.tileLayer(
        `http://127.0.0.1:8888/map/{z}/{x}/{y}.png`,
        {
          minZoom: 12,
          maxZoom: 18
        }
      )
      // 使用鹰眼图,添加到地图上
      const miniMap = new L.Control.MiniMap(minilayer, {
        // 鹰眼图配置项,参数非必须,可以忽略使用默认配置
        width: 300, // 鹰眼图宽度
        height: 200, // 鹰眼图高度
        toggleDisplay: true, // 是否显示最小化按钮
        minimized: false // 是否最小化位置开始
      }).addTo(this.map)

      //监听事件
      this.map.on("click", (e) => {
        let latlng = e.latlng
      })

      //
      this.getData()
    },
    //添加图标
    addIcon(latlng) {
      L.marker([latlng.lat, latlng.lng], {
        icon: new L.divIcon({
          // iconUrl: "@/assets/logo.png",
          className: "lmap-icon",
          iconSize: [48, 48],
          iconAnchor: [12, 12],
          // 标记显示文字和图片,图片使用本地的路径
          html: `<img src=${require("@/assets/logo.png")} class="dot-img"/>`
        }),
        draggable: true
      }).addTo(this.map)
    },
    /**
     * 绘制多边形
     */
    drawPolygon() {
      let map = this.map
      this.polygon = new L.Draw.Polygon(map, {
        shapeOptions: {
          weight: 1,
          color: "#3388ff",
          opacity: 0.8,
          fillColor: "#3388ff"
        },
        showArea: true
      })

      this.polygon.enable()
    },
    /**
     * 绘制线段
     */
    drawPolyline() {
      let map = this.map
      this.polyline = new L.Draw.Polyline(map, {
        shapeOptions: {
          weight: 1,
          color: "#3388ff",
          opacity: 0.8
        }
      })
      this.polyline.enable()
    },
    measureDistance() {
      if (this.polygon) {
        this.polygon.disable()
      }
      if (this.polyline) {
        this.polyline.disable()
      }
      // 距离
      this.isDrawLine = true
      this.drawPolyline()
    },
    measureArea() {
      if (this.polyline) {
        this.polyline.disable()
      }
      if (this.polygon) {
        this.polygon.disable()
      }
      this.isDrawPolygon = true
      this.drawPolygon()
    },
    getData() {
      // 绘制线段或者面时每点击一下的事件
      this.map.on("draw:drawvertex", (e) => {
        const { layers } = e
        // 获取layers._layers对象最后一个属性
        const lastKey = Object.keys(layers._layers)[
          Object.keys(layers._layers).length - 1
        ]
        // 获取layers._layers对象倒数第二个属性
        const flagKey = Object.keys(layers._layers)[
          Object.keys(layers._layers).length - 2
        ]
        // 添加到数组中给leaflet-measure-path绘制最终完成的线段或者面
        this.coordinates.push([
          layers._layers[lastKey]._latlng.lat,
          layers._layers[lastKey]._latlng.lng
        ])
      })

      this.drawGroupPolyline = L.featureGroup().addTo(this.map)
      this.drawGroupPolygon = L.featureGroup().addTo(this.map)
      //绘制时创建事件
      this.map.on(L.Draw.Event.CREATED, (event) => {
        const { layer, layerType } = event
        if (layerType === "polygon") {
          // 添加图层
          this.drawGroupPolygon.addLayer(layer)
        } else if (layerType === "polyline") {
          // 添加图层
          this.drawGroupPolyline.addLayer(layer)
        }
      })
      // 结束绘制监听
      this.map.on(L.Draw.Event.DRAWSTOP, (e) => {
        // 绘制完成后判断绘制的类型
        if (e.layerType == "polyline") {
          let polyline = L.polyline([...this.coordinates], {
            showMeasurements: true,
            measurementOptions: {
              imperial: true,
              // 格式化线段上显示的距离单位
              formatDistance: function (val) {
                return (val / 1000).toFixed(2) + "km"
              }
            },
            // 线条宽度
            weight: 1
          }).addTo(this.map)
          // 可编辑
          polyline.enableEdit()
          // 点击事件
          polyline.on("click", (e) => {
            // 弹框
            L.popup({
              closeButton: false
            })
              .setLatLng(e.latlng)
              .setContent("<button id='delBtn'>删除</button>")
              .openOn(this.map)
            // 监听按钮事件
            const delBtn = document.querySelector("#delBtn")
            console.log(delBtn)
            delBtn.onclick = () => {
              // 删除线段
              this.map.removeLayer(e.target)
              // 关闭弹框
              this.map.closePopup()
            }
          })
          this.map.on(
            "editable:vertex:drag editable:vertex:deleted",
            polyline.updateMeasurements,
            polyline
          )
          // 清除线段绘制时的线段,上方已将绘制完成的显示在地图上
          this.clearMeasureDistance()
          // 清空经纬度数组
          this.coordinates = []
        } else if (e.layerType == "polygon") {
          let polygon = L.polygon([...this.coordinates], {
            showMeasurements: true,
            weight: 1
          }).addTo(this.map)
          polygon.enableEdit()
          polygon.on("contextmenu", (e) => {
            // 停止事件冒泡
            L.popup({
              closeButton: false
            })
              .setLatLng(e.latlng)
              .setContent("<button id='delBtn'>删除</button>")
              .openOn(this.map)

            const delBtn = document.querySelector("#delBtn")
            delBtn.onclick = () => {
              this.map.removeLayer(e.target)
              // 关闭弹框
              this.map.closePopup()
            }
          })
          this.map.on(
            "editable:vertex:drag editable:vertex:deleted",
            polygon.updateMeasurements,
            polygon
          )
          this.clearMeasureArea()
          this.coordinates = []
        }
      })
      //添加画图的提示信息
      L.drawLocal.draw.handlers.polyline = {
        tooltip: {
          start: "点击地图开始画线",
          cont: "继续选择",
          end: "双击完成绘制"
        }
      }
      L.drawLocal.draw.handlers.polygon = {
        tooltip: {
          start: "点击地图开始绘制多边形",
          cont: "继续选择",
          end: "点击第一个顶点完成绘制"
        }
      }
    },
    clearMeasureDistance() {
      // 距离
      this.isDrawLine = false
      if (this.polyline) {
        this.polyline.disable()
      }
      // 下面代码是清除画的点
      let obj = document.getElementsByClassName("my-div-icon")
      for (let i = 0; i < obj.length; i++) {
        obj[i].innerHTML = ""
      }
      // 下面的代码是清除画的线
      if (this.drawGroupPolyline) {
        this.drawGroupPolyline.clearLayers()
      }
    },
    clearMeasureArea() {
      // 面
      this.isDrawPolygon = false
      if (this.polygon) {
        this.polygon.disable()
      }
      let obj = document.getElementsByClassName("my-div-icon")
      for (let i = 0; i < obj.length; i++) {
        obj[i].innerHTML = ""
      }
      if (this.drawGroupPolygon) {
        this.drawGroupPolygon.clearLayers()
      }
    }
  }
}
</script>

<style>
html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

.container {
  height: 100vh;
  width: 100%;
  position: relative;
  #map {
    width: 100%;
    height: 100%;
    .dot-img {
      width: 50px;
      height: 50px;
    }
    .leaflet-marker-icon {
      width: 100px;
    }
    .dot-text {
      font-size: 20px;
      font-weight: 600;
      color: rgb(11, 21, 215);
    }
  }

  .leftbox {
    z-index: 9999;
    width: 100px;
    height: 100vh;
    background-color: #fff;
    position: absolute;
    left: 0;
    top: 0;
    margin-left: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    .smallbox {
      width: 50px;
      height: 50px;
      margin-bottom: 10px;
      background-color: red;
    }
  }
}
.leftbg {
  width: 100%;
  height: 500px;
}

.lengthbg {
  width: 20px;
  height: 20px;
  background: url(../public/favicon.ico) no-repeat;
  background-size: 100% 100%;
  margin-bottom: 0.19rem;
}
.areabg {
  width: 20px;
  height: 20px;
  background: url(../public/favicon.ico) no-repeat;
  background-size: 100% 100%;
}
.lengthbg_select {
  width: 20px;
  height: 20px;
  background: url(../public/favicon.ico) no-repeat;
  background-size: 100% 100%;
  margin-bottom: 0.19rem;
}
.areabg_select {
  width: 20px;
  height: 20px;
  background: url(../public/favicon.ico) no-repeat;
  background-size: 100% 100%;
}
.leaflet-measure-path-measurement {
  font-size: 20px;
  color: red;
}

#delBtn {
  /* 按照element的按钮样式 */
  background-color: #fff;
  border: 1px solid #dcdfe6;
  color: #606266;
  -webkit-appearance: none;
  -moz-appearance: none;
  outline: none;
  cursor: pointer;
  transition: 0.1s;
  font-weight: 500;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  padding: 12px 20px;
  font-size: 14px;
  border-radius: 4px;

  &:hover {
    color: #409eff;
    border-color: #c6e2ff;
    background-color: #ecf5ff;
  }
}
</style>

总结:

总体来说不复杂,网上案例也挺多,对应着leaflet的api文档来处理的话,相对是很轻松的,现在项目中接触到webgis还是挺多的,后期还会继续在这方面处理其他功能