Vue CLI and Leaflet (8)地圖量測

1、功能分析

地圖的量測功能,是地理信息系統中的經常使用功能。主要量測地圖上的距離,面積,或者線要素的長度,面要素周長和麪積。javascript

這篇文章將介紹第一個基於 leaflet.js 實現地圖量測插件 leaflet-measure-path 。使用這個插件的緣由是它簡單好用,若是你看了以前的文章或掌握了 leaflet 中圖形加載與繪製,藉助這個插件可以快速的實現地圖量測功能。css

1)leaflet-measure-path 插件

插件的介紹以下,詳細信息查詢 leaflet-measure-path官方示例vue

A plugin to show measurements on paths (polylines, polygons and circles currently supported).java

一個在路徑的上現實測量結果的插件(目前支持線,面 和 圓圈)。node

此插件實現的量測的方式是,對 leaflet.js 中的 polyline, polygoncircle 三中圖形類作了 量測數據獲取與展現等相關功能 拓展。有點就是簡單易用,缺點目前只以爲在線要素的長度信息標註上有時候會出現比較不合理的狀況。git

2) 量測功能分析

根據個人對這個功能的理解,我將常見的量測功能分爲如下量測。github

(1) 已知圖形的量測

地圖上已經存在線,面等要素,須要獲取到其長度貨面積。功能的效果就是,直接地圖上加載圖形並顯示出圖形的量測信息。如 官方示例 中的效果:bash

static-measurement

(2) 動態量測

動態量測實際上就是咱們常常看到的量測功能,這裏主要是爲了上面的量測方式作區別。在用戶繪製的過程當中實時顯示量測信息。這個功能須要以繪製功能爲基礎,能夠參考之前的文章:Vue-CLI and Leaflet(6): 線 繪製Vue CLI and Leaflet (7) 面繪製less

dynamic-measurement

2、代碼實現

在實現功能以前必定要了解熟悉 leaflet-measure-path 插件的用法。還有全部的代碼都是基於第一篇文章中建好的目錄結構中添加的,請參考Vue-CLI and Leaflet (1):顯示一個地圖ssh

1)插件的引入

leaflet-measure-path 插件引入很簡單,若是是初學者可能會在對 Vue-CLI 中引用插件遇到問題。如下是引入方式。

// src\utils\map.js
import $L from "leaflet";
import "leaflet/dist/leaflet.css";

// 注意:
// 必定要在 leaflet.js 成功引入以後,在引用此插件
import "leaflet-measure-path";
import "leaflet-measure-path/leaflet-measure-path.css";

複製代碼

2)已知圖形的量測

首先介紹簡單的已知圖形的量測信息的顯示。插件引入成功以後,leaflet.js 中的 polyline, polygoncircle 要素就已經得到了 leaflet-measure-path 插件 的支持。顯示圖形量測信息的方法就是在建立要素時,開啓量測信息展現的屬性。

我在工程的 views 目錄線新建了一個 MeasureStatic.vue

// src\views\MeasureStatic.vue

<template>
  <div class="map-container">
    <div id="map-container"></div>
  </div>
</template>

<script>
export default {
  name: "map-point",
  data() {
    return {
      map: null,
      OSMUrl: "http://tile.osm.org/{z}/{x}/{y}.png",
      overLayer: {
        polylines: [],
        polygons: [],
        measurements: []
      },
      existLine: [
        [51.49404942257001, -0.14136314392089847],
        [51.48640717153227, -0.12797355651855472],
        [51.48640717153227, -0.12797355651855472],
        [51.48464339321334, -0.10376930236816408],
        [51.48464339321334, -0.10376930236816408],
        [51.486353724706795, -0.09801864624023438],
        [51.486353724706795, -0.09801864624023438],
        [51.486353724706795, -0.09801864624023438]
      ],
      existLinePolygon: [
        [51.497175428776025, -0.11269569396972658],
        [51.48825104864774, -0.1149272918701172],
        [51.48825104864774, -0.1149272918701172],
        [51.48723558931616, -0.09758949279785158],
        [51.48723558931616, -0.09758949279785158],
        [51.49450364191158, -0.09282588958740236],
        [51.49450364191158, -0.09282588958740236],
        [51.499980636437265, -0.10437011718750001],
        [51.499980636437265, -0.10437011718750001],
        [51.499980636437265, -0.10437011718750001]
      ]
    };
  },
  mounted() {
    this.map = this.$utils.map.createMap("map-container");
    this.map.setView([51.505, -0.09], 14);
    this.$utils.map.createTileLayer(this.map, this.OSMUrl, {});
    let existLineOpts = {
      color: "rgba(255, 58, 0, 1)",
      weight: 4,
      opacity: 1,
      // 是否顯示量測信息
      showMeasurements: true,
      // 量測信息的屬性:
      measurementOptions: { 
          minDistance: 30 // 顯示 最小的線長度爲30m(默認),小於此長度則不顯示
      }
    };
      
    this.$utils.map.createPolyline(this.map, this.existLine, existLineOpts);

    let existPolygonOpts = {
      color: "rgba(0, 58, 255, 0.85)",
      weight: 2,
      opacity: 0.85,
      // 是否顯示量測信息
      showMeasurements: true,
      // 量測信息的屬性:
      measurementOptions: { 
          showOnHover: true // 開始鼠標移動到圖形上時顯示量測信息
      }
    };
    this.$utils.map.createPolygon(
      this.map,
      this.existLinePolygon,
      existPolygonOpts
    );
  }
};
</script>
<style lang="less">
.map-container {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;

  #map-container {
    width: 100%;
    height: 100%;
  }
}
</style>
複製代碼

此外,插件還提供了 formatDistanceformatDistance 用來自定義量測結果的標註樣式。

3)動態量測

動態量測的關鍵仍是在圖形繪製上。如下的實現方式是在基於之前的繪製功能實現的。若是不熟悉繪製功能的實現方式,請移步至: Vue-CLI and Leaflet(6): 線 繪製Vue CLI and Leaflet (7) 面繪製

實現關鍵是,在繪製圖形的繪製工程中對全部須要建立的圖形像上面同樣設置正確的 showMeasurements: true, measurementOptions: { ... } 屬性。爲了讓文章過長,如下以距離量測做爲代碼示例, 完整量測功能請參看在 github 中參看源碼:

新增 量測開關組件 組件。

// src\components\MapMeasureDistance.vue
<template>
  <div class="map-tool-measure">
    <ul>
      <li @click="$emit('polyline')">動態線-量測</li>
      <li @click="$emit('showMeasurements')">顯示-量測結果</li>
      <li @click="$emit('hideMeasurements')">隱藏-量測結果</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "map-measure-dsitance"
};
</script>
<style lang="less">
.map-tool-measure {
  position: absolute;
  right: 15px;
  top: 15px;
  z-index: 999;

  height: 36px;
  box-shadow: 0px 0px 50px 2px rgba(0, 0, 0, 0.35);
  background-color: #fff;
  ul {
    display: flex;
    padding: 0;
    margin: 0;
    list-style: none;

    li {
      padding: 0 15px;
      height: 36px;
      font-size: 13px;
      line-height: 36px;
      cursor: pointer;
    }

    li:hover {
      background-color: rgb(212, 224, 246);
    }
  }
}
</style>
複製代碼

新增 距離量測組件 組件

// src\views\MeasureDistance.vue

<template>
  <div class="map-container">
    <div id="map-container"></div>
    <MapMeasureDistance
      @polyline="measurePolyline"
      @showMeasurements="showMeasurements"
      @hideMeasurements="hideMeasurements"
    ></MapMeasureDistance>
  </div>
</template>

<script>
import MapMeasureDistance from "@/components/MapMeasureDistance.vue";

export default {
  name: "map-point",
  components: { MapMeasureDistance },
  data() {
    return {
      map: null,
      OSMUrl: "http://tile.osm.org/{z}/{x}/{y}.png",
      overLayer: {
        polylines: [],
        polygons: [],
        measurements: []
      },
      tempGp: {
        lineNode: [],
        lineNodeLen: 0,
        tempLine: null,
        tempNode: []
      }
    };
  },
  mounted() {
    this.map = this.$utils.map.createMap("map-container");
    this.map.setView([51.505, -0.09], 14);
    this.$utils.map.createTileLayer(this.map, this.OSMUrl, {});
  },
  methods: {
    drawOn() {
      this.clearTemps();
      this.map.doubleClickZoom.disable();

      // 移除監聽地圖事件
      this.map.off("click");
      this.map.off("mousemove");
      this.map.off("dblclick");
    },
    drawOff() {
      // 移除監聽地圖點擊事件
      this.map.off("click");
      this.map.off("mousemove");
      this.map.off("dblclick");
      this.map.doubleClickZoom.enable();

      // 復原鼠標平移樣式
      this.$utils.map.removerCoursorStyle(this.map);
    },
    clearTemps() {
      // 清空面中間數據
      this.tempGp.lineNode = [];
      this.tempGp.lineNodeLen = 0;
      if (this.tempGp.tempLine) this.tempGp.tempLine.remove();
      this.tempGp.tempNode.map(el => el.remove());
    },
    measurePolyline() {
      this.$utils.map.addCursorStyle(this.map, "crosshare-cursor");
      this.drawOn();

      let tempPolygonOpts = {
        color: "rgba(255, 0, 0, 0.85)",
        weight: 3,
        opacity: 0.85,
        // 添加量測信息屬性,並開啓
        showMeasurements: true,
        // 根據須要設置量測顯示的參數
        measurementOptions: { minDistance: 500 }
      };

      let finalPolygonOpts = {
        color: "rgba(0, 255, 0, 0.85)",
        weight: 3,
        opacity: 0.85,
          // 同上
         // 添加量測信息屬性,並開啓
        showMeasurements: true,
         // 根據須要設置量測顯示的參數
        measurementOptions: { minDistance: 500 }
      };
      this.map.on("click", evt => {
        this.tempGp.lineNode.push([evt.latlng.lat, evt.latlng.lng]);
        this.tempGp.tempNode.push(this.addNode(evt.latlng, this.map));
        this.tempGp.lineNodeLen = this.tempGp.lineNode.length;
      });

      this.map.on("mousemove", evt => {
        if (this.tempGp.lineNodeLen >= 1) {
          if (this.tempGp.tempLine) this.tempGp.tempLine.remove();
          this.tempGp.lineNode[this.tempGp.lineNodeLen] = [
            evt.latlng.lat,
            evt.latlng.lng
          ];

          this.tempGp.tempLine = this.$utils.map.createPolyline(
            this.map,
            this.tempGp.lineNode,
            tempPolygonOpts
          );
        }
      });

      this.map.on("dblclick", () => {
        let polyline = this.$utils.map.createPolyline(
          this.map,
          this.tempGp.lineNode,
          finalPolygonOpts
        );
        this.overLayer.polylines.push(polyline);
        this.clearTemps();
      });
    },
    addNode(latlng, map) {
      let node = this.$utils.map.createIcon({
        iconUrl: require("./../assets/images/node.png"),
        iconSize: [10, 10]
      });
      node = this.$utils.map.createMakerByLatlng(latlng, {
        icon: node
      });
      node.addTo(map);
      return node;
    },
    showMeasurements() {
      this.overLayer.polylines.map(el => el.showMeasurements());
    },
    hideMeasurements() {
      this.overLayer.polylines.map(el => el.hideMeasurements());
    }
  }
};
</script>
<style lang="less">
.map-container {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;

  #map-container {
    width: 100%;
    height: 100%;
  }
}
</style>
複製代碼

這樣動態量測功能就實現了。

dynamic-measurement2

4)控制量測信息的顯示與隱藏

leaflet-measure-path 插件 提供了 showMeasurements,hideMeasurements 方法,可實現量測信息的顯示控制。使用方法爲:

// feature 爲已經加載在地圖中的 線,面或圓 圖形要素
// measurementOptions 即上文使用過的量測顯示屬性
feature.showMeasurements(measurementOptions)
feature.hideMeasurements()

複製代碼

在上文 2)動態量測 中提供了使用實例,效果以下:

dynamic-measurement3

3、總結

以上就是量測功能的介紹。 leaflet-measure-path 插件,目前是我以爲最爲簡單好用的基於 leaflet.js 的圖形量測插件了。文章實例相對來說是比較簡單的,主要是沒有具體的業務需求。但我相信掌握了這個量測插件的使用方法再結合實際業務需求,必定能實現很是豐富高效的功能。

若是有任何錯誤或疏漏,請各位多多指教。

目錄

(一) Vue-CLI and Leaflet:起步 - 在 Vue-CLI 中使用 Leaflet

(二) Vue-CLI and Leaflet:地圖基本操做(放大,縮小,平移,定位等)

(三) Vue-CLI and Leaflet: 添加 marker, polyline, polygon

(四) Vue-CLI and Leaflet: 添加 tooltips 和 popup

(五) Vue-CLI and Leaflet: 點 繪製

(六) Vue-CLI and Leaflet: 線 繪製

(七) Vue-CLI and Leaflet: 面 繪 制

(八) Vue-CLI and Leaflet :加載 Esri ArcGIS Map Service

(九) Vue-CLI and Leaflet: 圖層控制基本功能的實現

(十) Vue-CLI and Leaflet: AGS 屬性查詢與點圖查詢

(十一)Vue-CLI and Leaflet: 點聚合 Leaflet.markercluster

源碼請參看 個人GitHub,因爲文章是一邊coding,一邊寫的因此 Github 裏面的源碼可能有點亂,能夠根據功能來找對應的代碼。後面會陸續整理完善。

相關文章
相關標籤/搜索