Вопрос

in my app I want to use offline maps and with this maps use gpx files to have a route. I found openstreetmap to do it, but is there some better services? (the best solution with level curves) thanks

Это было полезно?

Решение

Offline maps require a bit of experience in iOS since there aren't many projects and examples out there. However, you got one project called Route-Me which could give you a starting point. We used it to develop Metro Valencia Offline which successfully made it to the App Store. I wrote a small tutorial on how to add Route-Me into your project.

Basically you can use any maps feed you may want (OpenStreetMaps is one of them and also the one we used). What you'll have to do is:

  1. Download the map tiles ( https://metacpan.org/pod/Geo::OSM::Tiles )
  2. Put them into a SQLite database ( http://shikii.net/blog/downloads/map2sqlite-bin.zip )
  3. Modify Route-Me for the tiles to be fed from the database instead of from OpenStreetMaps website

Другие советы

You can test also: skobbler's/Telenav: http://developer.skobbler.com/support#download it's based on OpenStreetMap and they got full offline capabilities (GPX tracks navigation, map rendering, routing & rerouting)

There is also Nutiteq Maps SDK: http://developer.nutiteq.com , offline maps for iOS and Android, supports Xamarin IDE (C#) and native languages (Java, ObjectiveC, Swift). It is not so much routing and navigation (as e.g. Skobbler), but more for focused to complex maps with interactive layers (points, lines and polygons on top of maps). One advantage is that you can use your own base map sources (in-house, 3rd parties), not only OpenStreetMap what SDK itself provides.

Disclaimer: I'm developer of it.

I used default map from MapKit and a subclass of MKTileOverlay to be able to save downloaded tiles and return already cached tiles without downloading them.

1) Change source for your default map from MapKit and use a subclass of MKTileOverlay (Used "open street map" here)

- (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) subclass from 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];
}

Uncomment "saveFileWithName" and run it on simulator. You can also add NSLog(fileName) to know where to get all tiles you need. (Simulator cache is in Users/YOU/Library/Developer/CoreSimulator/Devices/... And Library is a hidden directory)

After you cached all you need just put in your app's bundle (just like an any other image, if you want from box cached map). And tell your

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

to get tiles from bundle.

So now I can install my app, turn wi-fi off and I'll get those maps anyway.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top