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

1、概述

這篇文章講介紹如何在 Vue-CLI 工程中添加 ESRI 的地圖服務 ArcGIS Map Service,相信須要用到 ArcGIS Map Service 的對 ArcGIS 平臺都有一些瞭解。javascript

這裏咱們要是實現加載 ArcGIS Map Service 功能都是基於 leaflet.js 的。除了 leaflet.js 這庫,還須要用到 esri 提供的 基於 leaflet.js 實現的拓展庫 esri-leaflet。配合到 leaflet.js 生態,esri 在其基礎上適配了大量的用於支持 ArcGIS 平臺相關的服務的功能,如 地圖服務,地理處理服務,地理編碼,路徑分析等功能。html

這篇文章主要介紹是,其中關於 ArcGIS 各種圖層的加載方式,包括:底圖圖層動態圖層影像圖層瓦片圖層要素圖層聚合圖層熱力圖vue

2、代碼實現

1)代碼總覽與相關引用說明

全部的代碼都是基於第一篇文章中建好的目錄結構中添加的,請參考 Vue-CLI and Leaflet (1):顯示一個地圖java

下面對功能的實現,請參考個人 GitHub 源碼。git

首先在工程中安裝 esri-leafletgithub

npm install esri-leaflet --save
複製代碼

接着,開始依次實現圖層添加的功能。vue-cli

看如下是主視圖裏面的代碼, 其中組件 Layers 是爲了方便演示作的圖層開關圖層開關npm

// src\views\ArcgisServices.vue

<template>
  <div class="map-container">
    <div id="map-container"></div>
    <Layers
      @basemapLayer="addBaselayer"
      @tiledMapLayer="addTilelayer"
      @dynamicMapLayer="addDynamiclayer"
      @imageMapLayer="addImageLayer"
      @featureLayer="addFeaturelayer"
      @clusterLayer="addClusterLayer"
      @heatMap="addHeatmap"
    ></Layers>
  </div>
</template>

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

export default {
  name: "mapView",
  components: { Layers },
  data() {
    return {
      map: null,
      layers: [],
      OSMUrl: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
      tileLayer:
        "https://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer",
      featureLayer:
        "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/0/",
      dynamicLayer:
        "http://sampleserver6.arcgisonline.com/arcgis/rest/services/Hurricanes/MapServer/",
      imageLayer:
        "https://landsat.arcgis.com/arcgis/rest/services/Landsat/PS/ImageServer"
    };
  },
  async mounted() {
    this.map = this.$utils.map.createMap("map-container", {
      maxZoom: 20
    });
  },
  methods: {
    clearLyrs() {
      this.layers.map(lyr => {
        lyr.remove();
      });
      this.layers = [];
    },
    async addBaselayer() {
      this.clearLyrs();
      this.map.setView([22.302437935904464, 114.17198181152345], 8);
      let layer1 = await this.$utils.map.addEsirBasemap(this.map, "DarkGray");
      let layer2 = await this.$utils.map.addEsirBasemap(
        this.map,
        "TerrainLabels"
      );

      this.layers.push(layer1, layer2);
    },
    async addTilelayer() {
      this.clearLyrs();

      this.map.setView([38.369572, -97.681121], 12);
      let layer = await this.$utils.map.addEsirTiledMapLayer(this.map, {
        url: this.tileLayer,
        maxZoom: 15
      });
      this.layers.push(layer);
    },
    async addDynamiclayer() {
      this.clearLyrs();

      this.map.setView([18.930362, -28.243945], 2);
      let layer = await this.$utils.map.addEsirDynamicMapLayer(this.map, {
        url: this.dynamicLayer
      });
      this.layers.push(layer);
    },
    async addImageLayer() {
      this.clearLyrs();

      let layer = await this.$utils.map.addImageMaplayer(this.map, {
        url: this.imageLayer
      });
      this.layers.push(layer);
    },
    async addFeaturelayer() {
      this.clearLyrs();

      this.map.setView([18.930362, -28.243945], 2);
      let layer = await this.$utils.map.addEsirFeatureLayer(this.map, {
        url: this.featureLayer
      });
      this.layers.push(layer);
    },
    async addClusterLayer() {
      this.clearLyrs();

      let self = this;
      this.map.setView([18.930362, -28.243945], 2);
      let layer1 = await this.$utils.map.addEsirBasemap(this.map, "DarkGray");

      let layer2 = await this.$utils.map.addEsirClusterLayer(this.map, {
        url: this.featureLayer,
        // Cluster Options
        polygonOptions: {
          color: "#2d84c8"
        },
        // Feature Layer Options
        pointToLayer: function(geojson, latlng) {
          return self.$utils.map.createCircleMaker(latlng, 10, {
            color: "#2D84C8"
          });
        }
      });

      this.layers.push(layer1, layer2);
    },
    async addHeatmap() {
      this.clearLyrs();

      this.map.setView([18.930362, -28.243945], 2);
      let layer1 = await this.$utils.map.addEsirBasemap(this.map, "DarkGray");

      let layer2 = await this.$utils.map.addEsriHeatmap(this.map, {
        url: this.featureLayer,
        radius: 12,
        minOpacity: 1,
        blur: 50,
        gradient: { 1: "red", 0.65: "lime", 0: "blue" }
      });
      this.layers.push(layer1, layer2);
    }
  }
};
</script>
<style lang="less">
.map-container {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;

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


複製代碼

組件 Layers :json

// src\components\Layers.vue

<template>
  <div class="map-layers">
    <ul>
      <li @click="$emit('basemapLayer')">basemapLayer</li>
      <li @click="$emit('tiledMapLayer')">tiledMapLayer</li>
      <li @click="$emit('dynamicMapLayer')">dynamicMapLayer</li>
      <li @click="$emit('imageMapLayer')">imageMapLayer</li>
      <li @click="$emit('featureLayer')">featureLayer</li>
      <li @click="$emit('clusterLayer')">clusterLayer</li>
      <li @click="$emit('heatMap')">heatmap</li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "map-layers",
  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-layers {
  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>

複製代碼

而後須要特別說明一下,esri-leaflet 在地圖中的引用方方式, esri-leaflet 沒有暴露 default ,因此正確的引用方式以下:api

// src\utils\map.js

// 所有引用
import * as esri from "esri-leafle"
// 按需引用
import {
  basemapLayer,
  featureLayer,
  tiledMapLayer,
  dynamicMapLayer,
  imageMapLayer
} from "esri-leaflet";

// 錯誤引用
import "esri-leafle"
import esri from "esri-leafle"

複製代碼

2)底圖圖層 basemapLayer

basemapLayer 是用於顯示 Esri 提供的地圖和屬性數據, 這些底圖覆蓋了全世界範圍的各個層級,目前的底圖一共有十三種,以下:

  • Streets
  • Topographic
  • NationalGeographic
  • Oceans
  • Gray
  • DarkGray
  • Imagery
  • ImageryClarity (added in 2.1.3)
  • ImageryFirefly (added in 2.2.0)
  • ShadedRelief
  • Terrain
  • USATopo (added in 2.0.0)
  • Physical (added in 2.2.0)

此外,還有正針對不一樣底圖提供的文字標註標註圖層,可根據須要加載:

  • OceansLabels - Labels to pair with the Oceans basemap
  • GrayLabels - Labels to pair with the Gray basemap
  • DarkGrayLabels - Labels to pair with the DarkGray basemap
  • ImageryLabels - Labels including political boundaries to pair with any Imagery basemap
  • ImageryTransportation - Street map labels for pairing with any Imagery basemap
  • ShadedReliefLabels - Labels for pairing with the ShadedRelief basemap
  • TerrainLabels - Labels for pairing with the Terrain or Physical basemap

在 map.js 中添加底圖圖層的加載方法:

// src\utils\map.js

......

const addEsirBasemap = async (map, layerName) => {
  return await basemapLayer(layerName).addTo(map);
};

......

複製代碼

而後添加 ArcGIS.vue 中添加調用方法:

// src\views\ArcgisServices.vue

......
methods:{
	...
    async addBaselayer() {
      this.clearLyrs();
      this.map.setView([22.302437935904464, 114.17198181152345], 8);
      let layer1 = await this.$utils.map.addEsirBasemap(this.map, "DarkGray");
      let layer2 = await this.$utils.map.addEsirBasemap(
        this.map,
        "TerrainLabels"
      );

      this.layers.push(layer1, layer2);
    },
    ...
}
......

複製代碼

這裏須要注意,全部 arcgis mapservice 的構造方法都是異步方法,我在使用過程當中都是用 async,await 保證能正確圖層緩存圖層。後面的全部圖層都是如此。

調用成功以後看到的效果以下:

basemap

3)瓦片圖層 tiledMapLayer,動態圖層dynamicMapLayer, 影像圖層 imageMapLayer

由於這三個地圖加載方式都很是類似因此就放在一塊兒講了, 幾乎都是同樣的 map.js 中添加底圖圖層的加載方法。

其中 opts 參數是構造圖層的初始化參數,根據須要去 esri-lealfet 官網 API 文檔 上的查詢詳細描述。

參數中 url 是必須參數,且必須爲有效的 ArcGIS Map Service 或 Image Service

// src\utils\map.js

......

const addEsirTiledMapLayer = async (map, opts) => {
  return await tiledMapLayer(opts).addTo(map);
};

const addEsirDynamicMapLayer = async (map, opts) => {
  return await dynamicMapLayer(opts).addTo(map);
};
const addImageMaplayer = async (map, opts) => {
  return await imageMapLayer(opts).addTo(map);
};

......

複製代碼
// src\views\ArcgisServices.vue

......
methods:{
	...
	async addTilelayer() {
      this.clearLyrs();

      this.map.setView([38.369572, -97.681121]);
      let layer = await this.$utils.map.addEsirTiledMapLayer(this.map, {
        url: this.tileLayer,
        maxZoom: 15
      });
      this.layers.push(layer);
    },
    async addDynamiclayer() {
      this.clearLyrs();

      this.map.setView([18.930362, -28.243945], 2);
      let layer = await this.$utils.map.addEsirDynamicMapLayer(this.map, {
        url: this.dynamicLayer
      });
      this.layers.push(layer);
    },
    async addImageLayer() {
      this.clearLyrs();

      let layer = await this.$utils.map.addImageMaplayer(this.map, {
        url: this.imageLayer
      });
      this.layers.push(layer);
    },
    ...
}
......

複製代碼

若是根據我提供的代碼調用成功以後,你能夠看到正確的地圖顯示效果以下

瓦片圖層 tiledMapLayer

tiledMap

動態圖層dynamicMapLayer

dynanmiclayer
影像圖層 imageMapLayer, 影像圖加載須要必定的時間。
imageLayer

3)要素圖層 featureLayer

3.1 直接加載 featureLayer

一樣的, opts 參數是構造圖層的初始化參數,根據須要去 esri-lealfet 官網 API 文檔 上的查詢詳細描述,其中必須參數中必須包含有 url, 且此 url 必須爲有效的 ArcGIS Map Service Feature Layer

FeatureOpts

// src\utils\map.js

......

const addEsirFeatureLayer = async (map, opts) => {
  return await featureLayer(opts).addTo(map);
};

......

複製代碼
// src\views\ArcgisServices.vue

......
methods:{
	...    

async addFeaturelayer() {
      this.clearLyrs();
        // 根據每一個圖層展現內容的位置不一樣,調整視圖中心座標
      this.map.setView([18.930362, -28.243945], 2);
      let layer = await this.$utils.map.addEsirFeatureLayer(this.map, {
        url: this.featureLayer
      });
      this.layers.push(layer);
    }
  ...
}
.......
複製代碼

調用圖層加載方法成功你會看到的畫面以下(無底圖)

FeatureLayer1

3.2 顯示原來 featureLayer 配置的底圖樣式

上一步驟中加載的的圖層成果以後,會看到點要素圖層的圖標是 leaflet 中默認圖標。根據 esri-leaflet 的官方描述若是想要加載 圖層本來配置好的樣式須要用到一個插件 esriLeaflet-render

esri-render

npm install esri-leaflet-renderers --save
複製代碼

而後再再 map.js 中引用,正確成功的引用以後,則會看到的圖層樣式以下:

// src\utils\map.js
......

// 在 leaflet, esri-leallet 引用成功以後引用
import "esri-leaflet-renderers";
......

複製代碼

featureLayer2

3.3 自定義圖標樣式

除了使用默認圖標和顯示圖層所配置的樣式以後,feature layer 也提供了自定義圖標樣式的接口。

pointToLayer

構建方法也很是簡單pointToLayer 接口會返回圖層中所包含點要素的 geojson 與 latlng, 根據這個來構造 markercirlcle maker 等點要素並將其做爲返回值 return。

這裏有一點須要注意,若是引用 3.2 中的插件顯示圖層配置的樣式,自定圖標的接口將自動失效。

// for point GeoJSON, Leafet expects custom panes to be returned in pointToLayer()

 featureLayer({
    url: blockPointsUrl,
    pointToLayer: function (geojson, latlng) {
      // return $L.circleMarker(latlng);
      return $L.marker(latlng);
    }
  }).addTo(map);

複製代碼

4)聚合圖層 cluster

以前的文章有介紹過如何使用 Leaflet.markercluster 來實現點聚合的功能,esri-leaflet 也支持基於 arcgis mapservice 的 featurelayer 來實現點要素圖層的聚合功能,並且esri-leaflet的 cluster 圖層也是基於 Leaflet.markercluster 這個插件圖層實現的。所以,在使用以前須要同時安裝 esri-leaflet-cluster, Leaflet.markercluster,

npm install esri-leaflet-cluster leaflet.markercluster --save
複製代碼

但想要在 vue-cli 中使用 esri-leaflet 提供的 cluster 圖層,直接按以前的引用方式是沒法成功安裝官方示例來使用的,我試過不少方法都不行,因而,我看了插件的源碼,在源碼上作了簡單的修改。這裏由於篇幅的緣由,就不知將修改的源碼貼出來了,請項目源碼 src\utils\esri-cluster.js。下面直接講使用方法。

注意工程中 src\utils\ 新增了這個 esri-cluster.js

// src\utils\map.js
......

import Cluster from "./esri-cluster";

......


const addEsirClusterLayer = async (map, opts) => {
  return await Cluster(opts).addTo(map);
};

......

複製代碼
// src\views\ArcgisServices.vue

......
methods:{
	...    

async addFeaturelayer() {
      this.clearLyrs();

      let self = this;
      // 根據每一個圖層展現內容的位置不一樣,調整視圖中心座標
      this.map.setView([18.930362, -28.243945], 2);
      let layer1 = await this.$utils.map.addEsirBasemap(this.map, "DarkGray");

      let layer2 = await this.$utils.map.addEsirClusterLayer(this.map, {
        url: this.featureLayer,
        // Cluster Options
        polygonOptions: {
          color: "#2d84c8"
        },
        // Feature Layer Options
        pointToLayer: function(geojson, latlng) {
          return self.$utils.map.createCircleMaker(latlng, 10, {
            color: "#2D84C8"
          });
        }
      });

      this.layers.push(layer1, layer2);
    }
  ...
}
.......
複製代碼

調用成功以後看到的正確的顯示結果以下。若是有其它需求,可查閱 esri API 文檔 cluster-feature-layer

![clusterLayer](D:\Sync Files\個人堅果雲\2-文檔\Leaflet\assets/clusterLayer.gif)

5)熱力圖圖層 heatmap

跟 clusterLayer很是類似,esri-leaflet的 熱力圖圖層 heatmap 是基於另外一個leaflet插件 Leaflet.heat 實現的 esri-leaflet-heatmap ,而且和esri-leaflet-cluster 同樣,在調用這個插件時,按照以前的方式是沒法成功的按 esri-leaflet 官方示例代碼的方式使用的。所以,一樣的處理方法,從插件源碼中修改出 src\utils\esri-cluster.js。使用前先安裝依賴插件:

npm install esri-leaflet-heatmap leaflet-heat --save
複製代碼

注意工程中 src\utils\ 新增了這個 esri-heatmap.js ,在 map.js 引用

// src\utils\map.js 

......

import Heatmap from "./esri-heatmap";

......

const addEsriHeatmap = async (map, opts) => {
  return await Heatmap(opts).addTo(map);
};

......

複製代碼
// src\views\ArcgisServices.vue

......
methods:{
	...    

async addFeaturelayer() {
       this.clearLyrs();

      this.map.setView([18.930362, -28.243945], 2);
      let layer1 = await this.$utils.map.addEsirBasemap(this.map, "DarkGray");

      let layer2 = await this.$utils.map.addEsriHeatmap(this.map, {
        url: this.featureLayer,
        radius: 12,
        minOpacity: 1,
        blur: 50,
        gradient: { 1: "red", 0.65: "lime", 0: "blue" }
      });
      this.layers.push(layer1, layer2);
    }
  ...
}
.......
複製代碼

調用成功以後看到的正確的顯示結果以下。若是有其它需求,可查閱 Esri API 文檔 heatmap-feature-layer

heatmap

3、總結

除此以外,還有 vectorlayer 但這共功能跟 basemap 相似只能加載 esri 提供的圖層資源,且目前還是 beta 版的所以我以爲暫時沒有必要作 更深刻的研究。

若是項目使用的 esri arcgis 平臺,功能相對很複雜沒有三維顯示的需求,且又嫌 ArcGIS API for Javascript 過重。esri-leaflet 配合到 leaflet及其周邊生態確實是一個很是好的選擇。若是有什麼講的錯的地方,請各位指正。

目錄

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

相關文章
相關標籤/搜索