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?
Offline maps are downloaded on your device's internal storage by default, but you can download them on an SD card instead. If your device is on Android 6.0 or higher, you can only save an area to an SD card that's set up for portable storage. To learn how to set up your SD card, get help from your phone's manufacturer.
Google Maps allows you to download maps for offline use, helping you save on mobile data when you're out and about. The option to save maps for later is especially helpful when network coverage is sparse — like when you are travelling or in a place with dampeners.
After you download an area, use the Google Maps app just like you normally would. If your internet connection is slow or absent, your offline maps will guide you to your destination as long as the entire route is within the offline map.
The map image layer whose cache tiles you want to delete. You can choose it by browsing to the desired service in Portal or you can drag and drop a web tile layer from the Project pane Portal tab to supply this parameter. The map image layer whose cache tiles you want to delete.
In our company, we chose to use MapBox for our offline mapping.
There, you can design and style your own maps using MapBox Studio, then export your maps (at a chosen range of zoom levels) into an external file. Ours is about 40Mb in size.
From there, you can use the MapBox iOS SDK to easily add it to your app.
(Disclaimer: no, I don't work for them ! We specifically chose them for the ability to define our own land/sea colors and styling, and the ability to have the map files included in our Xcode project, and usable offline.)
I do appreciate your exact question was how to get OpenStreetMap's own maps to be offline, but I hope this is useful to you.
It looks like you're not populating the cache. It's always going to be empty?
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) {
// Instantiate a new cache if we don't already have one.
if (!self.cache) self.cache = [NSCache new];
// Add the data into the cache so it's there for next time.
if (data) {
[self.cache setObject:data forKey:[self URLForTilePath:path]];
}
result(data, connectionError);
}];
}
I think that'll solve your problem here. Remember that NSCache does not persist to disk (only to memory) so while it does survive the app being backgrounded, you'll need something more complex in the long run if you want total offline capability (probably Core Data).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With