iOS高德地圖添加自定義瓦片地圖

瓦片地圖

首先解釋一下什么是瓦片地圖,我們使用的地圖(例如百度,高德)都有一個底圖,在每一級的縮放比例下,都有一張很大的底圖,這張底圖按固定的大小切割成若干份,在地圖顯示時根據(jù)顯示范圍和縮放比例,請求對應幾張小的底圖,這些底圖就是瓦片地圖。

項目需求

項目使用的是高德地圖,基本的操作可以參考官方文檔,然后需要疊加自己的瓦片地圖。在官方文檔中找了好久,終于在繪制面_繪制瓦片圖層這一節(jié)中找到了相應的方法。主要步驟是先添加一個MATileOverlay到地圖中,然后實現(xiàn)delegate中的mapView:viewForOverlay:函數(shù),返回一個renderer對象。項目中用到的所有地圖都要加載這個瓦片服務,所以直接從MAMapView繼承出一個自定義MapView。

代碼實現(xiàn)

自定義一個PPGMapView,繼承自MAMapView。

#import <MAMapKit/MAMapKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface PPGMapView : MAMapView

@end

NS_ASSUME_NONNULL_END

在初始化時添加自定義瓦片圖層。

- (void)commonInit {
    // delegate指向自己
    self.delegate = self;
    // 初始化縮放級別
    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];
}

實現(xiàn)renderer方法

- (MAOverlayRenderer *)mapView:(MAMapView *)mapView rendererForOverlay:(id <MAOverlay>)overlay {
    // 外部delegate首先進行響應,如果沒有實現(xiàn),則使用自定義地圖的實現(xiàn)
    if ([self.extDelegate respondsToSelector:_cmd]) {
        MAOverlayRenderer *renderer = [self.extDelegate mapView:mapView rendererForOverlay:overlay];
        if (renderer) {
            return renderer;
        }
    }
    if ([overlay isKindOfClass:[MATileOverlay class]]) {
        // 默認的瓦片底圖renderer
        MATileOverlayRenderer *renderer = [[MATileOverlayRenderer alloc] initWithTileOverlay:overlay];
        return renderer;
    }
    return nil;
}

下面是自定義瓦片地圖的代碼

/**
 自定義瓦片圖層,繼承自MATileOverlay
 */
@interface PPGTileOverlay : MATileOverlay

@end

@implementation PPGTileOverlay

- (NSURL *)URLForTilePath:(MATileOverlayPath)path {
#warning 自行替換ip和端口號
    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和上面的方法會丟掉端口號,所以自己去請求,然后回調結果
- (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地圖中如果瓦片請求失敗,會一直重復去請求,這里用set存儲已請求的瓦片,不做重復請求
        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

到這里瓦片地圖就添加上去了,這里還有一個delegate的問題,因為我們初始化的時候設置delegate為自己了,瓦片地圖才能正常顯示,如果這是在使用的時候delegate設置為另外一個對象,而他沒實現(xiàn)上面的renderer 方法,那么我們的瓦片地圖又不能顯示了,所以這里使用了外部delegate方法。

在自定義MapView中添加一個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轉發(fā)方法到外部delegate

// runtime消息轉發(fā)
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if ([self.extDelegate respondsToSelector:aSelector]) {
        return self.extDelegate;
    }
    return nil;
}

代碼倉庫地址:
https://github.com/gaopeng-hz/TileOverlay

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容