這個例子相當復雜。我先簡單說說這個例子是干啥的。
在UI上,提供了一個下拉框、兩個滑動桿,以確定三個參數,使用這三個參數進行空間查詢。這個例子就頗帶空間查詢的意思了。
第一個參數是油井類型,第二個參數是油井的緩沖半徑,第三個參數是地震級別。
給定油井的類型,給定油井的緩沖半徑(緩沖區分析生成),給定地震級別,就能在油井附近以這個緩沖半徑為圓搜索出符合給定地震級別的地震點。
這個例子是干嘛的呢?
“因為開采油田會導致地下空間坍塌,而引發地震。”
看看搜索結果(隨便選的參數):
橙色點即為搜索結果(地震點)。
給出引用
話不多說,看看這個例子的引用有多少:
require( [ "esri/Map", "esri/views/MapView", "esri/layers/FeatureLayer", "esri/layers/GraphicsLayer", "esri/geometry/geometryEngine", "esri/Graphic", "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleMarkerSymbol", "dojo/on", "dojo/dom", "dojo/dom-construct", "dojo/domReady!" ], function(...){...} );
用到了兩種Layer,FeatureLayer是數據圖層,GraphicsLayer是緩沖區圖層和結果顯示圖層。
為了支持GraphicsLayer的緩沖區,需要用到geometryEngine模塊和Graphics模塊。
為了支持結果顯示,用到了SimpleFillSymbol模塊和SimpleMarkerSymbol模塊。
函數參數骨架

function(Map, MapView, FeatureLayer, GraphicsLayer, Graphics, SimpleFillSymbol, SimpleMarkerSymbol) { var quakesUrl = "..."; var wellBuffer,wellsGeometries,magnitude; //獲取dom元素 var wellTypeSelect = dom.byId("well-type"); var magSlider = dom.byId("mag"); var distanceSlider = dom.byId("distance"); //FeatureLayer&GraphicsLayer定義 var wellsLayer = new FeatureLayer({...}); var quakesLayer = new FeatureLayer({...}); var resultsLayer = new GraphicsLayer({...}); var map = new Map({...}); var view = new MapView({...}); view.ui.add("infoDiv", "top-right"); view.then(function(){...}); //功能方法 function getValues(response){...} function getUniqueValues(values){...} function addToSelect(values){...} function setWellsDefinitionExpression(newValue){...} function queryForWellGeometries(){...} function createBuffer(wellPoints){...} //事件 on(magSlider, "input", function(){...}); on(distanceSlider, "input", function(){...}); on(distanceSlider, "change", function(){...}); on(wellTypeSelect, "change", function(evt){...}); on(dom.byId("query-quakes"), "click", function(){...}); //其余方法 function queryEarthquakes(){...} function displayResults(results){...} )
可嚇壞我了,這么龐大的骨架(script標簽就有200+行)。
經過我一個月的細讀,我終於把這些流程大概弄懂了,如下圖:
有些地方表述可能不太對。
分為兩塊,一塊是事件體,另一塊是函數體。
先講簡單的吧,事件體。
事件體
wellTypeSelect和distanceSlider這兩個dom元素的change事件體都會獨立觸發createBuffer()函數體,wellTypeSelect事件則會先觸發setWellsDefinitionExpression()函數體進行遍歷搜索幾何體,然后把返回值(地震點的幾何體集合wellsGeometries)交給createBuffer()函數體,生成緩沖區。
意思是只要油井類型(下拉列表的項目改變)和距離滑塊改變,緩沖區就要進行重繪。
distanceSlider的input事件會改變DOM上的數值,為緩沖區半徑的數值。
magSlider的input事件會改變DOM上的數值,為地震級別的數值。
queryQuakes按鈕的click事件會通過已經生成的緩沖區進行疊置分析,搜索緩沖區內的地震點。
對大量的變量如果不是很懂,這里可以先緩緩,下面的函數體會更詳細介紹,事件體主要明白DOM的事件能干啥就行了。
函數體
有幾個函數體是連鎖反應的,而且是在MapView的then中進行連鎖異步操作的。
在這里我又不得不提一下異步操作和這個then的用法了。
then的含義就是,等then前面的操作在服務器上做完后,再執行then里面的內容。
因為js是單線程的解釋型語言,不會編譯,只會從頭到尾一次讀下來,所以執行到then前面的代碼時,是不可能停在那里的。
then前面的代碼執行完肯定有一個類似回執的東西(可以理解為返回值),但是瀏覽器不能一直等待這個回執啊?
所以就在then里面寫一個回調函數,丟給服務器,在服務器端等待then前的操作完成后,再把“返回值”返還給回調函數做,即可。
then()里的東西不會立刻在本地執行,在本地等待前一步的操作結果只會讓瀏覽器卡死。
實際上,我標紅的關鍵詞,就是異步操作的思想了。
復習完異步操作和then,我們再看看view的那一串then:
view.then(function() { return wellsLayer.then(function() { var query = wellsLayer.createQuery(); return wellsLayer.queryFeatures(query); }); }) .then(getValues) .then(getUniqueValues) .then(addToSelect) .then(createBuffer);
我直接給出我的解讀:首先,view加載完后,執行第一個then,第一個then返回wellsLayer的空間搜索(queryFeatures()這個方法)結果,這個結果是FeatureSet類型的,名為response。這response在哪用呢?它傳遞給了getValues()這個方法體:
function getValues(response) { var features = response.features; var values = features.map(function(feature) { return feature.attributes.STATUS2; }); return values; }
經查,response確實就是FeatureSet類的實例,通過getValues獲取其內所有features的所有STATUS2字段的值,名為values,類型為AJS中的Collection類型。
values又傳遞給getUniqueValues()方法體:
function getUniqueValues(values) { var uniqueValues = []; values.forEach(function(item, i) { if ((uniqueValues.length < 1 || uniqueValues.indexOf(item)===-1) && (item !== "")) { uniqueValues.push(item); } }); return uniqueValues; }
根據某些規則(if語句)遍歷values,使用Collection類的forEach方法,然后把遍歷結果作為JS的數組返回,名為uniqueValues,傳遞給
addToSelect()方法體,實現選擇列表這個DOM元素上出現油井類型(option):
function addToSelect(values) { values.sort();//排序 values.forEach(function(value) { var option = domConstruct.create("option"); option.text = value; wellTypeSelect.add(option);//給下拉列表添加選項
return setWellsDefinitionExpression(wellTypeSelect.value); });
不能光添加呀!所以它就return了,方法setWellsDefinitionExpression()的結果。
setWellsDefinitionExpression()這個方法是設置wellsLayer的SQL查詢語句的,但是沒完,setWellsDefinitionExpression()這個方法內還有一層return:
function setWellsDefinitionExpression(newValue) { wellsLayer.definitionExpression = "STATUS2 = '" + newValue + "'"; if (!wellsLayer.visible) { wellsLayer.visible = true; } return queryForWellGeometries(); } function queryForWellGeometries() { var wellsQuery = wellsLayer.createQuery(); return wellsLayer.queryFeatures(wellsQuery) .then(function(response) { wellsGeometries = response.features.map(function(feature) { return feature.geometry;//返回某個要素的幾何體 }); return wellsGeometries;//返回所有的幾何體(作為集合) }); }
queryForWellGeometries()這個方法是setWellsDefinitionExpression()這個方法必定會觸發的方法體,它用於搜索wellsLayer中的幾何體。
空間查詢要點
注意到了嗎?空間查詢是需要某個Layer的query對象的。
在wellsLayer中,使用createQuery()方法就能返回一個query對象,query對象包含了所有用於空間查詢(搜索)用的信息,通過它,才能使用queryFeatures等方法對需要進行空間查詢(搜索)的圖層進行空間查詢(搜索)。
所以圖中的連串的then對應的方法體就已經解釋完畢了。(呼呼~不知道各位能不能堅持到這,也不知道各位能不能看懂...)
還剩三個方法體,createBuffer()、queryEarthQuakes()和displayResults()方法體。
先來看看createBuffer():
function createBuffer(wellPoints) { var bufferDistance = parseInt(distanceSlider.value); var wellBuffers = geometryEngine.geodesicBuffer(wellPoints, [ bufferDistance ], "meters", true); wellBuffer = wellBuffers[0]; var bufferGraphic = new Graphic({ geometry: wellBuffer, symbol: new SimpleFillSymbol({ outline: { width: 1.5, color: [255, 128, 0, 0.5] }, style: "none" }) });
view.graphics.removeAll();
view.graphics.add(bufferGraphic);
}
從DOM元素中獲取緩沖區半徑,然后用geometryEngine這個工具集(或者叫類,還沒到空間分析章節就不說那么多了)中的geodesicBuffer方法產生緩沖區,數據是傳入的參數wellPoints,即上面addToSelect()方法中最后一層返回的幾何體集合(油井點集合)。
然后實例化Graphic實例,設置顏色、線寬,然后添加到view中的graphics容器內即可。
再看queryEarthQuakes()和displayResults():
function queryEarthquakes() { var query = quakesLayer.createQuery(); query.where = "mag >= " + magSlider.value; query.geometry = wellBuffer; query.spatialRelationship = "intersects"; return quakesLayer.queryFeatures(query); } function displayResults(results) { resultsLayer.removeAll(); var features = results.features.map(function(graphic) { graphic.symbol = new SimpleMarkerSymbol({ style: "diamond", size: 6.5, color: "darkorange" }); return graphic; }); var numQuakes = features.length; dom.byId("results").innerHTML = numQuakes + " earthquakes found"; resultsLayer.addMany(features); }
前一個設置query(又是query!)的信息,返回quakesLayer搜索的結果(看下一個方法,應該是FeatureSet類型的),結果傳遞給displayResults(在按鈕的click事件中通過then異步傳遞),名為results。
在后一個方法體中,使用map方法遍歷要素中所有幾何體,設置線寬和色彩等符號樣式,最后在DOM元素上刷新顯示搜索得到了多少個地震點“numQuakes earthquakes found”,並在結果圖層中添加這些要素,刷新顯示。
最后兩個其實就是通過上方生成的緩沖區進行空間查詢(搜索),得到結果並刷新顯示的過程。
至此,本例就全部解釋完畢了,至於有些變量是什么,看new的類型就知道啦,一路看我的文章應該都懂的。
————
感言:一個月了,好拖啊...4.3都出了這么久了,才把這個例子攻克。嗯,接下來的例子就不會那么又臭又長了...
總結一下
本例通過對圖層的query對象的使用,以及結合空間分析中的緩沖區(使用geometryEngine),進一步加深了異步操作和遍歷的代碼理解。
要說能符合本章空間查詢(搜索)的核心代碼,也不過是wellsLayer.createQuery()這一句代碼和query對象的屬性設置了,Graphics的操作(遍歷、屬性設置)和結果的互相傳遞都不是本章的重點,所以導致了這個例子閱讀困難。
大致流程為:從featureLayer中獲取字段值,並加到下拉選擇列表中——選擇某個油井類型,設置緩沖半徑,后台獲取油井的點幾何體集合,生成緩沖區——使用緩沖區,和設置好的地震級別,進行空間查詢,得到地震點的搜索結果,刷新顯示。
over!