web-dev-qa-db-fra.com

Dessiner un itinéraire dans MapKit dans iOS

Je souhaite tracer un itinéraire entre deux emplacements sur la carte. Quelque chose comme un guide touristique. Lorsque le touriste clique sur un autre endroit, je veux pouvoir tracer un itinéraire; ainsi que, informez-vous de la distance de l'emplacement actuel.

Je connais des sites sur Internet qui indiquent comment dessiner des polylignes sur la carte. Mais, la plupart des exemples avaient un fichier .csv préchargé avec différentes coordonnées.

Existe-t-il un autre moyen d'obtenir les coordonnées de Google ou de tout autre fournisseur, car l'emplacement est sélectionné dynamiquement.

Si NON, comment puis-je obtenir les informations pour les coordonnées intermédiaires?

IOS 6 fournit-il un moyen direct de résoudre ce problème?

54
PRN

Ceci est délicat. Il n'y a aucun moyen de le faire avec MapKit: il est assez facile de tracer des lignes lorsque vous connaissez les coordonnées, mais MapKit ne vous donnera pas accès aux routes ou à d'autres informations de routage. Je dirais que vous devez appeler une API externe pour obtenir vos données.

J'ai joué avec l'API cloudmade.com. Le serveur de flux vectoriel doit retourner ce dont vous avez besoin, puis vous pouvez le dessiner sur votre carte. Cependant, les divergences entre les cartes Google et les cartes OSM utilisées par cloudmade peuvent vous donner envie d'utiliser des cartes cloudmade à fond: elles ont un équivalent à MapKit.

P.S .: D'autres fournisseurs de cartographie - Google, Bing, etc. peuvent également fournir des flux de données équivalents. Je viens de regarder OSM/Cloudmade récemment.

P.P.S .: Rien de tout cela n'est un truc de débutant trivial! Bonne chance!

24
Andiih

Le viewDidLoad suivant (1) définira deux emplacements, (2) supprimera toutes les annotations précédentes et (3) appellera des fonctions d'assistance définies par l'utilisateur (pour obtenir des points de route et tracer la route).

-(void)viewDidLoad
{
    [super viewDidLoad];

    // Origin Location.
    CLLocationCoordinate2D loc1;
    loc1.latitude = 29.0167;
    loc1.longitude = 77.3833;
    Annotation *Origin = [[Annotation alloc] initWithTitle:@"loc1" subTitle:@"Home1" andCoordinate:loc1];
    [objMapView addAnnotation:Origin];

    // Destination Location.
    CLLocationCoordinate2D loc2;
    loc2.latitude = 19.076000;
    loc2.longitude = 72.877670;
    Annotation *destination = [[Annotation alloc] initWithTitle:@"loc2" subTitle:@"Home2" andCoordinate:loc2];
    [objMapView addAnnotation:destination];

    if(arrRoutePoints) // Remove all annotations
        [objMapView removeAnnotations:[objMapView annotations]];

    arrRoutePoints = [self getRoutePointFrom:Origin to:destination];
    [self drawRoute];
    [self centerMap];
}

Voici la méthode MKMapViewDelegate, qui dessine la superposition (iOS 4.0 et versions ultérieures).

/* MKMapViewDelegate Meth0d -- for viewForOverlay*/
- (MKOverlayView*)mapView:(MKMapView*)theMapView viewForOverlay:(id <MKOverlay>)overlay
{
    MKPolylineView *view = [[MKPolylineView alloc] initWithPolyline:objPolyline];
    view.fillColor = [UIColor blackColor];
    view.strokeColor = [UIColor blackColor];
    view.lineWidth = 4;
    return view;
}

La fonction suivante obtiendra les deux emplacements et préparera l'URL pour obtenir tous les points de l'itinéraire. Et bien sûr, appellera stringWithURL.

/* This will get the route coordinates from the Google API. */
- (NSArray*)getRoutePointFrom:(Annotation *)Origin to:(Annotation *)destination
{
    NSString* saddr = [NSString stringWithFormat:@"%f,%f", Origin.coordinate.latitude, Origin.coordinate.longitude];
    NSString* daddr = [NSString stringWithFormat:@"%f,%f", destination.coordinate.latitude, destination.coordinate.longitude];

    NSString* apiUrlStr = [NSString stringWithFormat:@"http://maps.google.com/maps?output=dragdir&saddr=%@&daddr=%@", saddr, daddr];
    NSURL* apiUrl = [NSURL URLWithString:apiUrlStr];

    NSError *error;
    NSString *apiResponse = [NSString stringWithContentsOfURL:apiUrl encoding:NSUTF8StringEncoding error:&error];
    NSString* encodedPoints = [apiResponse stringByMatching:@"points:\\\"([^\\\"]*)\\\"" capture:1L];

    return [self decodePolyLine:[encodedPoints mutableCopy]];
}

Le code suivant est la vraie magie (décodeur pour la réponse que nous avons obtenue de l'API). Je ne modifierais pas ce code à moins de savoir ce que je fais :)

- (NSMutableArray *)decodePolyLine:(NSMutableString *)encodedString
{
    [encodedString replaceOccurrencesOfString:@"\\\\" withString:@"\\"
                                  options:NSLiteralSearch
                                    range:NSMakeRange(0, [encodedString length])];
    NSInteger len = [encodedString length];
    NSInteger index = 0;
    NSMutableArray *array = [[NSMutableArray alloc] init];
    NSInteger lat=0;
    NSInteger lng=0;
    while (index < len) {
        NSInteger b;
        NSInteger shift = 0;
        NSInteger result = 0;
        do {
            b = [encodedString characterAtIndex:index++] - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
        } while (b >= 0x20);
        NSInteger dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
        lat += dlat;
        shift = 0;
        result = 0;
        do {
            b = [encodedString characterAtIndex:index++] - 63;
            result |= (b & 0x1f) << shift;
            shift += 5;
       } while (b >= 0x20);
        NSInteger dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
        lng += dlng;
        NSNumber *latitude = [[NSNumber alloc] initWithFloat:lat * 1e-5];
        NSNumber *longitude = [[NSNumber alloc] initWithFloat:lng * 1e-5];
        printf("\n[%f,", [latitude doubleValue]);
        printf("%f]", [longitude doubleValue]);
        CLLocation *loc = [[CLLocation alloc] initWithLatitude:[latitude floatValue] longitude:[longitude floatValue]];
        [array addObject:loc];
    }
    return array;
}

Cette fonction dessinera un itinéraire et ajoutera une superposition.

- (void)drawRoute
{
    int numPoints = [arrRoutePoints count];
    if (numPoints > 1)
    {
        CLLocationCoordinate2D* coords = malloc(numPoints * sizeof(CLLocationCoordinate2D));
        for (int i = 0; i < numPoints; i++)
        {
            CLLocation* current = [arrRoutePoints objectAtIndex:i];
            coords[i] = current.coordinate;
        }

        self.objPolyline = [MKPolyline polylineWithCoordinates:coords count:numPoints];
        free(coords);

        [objMapView addOverlay:objPolyline];
        [objMapView setNeedsDisplay];
    }
}

Le code suivant alignera la carte au centre.

- (void)centerMap
{
    MKCoordinateRegion region;

    CLLocationDegrees maxLat = -90;
    CLLocationDegrees maxLon = -180;
    CLLocationDegrees minLat = 90;
    CLLocationDegrees minLon = 180;

    for(int idx = 0; idx < arrRoutePoints.count; idx++)
    {
        CLLocation* currentLocation = [arrRoutePoints objectAtIndex:idx];

        if(currentLocation.coordinate.latitude > maxLat)
            maxLat = currentLocation.coordinate.latitude;
        if(currentLocation.coordinate.latitude < minLat)
            minLat = currentLocation.coordinate.latitude;
        if(currentLocation.coordinate.longitude > maxLon)
            maxLon = currentLocation.coordinate.longitude;
        if(currentLocation.coordinate.longitude < minLon)
            minLon = currentLocation.coordinate.longitude;
    }

    region.center.latitude     = (maxLat + minLat) / 2;
    region.center.longitude    = (maxLon + minLon) / 2;
    region.span.latitudeDelta  = maxLat - minLat;
    region.span.longitudeDelta = maxLon - minLon;

    [objMapView setRegion:region animated:YES];
}

J'espère que cela aiderait quelqu'un.

61
viral

Andiih a raison. MapKit ne vous laissera pas faire cela. Malheureusement, Google ne vous laissera pas faire ce que vous voulez faire non plus.

Lorsque Apple a annoncé MapKit et tous, ils ont également déclaré explicitement que toutes les applications de navigation seraient BYOM: Bring Your Own Maps, donc toute application de navigation utilise son propre ensemble d'outils de cartographie.

Les conditions d'utilisation de Google vous empêchent même d'afficher des itinéraires au-dessus de leurs cartes:

http://code.google.com/intl/de/apis/maps/iphone/terms.html

Restrictions de licence:

10.9 utiliser le Service ou le Contenu avec des produits, systèmes ou applications pour ou en relation avec:

(a) navigation en temps réel ou guidage d'itinéraire, y compris, mais sans s'y limiter, le guidage d'itinéraire virage par virage qui est synchronisé avec la position du dispositif activé par capteur d'un utilisateur;

b) tout système ou fonction de contrôle automatique ou autonome du comportement du véhicule; ou

(c) répartition, gestion de flotte, suivi des actifs commerciaux ou applications d'entreprise similaires (l'API Google Maps peut être utilisée pour suivre les actifs (tels que les voitures, les bus ou d'autres véhicules) tant que l'application de suivi est mise à la disposition du public sans Par exemple, vous pouvez proposer une implémentation gratuite et publique de l'API Maps qui affiche les transports en commun en temps réel ou d'autres informations sur l'état du transport.

Malheureusement, cela comprend ce que vous aimeriez faire. Espérons qu'un jour MapKit sera étendu pour permettre de telles fonctionnalités ... bien que peu probable.

Bonne chance.

12
Daniel Amitay

Vous voudrez peut-être jeter un œil à https://github.com/leviathan/nvpolyline Cette solution est spécialement destinée aux versions d'iPhone OS antérieures à la v.4.0

Bien qu'il puisse également être utilisé dans la version 4.0, j'espère que cela vous aidera.

5
leviathan
4
iOS_User

Obtenir et dessiner un itinéraire sur la carte est super facile avec l'API iOS 7:

MKDirectionsRequest *directionsRequest = [[MKDirectionsRequest alloc] init];

// Set the Origin of the route to be current user location
[directionsRequest setSource:[MKMapItem mapItemForCurrentLocation]];

// Set the destination point of the route
CLLocationCoordinate2D destinationCoordinate = CLLocationCoordinate2DMake(34.0872, 76.235);
MKPlacemark *destinationPlacemark = [[MKPlacemark alloc] initWithCoordinate:destinationCoordinate addressDictionary:nil];
[directionsRequest setDestination:[[MKMapItem alloc] initWithPlacemark:destinationPlacemark]];

MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];

// Requesting route information from Apple Map services
[directions calculateDirectionsWithCompletionHandler:^(MKDirectionsResponse *response, NSError *error) {
    if (error) {
        NSLog(@"Cannot calculate directions: %@",[error localizedDescription]);
    } else {
        // Displaying the route on the map
        MKRoute *route = [response.routes firstObject];
        [mapView addOverlay:route.polyline];
    }
}];
4
NAlexN

MapQuest possède un SDK qui remplace directement MapKit. Il est actuellement en version bêta, mais en cours de développement actif.

Il permet les superpositions, le routage et le géocodage.

MapQuest iOS Maps API

3
Fabian

Juste pour clarifier, il semble qu'il y ait deux ou trois choses en cours de discussion. L'une est un moyen d'obtenir les sommets d'un itinéraire et l'autre est de dessiner une superposition sur la carte en utilisant ces sommets. Je connais les API MapQuest, j'ai donc quelques liens pour ceux ci-dessous - Google et Bing ont des équivalents je pense.

1) Obtenir les sommets d'un itinéraire
Si vous recherchez les nouvelles coordonnées d'un itinéraire pour tracer une superposition d'itinéraire, vous pouvez soit utiliser un appel de service Web vers un service Web de routage - je suppose que vous utilisez JavaScript ici pour afficher le carte. Si vous utilisez du code natif, vous pouvez toujours accéder à un service Web ou utiliser un appel natif (c'est-à-dire que le SDK MapQuest iPhone contient un appel de route natif).

La plupart des services de directions doivent renvoyer les "points de forme" de l'itinéraire afin que vous puissiez dessiner.

Voici quelques exemples d'utilisation du service Web MapQuest-Directions pour obtenir des points de forme (voir l'objet de retour de forme) - http://www.mapquestapi.com/directions/

2) Dessiner une superposition
Une fois que vous avez vos sommets, vous devez les dessiner. Je pense que la plupart des API de carte JavaScript auront une classe de superposition. Voici celui de MapQuest: http://developer.mapquest.com/web/documentation/sdk/javascript/v7.0/overlays#line

3) Le faire avec un seul appel
MapQuest a également quelques fonctions pratiques pour faire l'appel d'itinéraire et tracer la ligne pour vous - je ne peux pas poster plus de deux liens! Allez donc sur le lien ci-dessus et cherchez "routage" dans la barre de navigation à gauche.

3
Roman

Pour mettre à jour cette question, il n'est pas nécessaire d'apk externe depuis iOS7.

Voici une solution très simple et efficace:

http://technet.weblineindia.com/mobile/draw-route-between-2-points-on-map-with-ios7-mapkit-api/2/

Je sais que la question concernait iOS 6, mais je pense que cette solution sera utile pour de nombreuses personnes.

La seule chose qui manque dans cette solution est l'implémentation de la méthode déléguée suivante pour afficher la broche de début et de fin

-(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
1
Omaty