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

1、概述

接着上一篇文章,這裏介紹 線繪製 的功能。Leaflet 中由於沒有圖層渲染的相關類,因此再圖形上沒有Geometry 和 Graphic 這兩個概念沒有很明確的劃分。這樣使得在完成圖像功能時,代碼會簡單了許多。如上一篇文章講到的提到的**點,線,面的繪製的關鍵在於根據用戶在的操做上的操做去獲取正確的座標,並將這些座標組織爲不一樣要素類型數據。**三種類型的要素的繪製中相同的地方在於都必須監測到用戶在地圖上所作到操做,不一樣在於對用戶操做結果的響應。javascript

1、功能分析

line-flowwork

線繪製功能流程圖

線繪製功能 在繪製的完整流程如上圖所示。其中有幾個地方,按照個人對功能理解與設計,我以爲下面幾處須要簡單說明一下。vue

1. 橡皮筋效果

橡皮筋效果,其實是在一條線段尚未繪製完成以前,將已有節點與鼠標當前位置鏈接起來的實時預覽。其實,這個效果不是必須的。爲了對用戶更加友好,幾乎全部關於繪製的功能,包括二維和三維的GIS 平臺的,都將會提供這樣的效果。java

elastic

2. 取消和完成繪製

取消,是結束以前,用戶若是不想保留當前結果並推出繪製狀態。node

完成繪製,保留當前繪製結果不退出繪製狀態。git

*這裏是指個人功能設計你將看到的狀況。github

2、代碼實現

*全部的代碼都是在第一章的項目結構中添加或者修改的。bash

代碼仍是在上一篇文章提到的 MapDraw.vuemap.js 、和新建的Polyline.vue 中。先看看全部代碼,再對某些部分進行說明。爲了使文章和源碼的能 一 一對應,我這裏把 Point 和 Polygon 暫時屏蔽掉了。less

// src\components\MapDraw.vue

<template>
  <div class="map-tools">
    <ul>
      <li :class="[{active: activeTool == 'point'}]" @click="point">Point</li>
      <li :class="[{active: activeTool == 'polyline'}]" @click="polyline">Polyline</li>
      <li :class="[{active: activeTool == 'polygon'}]" @click="polygon">Polygon</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "mapDraw",
  data() {
    return {
      activeTool: ""
    };
  },
  methods: {
    point() {
      if (this.activeTool !== "point") {
        this.activeTool = "point";
        this.$emit("point");
      } else {
        this.activeTool = "";
        this.$emit("end");
      }
    },
    polyline() {
      if (this.activeTool !== "polyline") {
        this.activeTool = "polyline";
        this.$emit("polyline");
      } else {
        this.activeTool = "";
        this.$emit("end");
      }
    },
    polygon() {
      if (this.activeTool !== "polygon") {
        this.activeTool = "polygon";
        this.$emit("polygon");
      } else {
        this.activeTool = "";
        this.$emit("end");
      }
    }
  }
};
</script>
<style lang="less">
.map-tools {
  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.active {
      background-color: rgb(102, 156, 255);
      color: #fff;
    }
    li:hover {
      background-color: rgb(212, 224, 246);
    }
  }
}
</style>

複製代碼

2) 新添加一個視圖 Polyline.vue

// src\views\Polyline.vue
<template>
  <div class="map-container">
    <div id="map-container"></div>
    <MapDraw @point="{}" @polyline="drawPolyline" @polygon="{}" @end="drawOff"></MapDraw>
  </div>
</template>

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

export default {
  name: "map-point",
  components: { MapDraw },
  data() {
    return {
      map: null,
      OSMUrl: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
      overLayer: {
        polylines: []
      },
      tempGp: {
        lineNode: [],
        lineNodeLen: 0,
        tempLine: null,
        tempNode: []
      }
    };
  },
  mounted() {
    this.map = this.$utils.map.createMap("map-container");
    this.$utils.map.createTileLayer(this.map, this.OSMUrl, {});
    this.map.setView([51.505, -0.09], 13);
  },
  methods: {
    drawOn() {
      // 移除監聽地圖事件
      this.map.off("click");
      this.map.off("mousemove");
      this.map.off("dblclick");
      this.map.doubleClickZoom.disable();
    },
    drawOff() {
      // 移除監聽地圖點擊事件
      this.map.off("click");
      this.map.off("mousemove");
      this.map.off("dblclick");
      this.map.doubleClickZoom.enable();

      // 復原鼠標平移樣式
      this.$utils.map.removerCoursorStyle(this.map);
    },
    drawPolyline() {
      this.$utils.map.addCursorStyle(this.map, "crosshare-cursor");
      let tempPolygonOpts = {
        color: "rgba(255, 0, 0, 0.85)",
        weight: 3,
        opacity: 0.85
      };

      let finalPolygonOpts = {
        color: "rgba(0, 255, 0, 0.85)",
        weight: 3,
        opacity: 0.85
      };

      this.drawOn();

      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", () => {
        this.overLayer.polylines.push(
          this.$utils.map.createPolyline(
            this.map,
            this.tempGp.lineNode,
            finalPolygonOpts
          )
        );
        this.tempGp.lineNode = [];
        this.tempGp.lineNodeLen = 0;
        this.tempGp.tempLine.remove();
        this.tempGp.tempNode.map(el => el.remove());
      });
    },
    addNode(latlng, map) {
      let node = this.$utils.map.createIcon({
        iconUrl: require("./../assets/images/node.png"),
        iconSize: [16, 16]
      });
      node = this.$utils.map.createMakerByLatlng(latlng, {
        icon: node
      });
      node.addTo(map);
      return node;
    }
  }
};
</script>
<style lang="less">
.map-container {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;

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

複製代碼

以上繪製的線的全部代碼,實現起來其實比較簡單。下面我選擇一些我以爲須要說明的地方解釋,ssh

1) data 中的臨時數據

繪製的過程,實際上是不斷的刪除線和添加線的過程,只要監聽到座標變化就刪掉上一個圖形,繪製新的圖形。post

所以 ,繪製過程當中必須會產生用以輔助的中間數據,須要在data提早定義好

...... 
overLayer: {
    polylines: []   // 存放最終完成繪製後產生的線對象
},
tempGp: {
    lineNode: [],   // 繪製過程當中用於生成線圖形的座標串
    lineNodeLen: 0, // 已添加節點數量
    tempLine: null, // 繪製完成前地圖上的線對象
    tempNode: []    // 每次單擊產生的節點對象
}
......

複製代碼

2)繪製代碼說明

drawPolyline() {
    this.$utils.map.addCursorStyle(this.map, "crosshare-cursor");
    // 繪製過程當中的線樣式
    let tempPolygonOpts = {
        color: "rgba(255, 0, 0, 0.85)",
        weight: 3,
        opacity: 0.85
    };
    // 完成繪製的線樣式,能夠和上面的繪製過程當中的線樣式相同,但這裏爲了區分,取了顏色
    let finalPolygonOpts = {
        color: "rgba(0, 255, 0, 0.85)",
        weight: 3,
        opacity: 0.85
    };

    this.drawOn();
    //每一次點擊都視爲用戶爲想要繪製的線添加了一個節點
    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 => {
        // 第一次點擊時地圖上只現實第一個節點
        // 從第二次開始到繪製結束以前,無論用戶添加了多少個點,鼠標移動時須要去跟新當 
        // this.tempGp.lineNode 中的第 n+1 個 這樣來實現橡皮筋效果
        if (this.tempGp.lineNodeLen >= 1) {
            // 移除上一個圖形
            if (this.tempGp.tempLine) this.tempGp.tempLine.remove();
            // 第n+1個點的索引爲 this.tempGp.lineNodeLen + 1
            // 只要在下一次點擊事件發生前,this.tempGp.lineNodeLen + 1 都同樣
            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", () => {
        this.overLayer.polylines.push(
            this.$utils.map.createPolyline(
                this.map,
                this.tempGp.lineNode,
                finalPolygonOpts
            )
        );

        // 重置臨時數據
        this.tempGp.lineNode = [];
        this.tempGp.lineNodeLen = 0;
        this.tempGp.tempLine.remove();
        this.tempGp.tempNode.map(el => el.remove());
    });
}
複製代碼

最終效果:

三)總結

繪製線功能自己時很簡單的,初學者最容易卡住的地方在於橡皮筋效果的實現這一步上。實現上面的功能以前必定要對 leaflet 中的 map, markerpolyline 等對象的 構造方法,屬性,事件等基礎信息要有必定的瞭解。同時要必定要弄清實現的思路,這樣才能觸類旁通,之後再處理相似功能就熟練了。




目錄

(一) 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 裏面的源碼可能有點亂,能夠根據功能來找對應的代碼。後面會陸續整理完善。

相關文章
相關標籤/搜索