How to efficiently save and load offline maps when using MKTileOverlay

I want to display and also cache map fragments for offline use with a selected service such as Open Street Maps. This seems simple, as iOS7it works with:

static NSString * const template = @"http://tile.openstreetmap.org/{z}/{x}/{y}.png";
self.tileOverlay = [[ZSSTileOverlay alloc] initWithURLTemplate:template];
self.tileOverlay.canReplaceMapContent = YES;
[self.mapView addOverlay:self.tileOverlay level:MKOverlayLevelAboveLabels];

In my subclass, MKTileOverLayI override loadTileAtPath:result:to load tiles and save them to disk for offline use. Later I will implement a method for users to select which part of the map to load:

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

    NSString *url = [NSString stringWithFormat:@"http://tile.openstreetmap.org/%li/%li/%li.png", (long)path.z, (long)path.x, (long)path.y];
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
        if (data == nil) {
            // Load a default tile here
        } else {

            UIImage *image = [[UIImage alloc] initWithData:data];
            result(data, nil);
            NSString *pathForWritting = [self pathToWriteImage:path];
            NSString *fileName = [NSString stringWithFormat:@"%li", (long)path.y];
            [self saveImage:image withFileName:fileName ofType:@"png" inDirectory:pathForWritting];

        }
    }];

}

- (void)saveImage:(UIImage *)image withFileName:(NSString *)imageName ofType:(NSString *)extension inDirectory:(NSString *)directoryPath {
    // Create folder structure if it doesn't exist
    if (![[NSFileManager defaultManager] fileExistsAtPath:directoryPath]) {
        NSError *error = nil;
        [[NSFileManager defaultManager] createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
        if (error) {
            NSLog(@"ERROR: %@", error.localizedDescription);
        }
    }
    if ([[extension lowercaseString] isEqualToString:@"png"]) {
        NSString *fullPath = [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageName, @"png"]];
        [UIImagePNGRepresentation(image) writeToFile:fullPath atomically:YES];

    } else if ([[extension lowercaseString] isEqualToString:@"jpg"] || [[extension lowercaseString] isEqualToString:@"jpeg"]) {
        [UIImageJPEGRepresentation(image, 1.0) writeToFile:[directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", imageName, @"jpg"]] options:NSAtomicWrite error:nil];
    }
}

- (NSURL *)applicationDocumentsDirectory {
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory
                                                   inDomains:NSUserDomainMask] lastObject];
}

- (NSString *)pathToWriteImage:(MKTileOverlayPath)path {

    NSString *pathFolder = [NSString stringWithFormat:@"tiles/%li/%li", (long)path.z, (long)path.x];
    return [NSString stringWithFormat:@"%@/%@", [self applicationDocumentsDirectory].path, pathFolder];

}

This works exactly as I would expect, and it saves the images in the appropriate folders:

enter image description here

Now the real question is thrice here:

  • Does the tile save to disk and then load it back offline? Or should they be stored in something like SQL Lite DB?
  • , MapKit, loadTitleAtPath:? , MKMapView ?
  • (, , ), , ? ?
+4

All Articles