iOS:如何使用离线地图

8

我的应用需要使用离线地图,并且使用GPX文件来规划路线。我发现OpenStreetMap可以实现此功能,但是是否有更好的服务(最好能提供等高线)?谢谢。


http://www.mapbox.com? - Wain
4个回答

11
离线地图需要在iOS方面有一定的经验,因为相关项目和例子并不多。 然而,你可以借助一个名为Route-Me的项目作为起点。 我们使用它来开发Metro Valencia Offline,并成功将其上架到App Store。 我写了一篇小教程,介绍如何在你的项目中添加Route-Me。
基本上,你可以使用任何地图源(OpenStreetMaps是其中之一,也是我们使用的)。你需要做的是:
  1. 下载地图瓦片(https://metacpan.org/pod/Geo::OSM::Tiles)
  2. 将它们放入SQLite数据库中 (http://shikii.net/blog/downloads/map2sqlite-bin.zip)
  3. 修改Route-Me,以便从数据库而不是从OpenStreetMaps网站获取瓦片

你能解释一下这个异常吗? "SimpleMap[1020:907] *** 由于未捕获的异常 'NSInternalInconsistencyException',原因是:如果 [contents minZoom] 比 [tileSource minZoom] 小 1.5,则图形和内存过度使用。" - cyclingIsBetter
3
实际上这是由Route-Me引发的一个NSAssert,我从来没有完全理解它。我注释掉了它,从未遇到任何问题。该NSAssert可以在RMMapContents.m中找到,在setTileSource:方法内。另外还有一个在setMinZoom:里,但我保留了最后一个。 - Alex Salom
哇,太棒了,现在它可以工作了!谢谢...如果我有其他问题,我可以在这里写吗? - cyclingIsBetter
你能建议我如何在我的地图中添加一个GPX文件吗?我有这个文件,怎么展示它在地图上呢? - cyclingIsBetter
你应该提出一个新的问题,而不是在评论中写下它。无论如何,给我发一封邮件到hola@alexsalom.es,我会尽力帮助你。 - Alex Salom
Alex刚刚为我节省了数小时的挫败感。 - Tom Kincaid

2

1
这里还有Nutiteq地图SDK:http://developer.nutiteq.com,适用于iOS和Android的离线地图,支持Xamarin IDE(C#)和本地语言(Java、ObjectiveC、Swift)。它不是像Skobbler那样主要用于路线规划和导航,而是更专注于具有交互式图层(点、线和多边形在地图顶部)的复杂地图。一个优点是你可以使用自己的基础地图源(内部、第三方),而不仅仅是SDK本身提供的OpenStreetMap。免责声明:我是它的开发者。

它是否需要许可证? - famfamfam

1

我使用了MapKit的默认地图和MKTileOverlay的子类来保存下载过的瓦片,并返回已经缓存的瓦片,而不必重新下载。

1)更改默认地图的源为MKTileOverlay的子类(这里使用了“开放街道地图”)

- (void)viewDidLoad{
    [super viewDidLoad];
    static NSString * const template = @"http://tile.openstreetmap.org/{z}/{x}/{y}.png";

    VHTileOverlay *overlay = [[VHTileOverlay alloc] initWithURLTemplate:template];
    overlay.canReplaceMapContent = YES;
    [self.mapView addOverlay:overlay level:MKOverlayLevelAboveLabels];
}

2) 从MKTileOverlay派生子类

@interface VHTileOverlay() // MKTileOverlay subclass
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@end

@implementation VHTileOverlay

-(instancetype)initWithURLTemplate:(NSString *)URLTemplate{

    self = [super initWithURLTemplate:URLTemplate];
    if(self){
        self.directoryPath = cachePath;
        self.operationQueue = [NSOperationQueue new];
    }
    return self;
}


-(NSURL *)URLForTilePath:(MKTileOverlayPath)path {
    return [NSURL URLWithString:[NSString stringWithFormat:@"http://tile.openstreetmap.org/%ld/%ld/%ld.png", (long)path.z, (long)path.x, (long)path.y]];
}

-(void)loadTileAtPath:(MKTileOverlayPath)path
                result:(void (^)(NSData *data, NSError *error))result
{
    if (!result) {
        return;
    }

    NSString *pathToFilfe = [[self URLForTilePath:path] absoluteString];
    pathToFilfe = [pathToFilfe stringByReplacingOccurrencesOfString:@"/" withString:@"|"];
    // @"/" - those are not approriate for file's url...

    NSData *cachedData = [self loadFileWithName:pathToFilfe]; 
    if (cachedData) {
        result(cachedData, nil);
    } else {
        NSURLRequest *request = [NSURLRequest requestWithURL:[self URLForTilePath:path]];
        __block VHTileOverlay *weakSelf = self;
        [NSURLConnection sendAsynchronousRequest:request
                                           queue:self.operationQueue
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
                                   NSLog(@"%@",[weakSelf URLForTilePath:path]);

                                   if(data){
                                       [self saveFileWithName:[[weakSelf URLForTilePath:path] absoluteString] imageData:data];
                                   }
                                   result(data, connectionError);
        }];
    }
}

-(NSString *)pathToImageWithName:(NSString *)fileName
{
    NSString *imageFilePath = [[OfflineMapCache sharedObject].cachePath stringByAppendingPathComponent:fileName];
    return imageFilePath;
}

- (NSData *)loadFileWithName:(NSString *)fileName
{
    NSString *imagePath = [self pathToImageWithName:fileName];
    NSData *data = [[NSData alloc] initWithContentsOfFile:imagePath];
    return data;
}

- (void)saveFileWithName:(NSString *)fileName imageData:(NSData *)imageData
{
//    fileName = [fileName stringByReplacingOccurrencesOfString:@"/" withString:@"|"];
//    NSString *imagePath = [self pathToImageWithName:fileName];
//    [imageData writeToFile:imagePath atomically:YES];
}

取消“saveFileWithName”的注释并在模拟器上运行它。您还可以添加NSLog(fileName)以了解获取所有所需瓦片的位置。 (模拟器缓存位于Users / YOU / Library / Developer / CoreSimulator / Devices / ..., 而Library是一个隐藏的目录)

在缓存所有所需内容后,将其放入您的应用程序包中(就像任何其他图像一样,如果您想从盒子缓存的地图中获取)。 并告诉您的

- (void)loadTileAtPath:(MKTileOverlayPath)path
                result:(void (^)(NSData *data, NSError *error))result

从bundle中获取瓦片。

现在,我可以安装我的应用程序,关闭Wi-Fi,然后无论如何都能获取这些地图。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接