經(jīng)常有人問我戳杀,ArcGIS怎么弄其馏,怎么弄。
作為當(dāng)年在廈門搞過商業(yè)級天地圖項目的我來說少态,是時候展現(xiàn)一波技術(shù)了惰聂,就把干貨告訴大家疆偿,讓大家好好搞。不僅會介紹API搓幌,還會介紹思路杆故,以及分析過程等等。
前公司項目:天地圖·廈門溉愁,已發(fā)布在App Store处铛,歡迎下載查看。
天地圖·廈門 首頁
[AGSRuntimeEnvironment setClientID:clientID error:&error];
用來設(shè)置認(rèn)證拐揭,只有通過認(rèn)證撤蟆,地圖才不會顯示開發(fā)版。
self.mapView.locationDisplay.showsPing = NO;//取消閃爍的光圈
self.mapView.locationDisplay.showsAccuracy = NO;//取消閃爍的光圈
通過這兩段代碼可以取消ArcGIS閃爍的光圈堂污。
self.mapView.locationDisplay.location.accuracy = 10;
設(shè)置定位精度家肯,單位是米。
self.mapView.layerDelegate = self;
設(shè)置層代理盟猖,主要用到了mapViewDidLoad的代理方法讨衣,當(dāng)?shù)貓D加載完成時調(diào)用。
self.mapView.touchDelegate = self;
設(shè)置觸碰代理式镐,主要用到了didClickAtPoint的代理方法值依,當(dāng)點擊地圖時調(diào)用。
self.mapView.minScale = 100000000;
self.mapView.maxScale = 1000;
設(shè)置地圖的放大倍數(shù)和縮小倍數(shù)碟案,ArcGIS不會自己縮小到很小愿险,需要你來設(shè)置,通過設(shè)置這個參數(shù),可以看到門牌號辆亏。
[self.mapView enableWrapAround];
設(shè)置允許地圖環(huán)繞风秤,如果不設(shè)置這個參數(shù),左右滑動地圖時會到底扮叨。比如向左滑動到美國時就停止了缤弦,設(shè)置了這個參數(shù),滑動到美國彻磁,可以繼續(xù)左滑到英國碍沐,再回到美國,形成循環(huán)衷蜓。
AGSSpatialReference *sr = [AGSSpatialReference spatialReferenceWithWKID:4490];
AGSEnvelope *env = [AGSEnvelope envelopeWithXmin:117.85362348365
ymin:24.398242072050003
xmax:118.48276347535
ymax:24.93150561495
spatialReference:sr];
[self.mapView zoomToEnvelope:env animated:NO];
設(shè)置初始定位區(qū)域累提,我將它定位在廈門。
以上方法我放在viewDidLoad方法中執(zhí)行磁浇。
[self.mapView.locationDisplay startDataSource];
在地圖上顯示設(shè)備位置斋陪。
[self.mapView.locationDisplay addObserver:self
forKeyPath:@"autoPanMode"
options:(NSKeyValueObservingOptionNew)
context:NULL];
監(jiān)聽地圖的定位情況,用來控制當(dāng)前是否處于地圖的中心
[self.mapView addObserver:self
forKeyPath:@"mapScale"
options:(NSKeyValueObservingOptionNew)
context:NULL];
地圖縮放系數(shù)改變時調(diào)用置吓,我用來取消搜索框鍵盤无虚。
[self.mapView addObserver:self
forKeyPath:@"visibleAreaEnvelope"
options:(NSKeyValueObservingOptionNew)
context:NULL];
地圖可見區(qū)域監(jiān)測,實時獲取地圖中心位置衍锚,糾錯的時候可以用到友题。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mapViewDidEndZooming:)
name:AGSMapViewDidEndZoomingNotification
object:nil];
地圖停止縮放時觸發(fā),用來判斷是否需要移除全球地圖戴质,提高加載速度咆爽。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(mapViewDidEndPanning:)
name:AGSMapViewDidEndPanningNotification
object:nil];
地圖停止平移時觸發(fā)。
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(handleMapPan:)];
[self.mapView addGestureRecognizer:panGestureRecognizer];
處理地圖平移置森。
以上方法我放在mapViewDidLoad方法中執(zhí)行。
[self.mapView zoomIn:YES];
[self.mapView zoomOut:YES];
地圖的放大和縮小操作符糊。
TDTTiledServiceLayer *tianDiTuLyr = [[TDTTiledServiceLayer alloc] initWithLayerType:type LocalServiceURL:nil error:&err];
添加全球地圖操作凫海,TDTTiledServiceLayer是我自己自定義的類,用來加載全球地圖用的男娄。遺憾的是行贪,ArcGIS并沒有使用一個類來處理加載這一類型全球底圖的操作,所以要自己去實現(xiàn)模闲,而這個實現(xiàn)過程過于復(fù)雜建瘫,我當(dāng)初也是花了不少時間,篇幅的限制尸折,就不在這里細(xì)細(xì)展開啰脚,因為細(xì)細(xì)展開的話,文章太長太長了实夹。
[self.mapView insertMapLayer:tianDiTuLyr withName:name atIndex:0];
在某個層級插入地圖橄浓。地圖上放置一張底圖粒梦,如果上面有什么信息的話荸实,就往上面加一層,需要什么樣的信息准给,就疊加什么樣的層,層級處理的理論基礎(chǔ)與iOS視圖或者cocos 2D都是一樣的露氮。
id tiledLayer = [AGSLocalTiledLayer localTiledLayerWithPath:fileName];
加載本地圖層祖灰,如果本地有tpk文件,可以直接加載夫植。tpk文件和離線地圖有緊密的關(guān)系油讯,因為離線地圖下載下來的就是tpk文件详民,tpk文件是離線地圖數(shù)據(jù)格式,之后會說到離線地圖陌兑。
id tiledLayer = [MDTiledMapServiceLayer tiledMapServiceLayerWithURL:url];
MDTiledMapServiceLayer是我自己定義的沈跨,繼承于AGSTiledMapServiceLayer兔综,根據(jù)需要,可能要重寫urlForTileKey方法软驰,并配置好column、row纠吴、level等信息慧瘤,而level信息要與服務(wù)端約定好。
以上內(nèi)容是圖層的初始化和疊加操作锅减。
接下來介紹專題圖的加載。
所謂的專題圖握联,無非是各種功能或者數(shù)據(jù)顯示圖,比如在地圖上顯示自行車位置拴疤,顯示水壩位置,顯示公園位置呐矾,這些都是一個個圖層蜒犯,需要往地圖上疊加组橄。而加載這些圖層用到的地圖類也不盡相同罚随。比如:
AGSDynamicMapServiceLayer layer = [AGSDynamicMapServiceLayer dynamicMapServiceLayerWithURL:url];
用來加載動態(tài)圖層淘菩。
AGSGraphicsLayer *graphicsLayer = [AGSGraphicsLayer graphicsLayer];
用來加載圖像層遵班。
我目前用到這兩個,當(dāng)然還有其他很多圖層潮改,之前都有研究過狭郑,走過不少彎路。
AGSCredential *credential = [[AGSCredential alloc] initWithToken:data.appToken referer:data.appReferer];
地圖憑證汇在,保證系統(tǒng)的安全和對用戶的控制。
AGSQueryTask *queryTask = [AGSQueryTask queryTaskWithURL:url credential:credential];
queryTask.delegate = self;
創(chuàng)建查詢?nèi)蝿?wù)亩鬼。
AGSQuery *query = [AGSQuery query];
query.whereClause = data.whereClause;
query.outFields = [NSArray arrayWithObject:@"*"];
query.returnGeometry = YES;
query.outSpatialReference = self.mapView.spatialReference;
[queryTask executeWithQuery:query];
創(chuàng)建查詢參數(shù)阿蝶。
查詢?nèi)蝿?wù)和查詢參數(shù)用于顯示圖層上已經(jīng)標(biāo)出的指定的點。有些需求可能需要對這些點進(jìn)行抽稀玷过,我在抽稀過程中使用了冒泡算法焚廊,加了點動態(tài)規(guī)劃习劫。
AGSIdentifyTask *identifyTask = [AGSIdentifyTask identifyTaskWithURL:url];
identifyTask.delegate = self;
創(chuàng)建一個Id任務(wù),之前使用這個去加載袒餐,因為內(nèi)容定制度較差,后來改用服務(wù)端進(jìn)行灸眼。
但無論是查詢?nèi)蝿?wù)或者Id任務(wù),它們都需要設(shè)置代理霉囚,然后實現(xiàn)代理的方法匕积,確保操作得到響應(yīng)。
接下來介紹測距和測面盅粪。
AGSSketchGraphicsLayer *measureSketchLayer = [AGSSketchGraphicsLayer graphicsLayer];
需要使用到該圖層悄蕾,在這圖層上進(jìn)行繪制。
然后你需要理解圖層上幾個相關(guān)變量的含義帆调,比如:
midVertexSymbol
allowHitTest
vertexSymbol
selectedVertexSymbol
mainSymbol
geometry
特別是geometry贷帮,幾種幾何圖形不同的創(chuàng)建方式如下:
//創(chuàng)建線
measureSketchLayer.geometry = [[AGSMutablePolyline alloc] initWithSpatialReference:self.mapView.spatialReference];
//創(chuàng)建面
measureSketchLayer.geometry = [[AGSMutablePolygon alloc] initWithSpatialReference:self.mapView.spatialReference];
記得設(shè)置一下地圖的觸摸代理。
self.mapView.touchDelegate = measureSketchLayer;
下面的通知撵枢,AGSSketchGraphicsLayerGeometryDidChangeNotification锄禽,是用來監(jiān)測幾何體的形狀變化。
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(respondToGeomChanged:)
name:AGSSketchGraphicsLayerGeometryDidChangeNotification
object:nil];
在這個方法里面處理距離變化或者面積變化沃但,以及處理之后要介紹的標(biāo)繪內(nèi)容。
介紹一下距離和面積的計算方法:
AGSGeometry *sketchGeometry = measureSketchLayer.geometry;
AGSGeometryEngine *geometryEngine = [AGSGeometryEngine defaultGeometryEngine];
self.distance = [geometryEngine geodesicLengthOfGeometry:sketchGeometry inUnit:AGSSRUnitMeter];
距離計算垂攘,單位是米淤刃。
AGSGeometry *sketchGeometry = measureSketchLayer.geometry;
AGSGeometryEngine *geometryEngine = [AGSGeometryEngine defaultGeometryEngine];
self.area = [geometryEngine shapePreservingAreaOfGeometry:sketchGeometry inUnit:AGSAreaUnitsSquareMeters];
面積計算,單位是平方米陨仅。
接下來介紹標(biāo)繪,包括點標(biāo)繪触徐、線標(biāo)繪狐赡、面標(biāo)繪、文字標(biāo)繪猾警。
創(chuàng)建方式:
//創(chuàng)建點
measureSketchLayer.geometry = [[AGSMutablePoint alloc] initWithX:NAN y:NAN spatialReference:self.mapView.spatialReference];
//創(chuàng)建線
measureSketchLayer.geometry = [[AGSMutablePolyline alloc] initWithSpatialReference:self.mapView.spatialReference];
//創(chuàng)建面
measureSketchLayer.geometry = [[AGSMutablePolygon alloc] initWithSpatialReference:self.mapView.spatialReference];
在AGSSketchGraphicsLayerGeometryDidChangeNotification的通知方法中发皿,更新點的信息。
如果你希望在點上面增加自定義視圖穴墅,可以這么寫:
self.mapView.callout.customView = antherView;
其中antherView,是我自己定義的一個視圖皇钞,比如在視圖上放個刪除按鈕松捉,放個label顯示數(shù)據(jù)等等,都可以可柿。
如果你想創(chuàng)建文字標(biāo)繪丙者,你需要這么寫:
AGSTextSymbol *textSymbol = [[AGSTextSymbol alloc] initWithText:self.plottingData.title
color:[UIColor redColor]];
textSymbol.fontFamily = @"Heiti SC";
textSymbol.vAlignment = AGSTextSymbolVAlignmentMiddle;
textSymbol.hAlignment = AGSTextSymbolHAlignmentCenter;
textSymbol.fontSize = 13;
textSymbol.offset = CGPointMake(0, 0);
AGSCompositeSymbol *compositeSymbol = [AGSCompositeSymbol compositeSymbol];
[compositeSymbol addSymbol:textSymbol];
AGSPoint *point_ags = [[AGSPoint alloc] initWithX:point.x y:point.y spatialReference:self.mapView.spatialReference];
AGSGraphic *graphic = [AGSGraphic graphicWithGeometry:point_ags symbol:compositeSymbol attributes:nil];
其中各種參數(shù)的含義,你通過英文的字面意思或者實際操作目锭,應(yīng)該都能理解纷捞。
有一個業(yè)務(wù)需求提到需要保存標(biāo)繪數(shù)據(jù),研究發(fā)現(xiàn)AGSGeometry實現(xiàn)了AGSCoding協(xié)議奖唯,可以將AGSGeometry對象轉(zhuǎn)成NSDictionary缀辩,這樣就可以使用歸檔保存在本地,歸檔類NSKeyedArchiver臀玄。
接下來介紹糾錯健无,路線規(guī)劃,運動累贤,搜索,離線地圖硼被。
糾錯需要讓地圖標(biāo)識符一直處于地圖的中點渗磅,調(diào)用的函數(shù)為:
CGPoint point = self.view.center;
AGSPoint *mapPoint = [self.mapView toMapPoint:point];
配合之前說的visibleAreaEnvelope就可以實現(xiàn)了。
路線規(guī)劃涉及到畫路線軌跡仔掸,先創(chuàng)建一個圖像層AGSGraphicsLayer医清,然后創(chuàng)建直線,或者在直線終點創(chuàng)建點会烙。
//創(chuàng)建直線持搜,添加路徑
AGSMutablePolyline *polyline = [[AGSMutablePolyline alloc] initWithSpatialReference:self.mapView.spatialReference];
[polyline addPathToPolyline];
//創(chuàng)建點,添加點路徑
AGSPoint *point = [AGSPoint pointWithX:x y:y spatialReference:self.mapView.spatialReference];
[polyline addPointToPath:point];
起點和終點的符號是由圖片和文字組成葫盼,你需要這么寫:
//創(chuàng)建一個圖片符號
AGSPictureMarkerSymbol *marker = [AGSPictureMarkerSymbol pictureMarkerSymbolWithImage:image];
//創(chuàng)建一個文字符號
AGSTextSymbol *textSymbol = [[AGSTextSymbol alloc] initWithText:text
color:[UIColor whiteColor]];
//然后創(chuàng)建一個AGSCompositeSymbol來加入這兩個符號贫导,如:
AGSCompositeSymbol *compositeSymbol = [AGSCompositeSymbol compositeSymbol];
[compositeSymbol addSymbol:marker];
[compositeSymbol addSymbol:textSymbol];
路線規(guī)劃的數(shù)據(jù)是通過請求服務(wù)端獲取的,獲取的數(shù)據(jù)有一定的格式孩灯,一般為分段的一系列點的集合峰档,需要根據(jù)約定好的規(guī)范進(jìn)行適配寨昙。
路線畫出來了掀亩,那么如何跳到路線所在位置的中心,你可以這么寫:
AGSMutableEnvelope *envelope = [routeGraphicsLayer.fullEnvelope mutableCopy];
[envelope expandByFactor:2.5];
[self.mapView zoomToEnvelope:envelope animated:YES];
說說運動:
運動中需要計步槽棍,計步采用加速度傳感器實現(xiàn),封裝了一個StepManager來管理步數(shù)缆巧,具體的算法就不細(xì)細(xì)展開豌拙,非本篇所講內(nèi)容。
運動需要在后臺長時間運行墩莫,需要申請后臺權(quán)限逞敷,上架的時候在備注中講清楚這個事,態(tài)度誠懇裂问,必要的時候叫蘋果幾聲爹牛柒,一般就沒什么問題了。
double distance = [geometryEngine geodesicLengthOfGeometry:lineGraphic.geometry inUnit:AGSSRUnitMeter];
運動采用的也是距離函數(shù)皮壁,保存上一點和下一點,然后規(guī)定一下距離超過5米或者10米時才畫直線虑瀑。
接下來說搜索滴须,
[self.mapView zoomToScale:self.mapView.mapScale withCenterPoint:(AGSPoint *)graphic.geometry animated:YES];
當(dāng)點擊搜索出來的紅色點時,或者點擊表格數(shù)據(jù)時痛侍,相應(yīng)的紅點要位于中心魔市。
搜索時需要與路線規(guī)劃對接赵哲,記住在程序設(shè)計的時候君丁,多抽離模塊代碼,避免相同模塊寫了兩份相同的代碼。
離線地圖比較簡單涧偷,就沒什么好說的了。需要注意的是這是個在后臺發(fā)起的下載任務(wù)喻鳄,而不是屬于某個控制器的下載任務(wù)确封,判斷好總大小,控制好節(jié)點數(shù)據(jù)颜曾,處理好暫停秉剑、繼續(xù),就沒什么問題了诡曙。
總結(jié):ArcGIS是定位最準(zhǔn)確的地圖略水,比高德和百度還準(zhǔn)確。但是缺點也是很明顯的渊涝,API封裝臃腫跨释,內(nèi)存消耗大,線程處理不夠優(yōu)美煤傍,地圖塊狀切割太大,用戶體驗不夠好等等五续。比起高德和百度地圖來說,還是差了一截凶伙。但是它碎,處理地圖的方式基本都是一致的,可以作為參考扳肛。
尾記:當(dāng)年研究的熱血涌上心頭挖息,不斷地自我突破,它是我1個人花了1個多月的時間做出來套腹,同樣的Deadline,安卓是兩個人做的幢码,還不算上我調(diào)通接口的時間尖飞。而且,論體驗和Bug率來說瓦糕,都屬上層腋么,最終讓地測院的人贊不絕口。
當(dāng)然圣勒,當(dāng)初的架構(gòu)還沒有使用到我文章列表的“iOS架構(gòu)”摧扇,代碼的結(jié)構(gòu)也非常的不優(yōu)美,但是注釋還是都有的吁峻。之所以出現(xiàn)這樣的問題,還是那句話:Deadline不是第一生產(chǎn)力用含,只是給了你把一堆Shit交上去的勇氣。
看完了痴鳄,不點個贊嗎缸夹?哈哈哈,隨便你橡类,開心就好趟妥。