Reputation: 1011
I'm trying to update my tile map code to use iOS 7's MKTileOverlay
and MKTileOverlayRenderer
, and I could use some pointers for making things work better.
First, here is the iOS6 code: AppleTileOverlay.m and TileOverlayView.m. This still works quite well in iOS 7 when when I replace TileOverlayView
with a class that is identical in all ways except that it's a subclass of MKOverlayRenderer
instead of MKOverlayView
.
The new piece I'm testing is a subclass of MKTileOverlay
with the only method being:
-(NSURL *)URLForTilePath:(MKTileOverlayPath)path {
NSString *tileKey = [[NSString alloc] initWithFormat:@"%d%d%d", path.x, path.y, path.z];
NSString *tilePath = [[NSBundle mainBundle] pathForResource:tileKey ofType:nil inDirectory:@"TileFolder"];
NSURL *url;
if (tilePath) {
url = [NSURL fileURLWithPath:tilePath];
}
return url;
}
The map tiles load fine most of the time, but the log fills up with messages like this:
Error loading URL (null): Error Domain=NSURLErrorDomain Code=-1000 "bad URL" UserInfo=0x1b3e19e0 {NSUnderlyingError=0x1894d470 "bad URL", NSLocalizedDescription=bad URL}
from the method returning nil for the URL.
So the question is: Can I avoid those error messages, or should I just stick with the older overlay class I have?
Upvotes: 3
Views: 3934
Reputation: 141
I think with Swift 2.0, URLForFilePath(...) cannot return nil as it is not an Optional.
I managed to solve this problem using the MKTileOverlay subclass to check for valid tile path as above and loading a 'dummy' transparent tile if a tile image was not available.
override func URLForTilePath(path: MKTileOverlayPath) -> NSURL {
let tileKey = String(format:"%d/%d/%d",path.z,path.x,path.y)
let tilePath = NSBundle.mainBundle().pathForResource(tileKey, ofType: "png", inDirectory: "Maps/Map1880")
let blankTilePath = NSBundle.mainBundle().pathForResource("blank", ofType: "png", inDirectory: "Maps")
var url: NSURL
if ((tilePath) != nil)
{
url = NSURL.fileURLWithPath(tilePath!)
} else {
url = NSURL.fileURLWithPath(blankTilePath!)
}
return url;
}
This is not very elegant as it loads the blank tile for every tile that is not part of the overlay.
However, there is a better solution, thanks to User: junkpile on Apple Developer Forum, the problem with attempting to load non-existent overlay tiles is that boundingMapRect is set by default to MKMapRectWorld, i.e. the whole world.
To restrict this to the required overlay region, subclass MKTileOverlay.
Here's an example:
import MapKit
class CustomTileOverlay : MKTileOverlay {
override var boundingMapRect: MKMapRect {
get {
//North-East Corner of region
let lat1 = 53.46075
let long1 = -1.92618
//South-West Corner of region
let lat2 = 53.43018
let long2 = -1.992885
//Convert to Coordinates
let coord1 = CLLocationCoordinate2DMake(lat1,long1)
let coord2 = CLLocationCoordinate2DMake(lat2,long2)
//Convert to map points
let p1 = MKMapPointForCoordinate (coord1);
let p2 = MKMapPointForCoordinate (coord2);
//Return the MKMapRect
return MKMapRectMake(fmin(p1.x,p2.x), fmin(p1.y,p2.y), fabs(p1.x-p2.x), fabs(p1.y-p2.y));
}
}
}
Upvotes: 1
Reputation: 1300
You can avoid it by creating a custom MKTileOverlay
subclass like the following:
@interface MyTileOverlay : MKTileOverlay
@end
@implementation MyTileOverlay
- (NSURL *)URLForTilePath:(MKTileOverlayPath)path
{
NSString *tileKey = [[NSString alloc] initWithFormat:@"%d%d%d", path.x, path.y, path.z];
NSString *tilePath = [[NSBundle mainBundle] pathForResource:tileKey ofType:nil inDirectory:@"TileFolder"];
NSURL *url;
if (tilePath)
{
url = [NSURL fileURLWithPath:tilePath];
}
return url;
}
- (void)loadTileAtPath:(MKTileOverlayPath)path result:(void (^)(NSData *, NSError *))result
{
NSURL *url = [self URLForTilePath:path];
if (url)
{
[super loadTileAtPath:path result:result];
}
}
@end
The idea is to not trigger the tile loading if you are sure the tile does not exists.
Upvotes: 0
Reputation: 8294
Does this happen when you are looking at areas of the map that you don't have tiles for?
Because you check if you have the file and then don't set url
if the file doesn't exist, you return nil. You should return a valid NSURL to a transparent image instead.
Upvotes: 0
Reputation: 5128
My guess is you are somehow always trying to set url
despite not having a true, valid tilePath
. Add some debugging and see.
Upvotes: 0