在“Hello World Map”裡我們已經成功載入了地圖,以前沒接觸過GIS的同學先得補一補功課,起碼要瞭解空間對象和資料模型的基本概念。常見的地圖服務,其背後都有一個地圖文檔(*.mxd或*.msd),文檔中有很多頁(圖層Layer),每頁上又有許多空間要素(點、線、面)可能對應了現實世界中的水井、道路、綠地等等,同時地圖文檔中還儲存了可見度、符號化、比例尺等很多配置資訊,用於控制地圖的最終呈現效果。
地圖組件MapView是地圖最基本的容器,負責地圖展示和使用者互動,因此AGSMapView也是最重要的類,它提供了一系列介面協助開發人員輕鬆疊加不同的空間資料、漫遊地圖、顯示資訊等等。
1. 載入圖層
“Hello World Map”樣本中疊加了兩個圖層,分別是基礎底圖圖層(切片地圖服務)和人口分布地圖圖層(動態地圖服務),添加的方法很簡單,其中Name參數是Layer的唯一標示,不能重名:
//按+1順序添加圖層[self.mapView addMapLayer:tiledLyrwithName:@"TiledLayer"];//按指定順序插入圖層[self.mapView insertMapLayer:tiledLyrwithName:@"TiledLayer0" atIndex: 0];//按名稱刪除指定圖層[self.mapViewremoveMapLayerwithName:@"TiledLayer"];
看得出來,MapView中圖層繪製的順序是自下而上,第一個載入的圖層意義比較特殊,常稱為BaseMapLayer,它將初始化整個地圖容器的空間參考(Spatial reference)、初始範圍(Initial extent)和全圖範圍(Full extent),這很關鍵,因為如果接下來添加的圖層空間參考不同,該圖層就顯示不出來(動態圖層除外,可以重投影),那怎麼知道是否載入成功?需要引用一個重要的協議:AGSMapLayerDelegate,圖層訊息委託,會在下一節中詳細介紹。
目前支援的圖層類型包括了:
· 切片地圖服務圖層(AGSTiledMapServiceLayer)
· 動態地圖服務圖層(AGSDynamicMapServiceLayer)
· 影像服務圖層(AGSImageServiceLayer)
· 要素服務圖層(AGSFeatureLayer)
· 微軟Bing服務圖層(AGSBingMapLayer)
· OSM服務圖層(AGSOpenStreetMapLayer)
· WMS服務圖層(AGSWMSLayer)
· 離線切片服務圖層(AGSLocalTiledLayer)
· 自訂切片服務圖層(OfflineTiledLayer)
· 圖形圖層(AGSGraphicLayer)
· 草圖繪製圖層(AGSSketchLayer)
這裡先瞭解一下ArcGIS for iOS支援的圖層類型,具體都幹嘛使的,下一章再詳細介紹;此外還有一種新型的智能地圖類型:WebMap,也會單獨介紹。
2. 圖層控制
地圖每添加一個圖層,MapView容器裡就對應添加一個子視圖,如果你希望控制該圖層的可見度、透明度等參數,可以自己顯式聲明一個UIView,讓其引用AGSLayerView協議,然後控制這個UIView就行了,如下:
//擷取圖層視圖引用UIView<AGSLayerView> *LyrView =[self.mapViewaddMapLayer: LyrwithName:@"PopulationLayer"];//設定圖層透明度LyrView.alpha = 0.3;//設定圖層可見度LyrView.hidden = NO;//操作地圖的同時允許繪製資料,會增加CPU消耗,建議僅對基礎地圖圖層使用LyrView.drawDuringPanning = YES;LyrView.drawDuringZooming = YES;
3. 顯示範圍
地圖顯示範圍,預設是BaseMapLayer的全圖範圍,預先可以構建一個AGSEnvelope對象,並直接放大到該範圍:
//設定地圖初始化顯示範圍為中國AGSEnvelope *chinaEnv = [AGSEnvelopeenvelopeWithXmin:7800000.00 ymin:44000.00 xmax:15600000.00 ymax:7500000.00 spatialReference:self.mapView.spatialReference];[self.mapViewzoomToEnvelope:chinaEnvanimated:YES];
*縮放到的幾何地區其空間參考必須和地圖一致
地圖還可以按指定點進行置中顯示:
//置中顯示AGSPoint *newPoint = [AGSPoint pointWithX:-93.032201 y:49.636213 spatialReference:self.mapView.spatialReference];[self.mapView centerAtPoint:newPoint animated:NO];
另外屬性visibleArea(取代了原來的envelope)返回結果是當前地圖的外接矩形:
AGSPolygon* mapExtent = self.mapView.visibleArea;
而屬性MaxEnvelope可以設定地圖的最大顯示範圍,所有操作都無能超該範圍:
self.mapView.maxEnvelope = mapExtent.envelope;
4. 手勢 MapView預設會響應以下手勢操作:
表3-2-1 地圖支援的手勢
手勢 |
地圖響應 |
輕劃(Swipe) |
平移 |
撐開(Pinch-Out) |
放大 |
捏合(Pinch-In) |
縮小 |
點擊(Tap) |
查看 |
常按(Long Press) |
啟用放大鏡 |
雙擊(Double Tap) |
放大 |
兩點點擊(Two finger Tap) |
縮小 |
兩點轉動(Two finger Twist)* |
旋轉 |
*地圖旋轉前提是MapView的allowRotationByPinching屬性為YES。
5. 圖例
一圖勝千言的佳話裡肯定少不了圖例(MapLegend),尤其當要素類型複雜時,通過圖例能簡明標示關鍵要素,圖例包括一組符號地圖片和對應的文字標註。
圖3-2-3-1 常見的地圖圖例樣式
ArcGIS for iOS v1.8之後下列圖層都支援擷取圖例資訊:
· 切片地圖服務圖層
· 動態地圖服務圖層
· 要素服務圖層
· 圖形圖層
5.1. 對於切片和動態地圖服務圖層:
ArcGIS Server v10 SP1以上版本發布的切片和動態地圖服務提供了圖例資訊,通過AGSMapServiceInfo對象的retrieveLegendInfo方法非同步擷取到legendLabels和legendImages:
//AGSMapServiceInfoDelegate 方法-(void) mapServiceInfo:(AGSMapServiceInfo *)mapServiceInfo operationDidRetrieveLegendInfo:(NSOperation *)op { //loop through all sub-layers NSArray* layerInfos = mapServiceInfo.layerInfos; for(int i=0;i<[layerInfos count];i++){ //access legend information of each sub-layer AGSMapServiceLayerInfo* layerInfo = [layerInfos objectAtIndex:i]; NSArray* legendLabels = layerInfo.legendLabels; NSArray* legendImages = layerInfo.legendImages; ... }}
5.2. 對於要素和圖形圖層:
成功載入了要素和圖形服務,其符號和檔案標註已經下載到終端,所以只需要匯出簡單符號或渲染色帶就好:
//匯出symbol圖片 AGSSymbol* symbol = ...; UIImage* image = [symbol swatchForGeometryType:AGSGeometryTypePoint size:CGSizeMake(20,30)]; //匯出渲染色帶 AGSRenderer* renderer = ...; AGSGraphic* graphic = ...; UIImage* image = [renderer swatchForGraphic:graphic size:CGSizeMake(20,30)];
目前還沒有封裝成拿來即用的組件,需要開發人員手動填充到View或UITableView裡,稍顯麻煩。
6. 經向環繞 平面地圖都是從經度-180展開到180,如果你要同時瀏覽東西經180的資料就很不方便,因此在v1.8中新增加了經向環繞“Wrap Around”的設定,使得地圖資料在東西向平移時無縫環繞:
//地圖組件開啟環繞self.mapView.wrapAround = YES;
經向環繞的原理類似與螢幕擴充:-180~180是當前螢幕0,-180再往西就是螢幕-1(-180~-540),而正180再往東就是螢幕1(180~-540)。在採集座標時這會造成困擾,因此需要先對進行標準化(normalize),Geometry Engine裡提供了該方法normalizeCentralMeridianOfGeometry。
經向環繞的使用條件:
· 最大範圍覆蓋全球
· 地圖空間參考是WGS 84 (WKID=4326) 或Web Mercator (WKID=102113, 102100, or 3857)
· 支援WKT關鍵字的動態服務(ArcGIS Server10.0以上版本發布)
7. 時態展示 包含時間資訊的地理資料在伺服器端註冊成“時態(Time-aware Layer)”資料後, 用戶端就能展示時態,比如:24小時內颶風的推進過程、1周的氣溫變化過程等。支援時態的地圖服務都是動態服務,包括:動態地圖服務、影像服務和要素服務。
時間範圍(TimeExtent)屬性,定義了顯示資料的時間區間,利用它能快速過濾時態資料,範圍外的資料都不會顯示:
NSDate* now = [NSDate date]; NSDate* yesterday = ... [NSDate dateWithTimeIntervalSinceNow: -(24 * 60 * 60)]; //過濾顯示最近1天的資料AGSTimeExtent* extent = [[AGSTimeExtent alloc] initWithStart:now end: yesterday]; map.timeExtent = extent;//只顯示某一時間點的資料AGSTimeExtent* extent = [[AGSTimeExtent alloc] initWithStart:now end: now]; map.timeExtent = extent;
*要表示截止某時間點前(後),起始(終止)時間參數可以輸入nil。
如果輸入時間的時區是原生系統時區(如:中國上海-香港-烏魯木齊時間),而請求的時態服務又是其他時區,則需要統一時間格式:
//定義時間格式NSDateFormatter* inputFormatter = [[NSDateFormatter alloc] init]; [inputFormatter setDateFormat:@"M/d/yyyy h:mm a"];//設定時區[inputFormatter setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]]; //按格式構造NSDateNSDate* date = [inputFormatter dateFromString:@"1/1/2001 12:00 AM"];AGSTimeExtent* extent = [[AGSTimeExtent alloc] initWithStart:date end:date];
*更多NSDate時間轉換的內容請參考博文-http://blog.csdn.net/diyagoanyhacker/article/details/7096612。
如果不需要按時間過濾,設定圖層的時間開關(useTime)為false即可:
AGSLayerTimeOptions* op = [[[AGSLayerTimeOptions alloc] init] autorelease];op.layerId = 1; //關閉指定圖層的時態響應op.useTime = FALSE;