user717452
user717452

Reputation: 111

Offline Map in iOS

I have a big need to do an offline map for my app, as it is made mostly for Thailand, where internet connection is often hard to come by. I am using OpenStreetMap right now for my MKTileOverlay but am having issues implementing it for offline use. I have found a tutorial that says to subclass MKTileOverlay. So, in my ViewController where the map is I have:

 -(void) viewWillAppear:(BOOL)animated {
CLLocationCoordinate2D coord = {.latitude =  15.8700320, .longitude =  100.9925410};
MKCoordinateSpan span = {.latitudeDelta =  3, .longitudeDelta =  3};
MKCoordinateRegion region = {coord, span};
[mapView setRegion:region];
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"Map";
    NSString *template = @"http://tile.openstreetmap.org/{z}/{x}/{y}.png";
    self.overlay = [[XXTileOverlay alloc] initWithURLTemplate:template];
    self.overlay.canReplaceMapContent = YES;
    [mapView addOverlay:self.overlay level:MKOverlayLevelAboveLabels];


}
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id)overlay {

        return [[MKTileOverlayRenderer alloc] initWithTileOverlay:overlay];

}

In my subclass of MKTileOverlay, I have:

- (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;
    }
    NSData *cachedData = [self.cache objectForKey:[self URLForTilePath:path]];
    if (cachedData) {
        result(cachedData, nil);
    } else {
        NSURLRequest *request = [NSURLRequest requestWithURL:[self URLForTilePath:path]];
        [NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
            result(data, connectionError);
        }];
    }
}

The issue is that NOTHING gets loaded at all, unless I comment out the code in the subclass. Where am I messing up?

Upvotes: 2

Views: 4242

Answers (2)

Naveen Kumar H S
Naveen Kumar H S

Reputation: 1304

I am also interested in loading the map in offline mode.

  1. First of all I downloaded the tiles into my local system (computer) with the help of gmapcatcher.

    You can get some information about the gmapcatcher here. And you can download this application here.

    One more important thing if you want to download the files check offline and there are many ways that can save the files. For this scenario select OSM in setting window (located to the left side where you will specify the path to download in the setting window)

  2. Simply add that created tiles folder to the project.

  3. And I don' know objective-c so I am implementing in the swift and I posted that code only.

Here is the code

import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.mapView.delegate = self

        let baseURL = NSBundle.mainBundle().bundleURL.absoluteString
        let urlTemplate = baseURL.stringByAppendingString("OSM_sat_tiles/{z}/{x}/{y}.png")
        //OSM_sat_tiles is the folder name which has the tiles.
        let layer = MKTileOverlay(URLTemplate: urlTemplate)
        layer.canReplaceMapContent = true
        self.mapView.addOverlay(layer)
    }

    func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
        if overlay is MKTileOverlay {
            let renderer = MKTileOverlayRenderer(overlay:overlay)
            renderer.alpha = 0.8
            return renderer
        }
        return MKTileOverlayRenderer(overlay: overlay)
    }
}

I will go brief on the code.

In the viewDidLoad delegate method I am creating MKTileOverlay by giving path of the tiles folder. Since I have placed in the project it will be in the bundle.

And in the rendererForOverlay delegate method I am returning the MKTileOverlayRenderer.

Upvotes: 4

Marco Santarossa
Marco Santarossa

Reputation: 4066

You need a server which responds to your http://<domain>/{z}/{x}/{y}/image.png with an image or you must save the tiles in your app bundle using the path /tiles/{z}/{x}/{y}/image.png.

Upvotes: 1

Related Questions