通過上一節的學習,我們已經知道了如何與GraphicLayer互動,但畢竟GIS不是一個畫闆,是以這節來看一下如何通過Silverlight API完成GIS中的分析功能。
GIS之是以是一個通用的工具,就是因為它具有各種各樣分析和處理資料的能力。Silverlight API中提供了Task,使我們能夠輕松完成常見的分析任務。
先來考慮一下吃餃子的場景。要想吃餃子,我們需要先去買菜,買肉,回家後在廚房裡洗菜,揉面, 拌餡,包餃子,煮餃子,吃餃子,之後别忘了洗碗;另一種情況就是去飯館,告訴服務員我要吃3兩茴香,3兩韭菜的餃子,然後等着餃子端到你面前,開吃,走人。
在ArcGISServer程式開發中,要完成GIS的分析功能其實和吃餃子是一樣的。用ADF程式設計就像在家裡吃餃子,除了架設伺服器,所有的工作基本上也都得我們自己在伺服器端來完成,要處理的地方比較多;而用用戶端API程式設計相當于去外面吃餃子,我們隻要把任務交給相應的Task,之後接受結果就行了,不用做餃子。唯一不同的就是在外面吃完餃子别忘了付錢,而用Task完成分析任務則是免費的。這點也展現在使用用戶端API中的Task時,是由ArcGISOnline提供給你的,不需要自己購買AGS軟體。
現在來看看Silverlight API目前給我們提供了那些Task功能:
Query:能夠在已經釋出的服務資料中,通過屬性條件(可以屬性字段中進行關系判斷,字元查找等),圖形條件(與輸入的圖形相交、包含、相離等),或者是兩者的組合,查詢出滿足條件的資料并傳回。相當于Engine中的SpatialFilter,當然也是QueryFilter。
Find:在地圖資料的屬性字段中查找包含有關鍵字參數的資料并傳回。
Identity:對滑鼠目前點選位置上的資料進行辨識并傳回結果,可以對多個圖層的資料進行辨識。
Address Locator:輸入經緯度,傳回位址結果(Geocoding);輸入一個地方的位址,傳回經緯度結果(Reverse Geocoding)。由于國内地圖資料保密工作做的相當好,這個Task暫時用不到。
Geometry Service:可以對輸入的地理資料進行如緩沖區,動态投影,面積/周長量算等幾何操作。
Geoprocessing:能夠完成複雜的GIS任務,類似ToolBox中的工具。
抽象一下,可以看出,Query完全可以完成Identity和Find的工作,但後兩者在特定場合下使用起來比Query要友善的多;Geoprocessing完全可以替代Geometry Service,但是在利用REST API編寫的程式中,要盡量使用GeometryService。
再抽象一下,Silverlight API中的這幾個Task和JavaScript/FlexAPI中的Task是大同小異的,因為其實它們都是AGS 9.3 REST API中暴露出來的操作資源(OperationResource)見下圖:

2009-4-26 13:27

首先選擇工具條中的畫線工具,在螢幕上畫一條曲線,會根據曲線自動生成一個距離100公裡的緩沖區顯示在地圖上,之後開始查詢緩沖區圖形經過的州(相交),将結果顯示在地圖上。可以單擊每個州檢視詳細資訊。這裡假設你已學習了前幾節的内容,隻讨論Task用法的部分。
1、利用所畫的線生成緩沖區。畫線利用的是Draw工具中的Freehand,在這個動作完成後會觸發Draw的OnDrawCompleted事件,自然可以在這裡開始進行緩沖區的工作,用的是Geometry Service裡的Buffer。
初始化Geometry Service。假設已經在Map1中添加了ID為glayerResult的GraphicsLayer,linesymbolred是提前設定好的CartographicLineSymbol:
private void Draw1_OnDrawComplete(object sender, DrawEventArgs args)
{
Draw1.Deactivate();//Freehand動作失效
//将Freehand畫的曲線顯示在地圖上
GraphicsLayer glayer = Map1.Layers["glayerResult"] as GraphicsLayer;
Graphic g = new Graphic();
g.Symbol = linesymbolred;
g.Geometry = args.Geometry;
glayer.Graphics.Add(g);
//初始化Geometry Service
GeometryService geometrytask = new GeometryService("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Geometry/GeometryServer");
}
複制代碼
GeometryService的初始化使用構造函數來完成的,裡面接受一個URL,這個是Geometry Service的REST APIEndpoint。順便說一下,不同于其他服務比如MapService,一個GISServer隻能釋出一個GeometryService,并且它的名稱必須是Geometry。
當一個Task完成時會觸發Completed事件,失敗時也有Failed事件,對這兩個事件進行監聽:
geometrytask.BufferCompleted += new EventHandler(geometrytask_BufferCompleted);
geometrytask.Failed += new EventHandler(geometrytask_Failed);
設定Buffer操作所需的參數:
BufferParameters bufferparameters = new BufferParameters();
bufferparameters.Unit = LinearUnit.Kilometer;
//必須指定下面兩個spatialreference,否則buffer結果集為空
bufferparameters.BufferSpatialReference = new SpatialReference(3395);
bufferparameters.OutSpatialReference = Map1.SpatialReference;
bufferparameters.Distances.Add(100);
bufferparameters.Features.Add(g);
BufferParameters是專門用于Buffer的參數;BufferSpatialReference是将要Buffer的圖形重新投影到這個坐标系下(常常需要根據地圖資料所在地方的情況來設定這個參數),并設定Buffer距離的機關為公裡,Buffer的輸出一般與地圖坐标系一緻;Buffer參數有一個Features屬性,是List類型,裡面的Graphic都将被Buffer。下來将Buffer的任務送出到伺服器(可以看出為什麼這些動作要叫Task):
geometrytask.BufferAsync(bufferparameters);
以上代碼都放在Draw1_OnDrawComplete函數中。任務送出到伺服器後,由GeometryService接管,計算,完成後會立刻将結果傳回給我們,通知我們結果已經完成的方式就是前面綁定的Completed事件。接收到結果後,首先将緩沖區顯示出來:
private void geometrytask_BufferCompleted(object sender, GraphicsEventArgs args)
if (args.Results.Count>0)
g.Symbol = fillsymbolBuffer;
g.Geometry = args.Results[0].Geometry;
如圖:

2、利用生成緩沖區的緩沖區進行空間查詢。要達到我們的目的,就還需要進行一個Query的Task,那麼就可以在這裡馬不停蹄的開始Query的Task。步驟基本都是一樣的,初始化,設定參數,送出結果,處理結果:
//初始化QueryTask
QueryTask querytask = new QueryTask("http://sampleserver1.arcgisonline.com/ArcGIS/rest/services/Demographics/ESRI_Census_USA/MapServer/5");
//準備接收結果或者處理失敗的通知
querytask.ExecuteCompleted += new EventHandler(querytask_ExecuteCompleted);
querytask.Failed += new EventHandler(querytask_Failed);
//設定Query Task所需的參數
Query query = new Query();
query.OutFields.Add("*");//也順便設定了query.ReturnGeometry=true;
query.Geometry = g.Geometry;
query.SpatialRelationship = SpatialRelationship.esriSpatialRelIntersects;
//向伺服器上的對應圖層送出任務
querytask.ExecuteAsync(query);
Map1.Cursor = System.Windows.Input.Cursors.Wait;
這裡的查詢實在美國州的圖層上進行的,詳細資訊将QueryTask構造函數裡的那個參數輸入浏覽器檢視;query.Geometry是設定需要進行空間查詢的圖形,就是上面緩沖區的結果;OutFields是查詢結果需要傳回的字段,這裡傳回全部字段,如果傳回全部字段,則強制設定了ReturnGeometry為true,如果我們不需要處理結果的圖形資訊,則可以将這個參數設為false,以節省流量,顯然這裡不是;空間關系可以參考API,與Engine中的完全一緻。
接下來處理QueryTask完成後的結果:
private void querytask_ExecuteCompleted(object sender, QueryEventArgs args)
GraphicsLayer graphicslayer = Map1.Layers["glayerResult"] as GraphicsLayer;
FeatureSet featureset = args.FeatureSet;
if (featureset != null && featureset.Features.Count > 0)
graphicslayer.ClearGraphics();
listboxResults.Items.Clear();
foreach (Graphic graphic in featureset.Features)
graphic.Symbol = fillsymbolresult;
graphicslayer.Graphics.Add(graphic);
MyMapTip.GraphicsLayer = graphicslayer;
Map1.Cursor = System.Windows.Input.Cursors.Arrow;
上面處理空間查詢的結果隻是将圖形顯示了出來,那麼對于單擊某個州後,顯示出其詳細資訊該怎麼辦呢?從圖一可以看出,用到了Silverlight的DataGrid控件,資訊從哪裡去呢?記得上面我們設定結果中傳回的全部屬性字段嗎?它們存儲在每個Graphic的Attributes屬性中。要麼綁定到DataGrid裡,要麼一條條添加……你可能已經發現了這條語句MyMapTip.GraphicsLayer =graphicslayer;,還記得第三節的Widgets嗎?那裡我們落下了MapTip這個小家夥,現在派上用場了。除了在這裡設定MapTip的GraphicsLayer屬性外,在xaml中有如下的定義:
<esriWidgets:MapTip x:Name="MyMapTip" BorderBrush="#99000000"
BorderThickness="1" Title="詳細資訊" VerticalOffset="10"
HorizontalOffset="10" Background="#DDFFFFFF" />
僅此而已。MapTip會自動找尋自己GraphicsLayer中的Graphic,當滑鼠懸停在某個Grpahic上時,會自動讀取它的Attributes屬性并顯示,小玩具又發揮了大作用。
别忘了萬一處理任務失敗時的提示:
private void geometrytask_Failed(object sender, TaskFailedEventArgs args)
MessageBox.Show("Buffer Error:" + args.Error);
private void querytask_Failed(object sender, TaskFailedEventArgs args)
MessageBox.Show("Query failed: " + args.Error);
前面說到,雖然去外面吃餃子很友善,但是畢竟那是人家做好的,對于老饕來說還需要自己的口感,自己下廚畢竟能控制整個過程的方方面面,哪怕你想做出餃立方也都是有可能的。同樣,ADF程式設計可以調用伺服器端的ArcObjects,讓你為所欲為,這點是用戶端API無論如何也辦不到的。