图层是空间数据的载体,如果你对ArcGIS Server稍有了解的话,就能明白API里不同类型的图层对应了服务器端发布的不同Service,可以分成两大类:静态图层和动态图层。
静态图层泛指缓存过的地图服务,除非服务端删除或更新缓存,否则客户端请求的数据永远是固定不变的,而动态图层正好相反,服务器根据每个请求动态的生成数据,一静一动各有用途。
表3-2-1 不同类型图层的继承关系
AGSLayer是所有图层的基类,声明了空间参考、最大范围、初始范围、单位和图层委托等基本属性,还有图层加载的相关方法。
表3-2-2图层类的属性和方法
静态图层根据一定的规则提前生成缓存,这样客户端的请求就能直接调用缓存地图切片,显著提高了地图请求的响应速度。常常用于那些使用频率高、更新周期长的基础地理数据,包括大家常常用到的电子地图、地形图、卫星影像图等。
AGSTiledLayer继承自AGSLayer,同时也派生出不同类型的静态图层:
表3-2-3 静态图层的继承关系
在MVC结构中,AGSTiledLayer属于Model角色,对应的View角色是AGSTiledLayerView。其中实现了关键的异步获取切片数据操作,并获取到切片服务的缓存规则:
表3-2-4 静态图层的方法和属性
注意:如果静态服务图层的空间参考与MapView的不一致,就无法显示。
切片地图服务图层是最常用的服务,ArcGIS在线提供了一系列,其切片规则(tiling Schema)里描述了切片的尺寸、格式、比例尺、级别等信息。
“虽然图层已经初始化并添加到地图中,但切片信息还是空值?”,建议先判断加载成功与否,尤其是在网速不好的情况下:
//图层加载完成后才能获取完整的属性 if(layer.loaded){ NSLog(@"TilingScheme: %@", layer.tileInfo);}
由于Esri与微软之间的合作关系,保证了ArcGIS用户也能使用最新的微软Bing全球影像地图服务,当然这也是静态服务,加入用户的密匙(在ArcGISServer Manager页面可获取)就能使用:
NSString* bingMapsKey = @"--your--key--"; AGSBingMapLayer* layer = [[AGSBingMapLayeralloc]initWithAppID:bingMapsKeystyle:AGSBingMapLayerStyleAerial];
*Bing服务空间参考为横轴Web摩卡托:
WGS1984 Web Mercator (Auxiliary Sphere) ,WKID=102100:
Esri与OpenStreetMap之间也建立了合作关系, ArcGIS用户同样也能使用OpenStreetMap全球矢量地图服务:
AGSOpenStreetMapLayer* OSMLyr = [[AGSOpenStreetMapLayeralloc]init];
*OpenStreetMap服务空间参考为横轴Web摩卡托:
WGS1984 Web Mercator (Auxiliary Sphere) ,WKID=102100:
国内流行的切片地图还有:谷歌地图、百度地图、MapABC、天地图等,各家地图的切片策略有差异但原理相同,了解其切片策略信息后,继承AGSTiledLayer拓展自定义切片图层 (Custom TiledLayer),就能正常使用该切片地图。
Esri提供了一个访问本地切片(png或jpg)的类 “OfflineTiledLayer”,可以作为我们设计自定义切片图层的参考,包含3部分:切片图层对象,切片数据解析委托和切片请求操作。下面的流程示意图能看出自定义切片加载的整个过程:
图3-2-6 自定义切片图层的调用流程
“OfflineTiledLayer“继承了AGSTiledLayer,并重写了切片信息(tileInfo)和切片获取方法(retrieveImageAsyncForTile),其初始化方法(initWithDataFramePath)也很简单:
//继承了AGSTiledLayer @interface OfflineTiledLayer :AGSTiledLayer { ... } //自定义切片服务元数据 @implementation OfflineTiledLayer -(AGSUnits) units { //todo } -(AGSSpatialReference*) spatialReference {//todo } -(AGSEnvelope*) fullEnvelope { //todo } -(AGSEnvelope*) initialEnvelope { //todo } -(AGSTileInfo*) tileInfo { //todo } -(NSOperation<AGSTileOperation>*)retrieveImageAsyncForTile:(AGSTile*)tile { //todo } //初始化方法-path:切片路径 - (id)initWithDataFramePath:(NSString*)patherror:(NSError**)outError; ... @end
初始化方法中需要输入“切片文件路径”,下图的示例数据是10.0版本的松散型(Exploded)切片缓存,切片服务元数据就保存在conf.cdi和conf.xml两个XML文件中,其中conf.cdi记录了地图全图范围、而conf.xml记录了空间参考、切片格式等信息,其中和切片组织(LODInfos)对应了_alllayers目录下的级别(L)-行号(R)-列号(C),简单理解就是用指定网格按指定比例尺一层层的把地图切成规则的图片矩阵,建立起了网格与切片文件的对应关系,使用时按网格检索切片文件,并动态拼接起来。
*关于切片规则的详细介绍,请参考网上其他资料。
图3-2-7 松散型切片的组织结构
1. 解析切片元数据:
“OfflineCacheParserDelegate“负责从元数据(conf.cdi和conf.xml)中解析出自定义的切片信息并构建出关键的AGSTileInfo对象:
- (id)initWithDataFramePath:(NSString *)path error:(NSError**) outError { if (self = [super init]) { self.dataFramePath = path; //解析 conf.cdi NSString* confCDI = [[NSBundlemainBundle] pathForResource:@"conf" ofType:@"cdi"inDirectory: _dataFramePath ]; NSXMLParser* xmlParser = [[NSXMLParser alloc]initWithContentsOfURL:[NSURL fileURLWithPath:confCDI]]; OfflineCacheParserDelegate*parserDelegate = [[[OfflineCacheParserDelegate alloc] init] autorelease] ; [xmlParsersetDelegate:parserDelegate]; [xmlParser parse]; [xmlParser release]; //解析 conf.xml NSString* confXML = [[NSBundlemainBundle] pathForResource:@"conf" ofType:@"xml"inDirectory: _dataFramePath]; ... }
以下是XML元数据的具体解析方法:
-(void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementNamenamespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { if ([elementNameisEqualToString:@"LODInfo"]){ self.lod = [[[AGSLODalloc]initWithLevel:_level resolution:_resolution scale:_scale] autorelease]; [self.lods addObject:_lod]; }else if ([elementNameisEqualToString:@"CacheInfo"]){ _tileSize = CGSizeMake(_tileWidth,_tileHeight); self.spatialReference =[[[AGSSpatialReference alloc] initWithWKID:_WKID WKT:_WKT] autorelease]; self.tileOrigin = [[[AGSPointalloc] initWithX:_tileOriginX y:_tileOriginYspatialReference:_spatialReference] autorelease]; self.fullEnvelope = [AGSEnvelopeenvelopeWithXmin:_xmin ymin:_ymin xmax:_xmax ymax:_ymax spatialReference:_spatialReference]; self.tileInfo = [[[AGSTileInfoalloc] initWithDpi: _dpi format:_tileFormat lods:_lods origin:_tileOrigin spatialReference:_spatialReference tileSize:_tileSize] autorelease]; } }
2. 请求和获取切片数据:
“OfflineTileOperation“按照地图所请求AGSTile的level, row, column参数获取本地切片图片文件,将其填充到Image属性:
//根据 Level, Row,Column获取切片
@try {
//Level ('L' followed by 2 digits)
NSString *decLevel = [NSStringstringWithFormat:@"L%02d",self.tile.level];
//Row ('R' followed by 8hexadecimal digits)
NSString *hexRow = [NSStringstringWithFormat:@"R%08x",self.tile.row];
//Column ('C' followed by 8hexadecimal digits)
NSString *hexCol = [NSStringstringWithFormat:@"C%08x",self.tile.column];
NSString*dir = [_allLayersPathstringByAppendingFormat:@"/%@/%@",decLevel,hexRow];
//查找PNG格式切片
NSString *tileImagePath =[[NSBundle mainBundle] pathForResource:hexCol ofType:@"png"inDirectory:dir];
...
}
@catch (NSException *exception) {
NSLog(@"main: Caught Exception%@: %@", [exception name], [exception reason]);
}
@finally {
//Invoke the layer's action method
[_target performSelector:_actionwithObject:self];
}
*OfflineTiledLayer用来解释自定义切片服务图层设计原理,不适合大规模离线使用,因为松散型的切片小文件分发、更新非常耗时,建议采用下一节的AGSLocalTiledLayer接口+切片包TPK压缩格式。
*加载其他在线切片地图(百度地图、天地图和谷歌地图)的方法和代码,我会在后续章节专门做介绍。
本地切片图层(AGSLocalTiledLayer)也叫离线切片图层,是上一节“OfflineTiledLayer”示例的升级版,其面向的切片缓存数据源是ArcGIS 10.1新推出的切片地图包(.tpk文件)。如果用localTiledLayerWithName方法加载,会依次遍历该应用的bundle和Document目录,意味着如果tpk文件不大,可以直接将其打包在应用的资源文件中分发,如果tpk文件很大,也允许用户通过Itunes手动加载数据,前者的优点是分发简单,后者的优点是应用与数据不绑定,自由加载。
AGSLocalTiledLayer* layer = [AGSLocalTiledLayerlocalTiledLayerWithName:@"world"];
切片地图包(tile package)是紧凑型(compact)切片缓存数据的压缩包,提高了切片缓存数据的分发效率,可以用10.1桌面“分享为TPK”,也可以自己手动构建,详细过程可参考:http://blog.csdn.net/arcgis_mobile/article/details/8048549.