瓦片地圖
首先解釋一下什么是瓦片地圖,我們使用的地圖(例如百度,高德)都有一個(gè)底圖卦方,在每一級(jí)的縮放比例下,都有一張很大的底圖泰佳,這張底圖按固定的大小切割成若干份盼砍,在地圖顯示時(shí)根據(jù)顯示范圍和縮放比例尘吗,請(qǐng)求對(duì)應(yīng)幾張小的底圖,這些底圖就是瓦片地圖浇坐。
項(xiàng)目需求
項(xiàng)目使用的是高德地圖睬捶,基本的操作可以參考官方文檔,然后需要疊加自己的瓦片地圖近刘。在官方文檔中找了好久擒贸,終于在繪制面_繪制瓦片圖層這一節(jié)中找到了相應(yīng)的方法。主要步驟是先添加一個(gè)MATileOverlay
到地圖中觉渴,然后實(shí)現(xiàn)delegate中的mapView:viewForOverlay:
函數(shù)介劫,返回一個(gè)renderer對(duì)象。項(xiàng)目中用到的所有地圖都要加載這個(gè)瓦片服務(wù)案淋,所以直接從MAMapView
繼承出一個(gè)自定義MapView座韵。
代碼實(shí)現(xiàn)
自定義一個(gè)PPGMapView
,繼承自MAMapView
踢京。
#import <MAMapKit/MAMapKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface PPGMapView : MAMapView
@end
NS_ASSUME_NONNULL_END
在初始化時(shí)添加自定義瓦片圖層誉碴。
- (void)commonInit {
// delegate指向自己
self.delegate = self;
// 初始化縮放級(jí)別
self.zoomLevel = 18.f;
self.showsUserLocation = YES;
self.userTrackingMode = MAUserTrackingModeFollow;
// 添加自定義瓦片圖層
PPGTileOverlay *overlay = [[PPGTileOverlay alloc] init];
overlay.maximumZ = 20;
overlay.minimumZ = 14;
overlay.boundingMapRect = MAMapRectWorld;
[self addOverlay:overlay];
}
實(shí)現(xiàn)renderer
方法
- (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id <MAOverlay>)overlay {
// 外部delegate首先進(jìn)行響應(yīng),如果沒有實(shí)現(xiàn)瓣距,則使用自定義地圖的實(shí)現(xiàn)
if ([self.extDelegate respondsToSelector:_cmd]) {
MAOverlayRenderer *renderer = [self.extDelegate mapView:mapView rendererForOverlay:overlay];
if (renderer) {
return renderer;
}
}
if ([overlay isKindOfClass:[MATileOverlay class]]) {
// 默認(rèn)的瓦片底圖renderer
MATileOverlayRenderer *renderer = [[MATileOverlayRenderer alloc] initWithTileOverlay:overlay];
return renderer;
}
return nil;
}
下面是自定義瓦片地圖的代碼
/**
自定義瓦片圖層黔帕,繼承自MATileOverlay
*/
@interface PPGTileOverlay : MATileOverlay
@end
@implementation PPGTileOverlay
- (NSURL *)URLForTilePath:(MATileOverlayPath)path {
#warning 自行替換ip和端口號(hào)
NSString *urlStr = [NSString stringWithFormat:@"http://ip:port/mapImg/tiles/%ld/%ld_%ld.png", (long)path.z, (long)path.x, (long)path.y];
return [NSURL URLWithString:urlStr];
}
// 使用template和上面的方法會(huì)丟掉端口號(hào),所以自己去請(qǐng)求蹈丸,然后回調(diào)結(jié)果
- (void)loadTileAtPath:(MATileOverlayPath)path result:(void (^)(NSData *, NSError *))result {
NSURL *url = [self URLForTilePath:path];
// 使用SDWebImage管理本地瓦片
SDImageCache *cache = [SDImageCache sharedImageCache];
UIImage *image = [cache imageFromCacheForKey:[url absoluteString]];
if (image) {
result(UIImagePNGRepresentation(image), nil);
} else {
// 在3D地圖中如果瓦片請(qǐng)求失敗成黄,會(huì)一直重復(fù)去請(qǐng)求,這里用set存儲(chǔ)已請(qǐng)求的瓦片白华,不做重復(fù)請(qǐng)求
static NSMutableSet *urlSet;
if (urlSet == nil) {
urlSet = [NSMutableSet new];
}
if ([urlSet containsObject:url]) {
return;
}
[[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url options:SDWebImageDownloaderHighPriority progress:nil completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
if (finished && data) {
[cache storeImageDataToDisk:data forKey:[url absoluteString]];
}
result(data, error);
[urlSet addObject:url];
}];
}
}
@end
到這里瓦片地圖就添加上去了慨默,這里還有一個(gè)delegate的問題,因?yàn)槲覀兂跏蓟臅r(shí)候設(shè)置delegate為自己了弧腥,瓦片地圖才能正常顯示厦取,如果這是在使用的時(shí)候delegate設(shè)置為另外一個(gè)對(duì)象,而他沒實(shí)現(xiàn)上面的renderer 方法管搪,那么我們的瓦片地圖又不能顯示了虾攻,所以這里使用了外部delegate方法。
在自定義MapView中添加一個(gè)Extension更鲁,添加私有屬性extDelegate
@interface PPGMapView () <MAMapViewDelegate>
/**
外部的代理
*/
@property (nonatomic, weak) id<MAMapViewDelegate> extDelegate;
@end
重寫setDelegate方法和respondsToSelector方法
// 保存本地delegate霎箍,記錄外部delegate
- (void)setDelegate:(id<MAMapViewDelegate>)delegate {
if (delegate == self) {
[super setDelegate:delegate];
} else {
self.extDelegate = delegate;
}
}
- (BOOL)respondsToSelector:(SEL)aSelector {
BOOL responds = [super respondsToSelector:aSelector];
if (responds) {
return responds;
} else {
return [self.extDelegate respondsToSelector:aSelector];
}
}
runtime轉(zhuǎn)發(fā)方法到外部delegate
// runtime消息轉(zhuǎn)發(fā)
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.extDelegate respondsToSelector:aSelector]) {
return self.extDelegate;
}
return nil;
}