好吧,我都要吐了。
接連三個例子都是類似的套路,使用某個查詢參數類的實例,結合對應的Task類,對返回值進行取值、顯示。
這個例子是Identify識別,使用了TileLayer這種圖層,數據來自Server的MapServer。
結果演示
戳不同的地方會有不同的識別結果。
我對TileLayer不是很了解,這一例僅針對有了解的同學,做一個IdentifyTask的解釋。
IdentifyTask/IdentifyParameter/IdentifyResult三個類
既然是一樣的套路,那么先對這三個類有了解的話就會好說很多吧。
IdentifyTask
當前,IdentifyTask不能在SceneView和dynamic layers中使用。有關Identify是什么,請【點我】
其執行成功的返回值包括有一個IdentifyResult[]。
IdentifyParameters
本例中用到的屬性有:tolerance(Number)、mapExtent(Extent類)、layerId(Number[])、layerOption(String)、width(Number)、height(Number)、geometry(Geometry類);
分別意義是:屏幕像素與應被識別的幾何體之間的距離;地圖外框;需要被識別的圖層的ID號;哪些圖層需要被識別(默認頂層圖層),是個枚舉量;view的長寬。
IdentifyResult
本例中用到IdentifyResult的屬性有:
layerName(String類)、feature(Geometry類)
后者是識別得到的幾何體,前者是包括后者的圖層的圖層名。
給出引用
require([ "esri/Map", "esri/views/MapView", "esri/layers/TileLayer", "esri/tasks/IdentifyTask", "esri/tasks/support/IdentifyParameters", "dojo/_base/array", "dojo/on", "dojo/dom", "dojo/domReady!" ], function( Map, MapView, TileLayer, IdentifyTask, IdentifyParameters, arrayUtils, on, dom ) }
不解釋,和前兩篇類似。
函數參數骨架
function(Map, MapView, TileLayer, IdentifyTask, IdentifyParameters, arrayUtils, on, dom){ var identifyTask, params; var soilURL = "https://services.arcgisonline.com/arcgis/rest/services/Specialty/Soil_Survey_Map/MapServer";
var parcelsLyr = new TileLayer({...}); var map = new Map({...}); map.add(parcelsLyr); var view = new MapView({...}); view.then(function(){...}); //每次點擊地圖時,就會處理一次的方法體 function executeIdentifyTask(event){...} }
看起來也不是很復雜的樣子,嗯,時間不早了,先吃個晚飯,晚上回來繼續寫完第七章。
好的我吃完了,咱們繼續學習Identify這個例子。
首先是根據soilURL這個MapServer地址生成一個TileLayer,名為parcelsLyr,把它添加到實例化的Map中。
在MapView對象創建完成后,緊接着一個異步操作,下面就對MapView對象的回調函數進行解釋:
view.then(function() { on(view, "click", executeIdentifyTask); identifyTask = new IdentifyTask(soilURL); params = new IdentifyParameters(); params.tolerance = 3; params.layerIds = [0, 1, 2]; params.layerOption = "top"; params.width = view.width; params.height = view.height; });
每當點擊view的時候,觸發click事件executeIdentifyTask()。
然后就實例化一個IdentifyTask對象和一個IdentifyParameters對象,並對IdentifyParameters對象賦值(屬性)。
於是這個例子最大頭的executeIdentifyTask()方法體就是核心了,先把它骨架化:
function executeIdentifyTask(event) { params.geometry = event.mapPoint; params.mapExtent = view.extent; dom.byId("viewDiv").style.cursor = "wait"; identifyTask.execute(params)
.then(function(response) {..這里很長..}) .then(showPopup); function showPopup(response){...} }
首先呢,獲取一些必要的參數,傳遞給IdentifyParameters(geometry屬性和mapExtent屬性)
然后執行IdentifyTask的execute()方法,這個方法有兩個異步操作,第一個異步操作的回調函數非常的長,還得繼續往下拆。
第二個回調函數待第一個回調函數完成后,顯示一個彈窗。
那么【第一個回調函數】繼續拆解如下:
.then(function(response) { var results = response.results; return arrayUtils.map(results, function(result) { var feature = result.feature; var layerName = result.layerName; feature.attributes.layerName = layerName; if (layerName === 'Soil Survey Geographic') { feature.popupTemplate = {...}; } else if (layerName === 'State Soil Geographic') { feature.popupTemplate = {...}; } else if (layerName === 'Global Soil Regions') { feature.popupTemplate = {...}; } return feature; }); })
從response中獲取results屬性,該屬性為Object數組,裝箱為IdentifyResult類型的數組。(同上例)
緊接着返回arrayUtils.map方法遍歷results(IdentifyResult數組)得到的feature集合(Geometry類型)。
arrayUtils.map()方法遍歷得到的是Geometry數組。
這個map()方法里做了什么呢?
首先,獲取IdentifyResult數組其中的IdentifyResult元素中的一個屬性——feature(Graphic類型),然后對不同的IdentifyResult對象的LayerName,設置feature不同的彈窗模板。
————是不是很亂?
1. 遍歷IdentifyResult[]中的每一個IdentifyResult;//別問我IdentifyResult[]怎么來的,從response中來的。見上一篇博客。
2. 對於每一個IdentifyResult,它有兩個屬性:feature(Graphic類型)和layerName(String類型);
3. 對於每一個IdentifyResult,如果layerName不同,那么對應的feature的彈窗信息格式(即popupTemplate)肯定不同;
4. 三個分支,對不同layerName的IdentifyResult,其feature就設置不同的popupTemplate;
5. 返回此IdentifyResult的feature。
這一層回調函數就算OK了。因為feature是Graphic類型的,所以map返回的就是Graphic[]類型的。
題外話,其實ESRI這樣寫很繞,雖然充分利用了JS的靈活和強大,但是代碼解讀起來就非常困難,函數能作為變量到處嵌套。
【第二個回調函數】
.then(showPopup); function showPopup(response) { if (response.length > 0) { view.popup.open({ features: response, location: event.mapPoint }); } dom.byId("viewDiv").style.cursor = "auto"; }
這回終於把回調函數寫出來了。
第二層回調函數的response參數是什么呢?
可以看到,把response賦給了popup的features屬性了,可知response是Graphic[]類型的,這與上一個then返回的值一致。
這樣,就能利用第一個回調函數中設置好的popupTemplate進行不同格式的彈窗了。
關於第一個回調函數的彈窗popupTemplate不作詳細解釋了,因為這已經夠繞了。我在文末給出整個js代碼以供需要的同學參考。
總結一下
本例,仍然是對某某Result中的Graphic或者Geometry的屬性進行讀取或者操作。
和上兩篇是類似的,空間查詢的重點就是某某Task和某某Parameters和某某Result的交叉使用:
某某Task.execute(某某Parameters)
.then(回調函數處理某某Result)
.then(..)....
至於怎么處理這個Result中的feature,官方的例子寫的很明確了,但是值類型就很隱晦。
因為JS的弱類型性,導致這些值類型十分模糊,所以繞、暈也是正常的,我已經盡我所能把每個關鍵的地方的值類型進行解釋和說明了,希望大家能看懂我的胡言亂語。
好了,第七章空間查詢的內容並不是很多,這幾個Task肯定很有用,我在寫完第八章空間查詢后會對其進行一個比較全面的解釋。
第八章見!
附上這例子的全代碼:

<script> require([ "esri/Map", "esri/views/MapView", "esri/layers/TileLayer", "esri/tasks/IdentifyTask", "esri/tasks/support/IdentifyParameters", "dojo/_base/array", "dojo/on", "dojo/dom", "dojo/domReady!" ], function( Map, MapView, TileLayer, IdentifyTask, IdentifyParameters, arrayUtils, on, dom ) { var identifyTask, params; // URL to the map service where the identify will be performed var soilURL = "https://services.arcgisonline.com/arcgis/rest/services/Specialty/Soil_Survey_Map/MapServer"; // Add the map service as a TileLayer for fast rendering // Tile layers are composed of non-interactive images. For that reason we'll // use IdentifyTask to query the service to add interactivity to the app var parcelsLyr = new TileLayer({ url: soilURL, opacity: 0.85 }); var map = new Map({ basemap: "osm" }); map.add(parcelsLyr); var view = new MapView({ map: map, container: "viewDiv", center: [-120.174, 47.255], zoom: 7 }); view.then(function() { // executeIdentifyTask() is called each time the view is clicked on(view, "click", executeIdentifyTask); // Create identify task for the specified map service identifyTask = new IdentifyTask(soilURL); // Set the parameters for the Identify params = new IdentifyParameters(); params.tolerance = 3; params.layerIds = [0, 1, 2]; params.layerOption = "top"; params.width = view.width; params.height = view.height; }); // Executes each time the view is clicked function executeIdentifyTask(event) { // Set the geometry to the location of the view click params.geometry = event.mapPoint; params.mapExtent = view.extent; dom.byId("viewDiv").style.cursor = "wait"; // This function returns a promise that resolves to an array of features // A custom popupTemplate is set for each feature based on the layer it // originates from identifyTask.execute(params).then(function(response) { var results = response.results; return arrayUtils.map(results, function(result) { var feature = result.feature; var layerName = result.layerName; feature.attributes.layerName = layerName; if (layerName === 'Soil Survey Geographic') { feature.popupTemplate = { // autocasts as new PopupTemplate() title: "{Map Unit Name}", content: "<b>Dominant order:</b> {Dominant Order} ({Dom. Cond. Order %}%)" + "<br><b>Dominant sub-order:</b> {Dominant Sub-Order} ({Dom. Cond. Suborder %}%)" + "<br><b>Dominant Drainage Class:</b> {Dom. Cond. Drainage Class} ({Dom. Cond. Drainage Class %}%)" + "<br><b>Farmland Class:</b> {Farmland Class}" }; } else if (layerName === 'State Soil Geographic') { feature.popupTemplate = { // autocasts as new PopupTemplate() title: "{Map Unit Name}", content: "<b>Dominant order:</b> {Dominant Order} ({Dominant %}%)" + "<br><b>Dominant sub-order:</b> {Dominant Sub-Order} ({Dominant Sub-Order %}%)" }; } else if (layerName === 'Global Soil Regions') { feature.popupTemplate = { // autocasts as new PopupTemplate() title: layerName, content: "<b>Dominant order:</b> {Dominant Order}" + "<br><b>Dominant sub-order:</b> {Dominant Sub-Order}" }; } return feature; }); }).then(showPopup); // Send the array of features to showPopup() // Shows the results of the Identify in a popup once the promise is resolved function showPopup(response) { if (response.length > 0) { view.popup.open({ features: response, location: event.mapPoint }); } dom.byId("viewDiv").style.cursor = "auto"; } } }); </script>