Desenhando uma rota no MapKit no iPhone SDK

Eu quero desenhar uma rota entre dois locais no mapa. Algo como um guia turístico. Quando o turista clica em outro local, eu quero ser capaz de desenhar uma rota; bem como, informar sobre a distância da localização atual.

Estou ciente de sites na Internet que dizem como desenhar polilinhas no mapa. Mas, a maioria dos exemplos tinha um arquivo .csv pré-carregado com várias coordenadas.

Existe uma maneira alternativa de obter as coordenadas do Google ou de qualquer outro provedor, pois o local é selecionado dinamicamente.

Se NÃO, como obtenho as informações para coordenadas intermediárias?

O iOS 6 fornece alguma maneira direta para esse problema?

Isso é complicado. Não há como fazer isso com o MapKit: é fácil desenhar linhas quando você conhece as coordenadas, mas o MapKit não lhe dará access às estradas ou a outras informações de roteamento. Eu diria que você precisa chamar uma API externa para obter seus dados.

Eu tenho jogado com a API cloudmade.com. O servidor de stream vetorial deve retornar o que você precisa e, em seguida, você pode desenhar isso no seu mapa. No entanto, as discrepâncias entre os mapas do Google e os mapas do OSM usados ​​pela nuvem podem fazer com que você deseje usar os mapas da nuvem até o fim: eles têm um equivalente ao MapKit.

PS: Outros provedores de mapeamento – Google, Bing, etc. também podem fornecer feeds de dados equivalentes. Acabei de ver o OSM / Cloudmade recentemente.

PPS: Nada disso é novidade trivial! Boa sorte!

O viewDidLoad seguir irá (1) definir dois locais, (2) remover todas as annotations anteriores e (3) chamar funções auxiliares definidas pelo usuário (para obter pontos de rota e desenhar a rota).

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

Segue-se o método MKMapViewDelegate , que desenha sobreposição (iOS 4.0 e posterior).

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

A function a seguir obterá os dois locais e preparará o URL para obter todos os pontos de rota. E, claro, vai chamar 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]]; } 

O código a seguir é a mágica real (decodificador da resposta que recebemos da API). Eu não iria modificar esse código a menos que eu saiba o que estou fazendo 🙂

 - (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; } 

Esta function irá desenhar uma rota e irá adicionar uma sobreposição.

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

O código a seguir centralizará o alinhamento do mapa.

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

Espero que isso ajude alguém.

E tem tudo certo. O MapKit não permitirá que você faça isso. Infelizmente, o Google também não permite que você faça o que deseja.

Quando a Apple anunciou o MapKit e todos eles, eles também declararam explicitamente que qualquer aplicativo de navegação seria BYOM: Traga seus próprios mapas, de modo que qualquer aplicativo de navegação use seu próprio conjunto de ferramentas de mapeamento.

Os Termos de Serviço do Google impedem que você exiba rotas em seus mapas:

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

Restrições de Licença:

10.9 usar o Serviço ou Conteúdo com quaisquer produtos, sistemas ou aplicativos para ou em conexão com:

(a) navegação em tempo real ou orientação de rota, incluindo, mas não limitado a, orientação de rota curva a curva que é sincronizada com a posição do dispositivo ativado por sensor de um usuário;

(b) quaisquer sistemas ou funções para controlo automático ou autónomo do comportamento do veículo; ou

(c) expedição, gerenciamento de frota, rastreamento de ativos comerciais ou aplicativos corporativos semelhantes (a API do Google Maps pode ser usada para rastrear ativos (como carros, ônibus ou outros veículos), desde que o aplicativo de rastreamento seja disponibilizado ao público sem Por exemplo, você pode oferecer uma Implementação pública e gratuita da API do Google Maps que exibe informações de trânsito público em tempo real ou outras informações sobre o status do transporte.

Infelizmente, isso inclui o que você gostaria de fazer. Espero que um dia o MapKit seja expandido para permitir tais resources … embora improvável.

Boa sorte.

Você pode querer dar uma olhada em https://github.com/leviathan/nvpolyline Esta solução é especialmente voltada para as versões do iPhone OS anteriores à v.4.0

Embora também possa ser usado na v.4.0 Hope isso ajuda.

Experimente o MapKit-Route-Directions ( GitHub ).

O MapQuest tem um SDK que substitui o MapKit. Está atualmente em beta, mas em desenvolvimento ativo.

Permite sobreposições, roteamento e geocodificação.

API do Google Maps do MapQuest para iOS

Só para esclarecer, parece que há algumas coisas sendo discutidas. Uma é uma maneira de obter os vértices de uma rota e a outra é desenhar uma sobreposição no mapa usando esses vértices. Eu conheço as APIs do MapQuest, então tenho alguns links para os que estão abaixo – Google e Bing têm equivalentes, eu acho.

1) Obtendo vértices de uma rota
Se você estiver procurando as novas coordenadas de uma rota para desenhar uma sobreposição de rota, poderá usar uma chamada de serviço da Web para um serviço da Web de roteamento. Suponho que esteja usando JavaScript aqui para exibir o mapa. Se você estiver usando código nativo, ainda poderá acessar um serviço da web ou usar uma chamada nativa (ou seja, o MapQuest iPhone SDK tem uma chamada de rota nativa).

A maioria dos serviços de rotas deve retornar os “pontos de controle” da rota para que você possa desenhar.

Aqui estão alguns exemplos usando o serviço da Web MapQuest-Directions para obter shapepoints (consulte o object de retorno Shape) – http://www.mapquestapi.com/directions/

2) Desenhando uma sobreposição
Depois de ter seus vértices, você precisa desenhá-los. Eu acho que a maioria das APIs de mapas JavaScript terá uma class de sobreposição de algum tipo. Aqui está o MapQuest: http://developer.mapquest.com/web/documentation/sdk/javascript/v7.0/overlays#line

3) Fazendo isso com uma chamada
O MapQuest também tem algumas funções de conveniência para fazer a chamada de rota e desenhar a linha para você – não posso postar mais de dois links! Então vá para o link acima e procure por “roteamento” na barra de navegação à esquerda.

Obter e desenhar uma rota no mapa é super fácil com a API do 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]; } }]; 

Para atualizar esta questão, não há necessidade de apk externo desde o iOS7.

Aqui uma solução muito simples e eficaz:

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

Eu sei que a pergunta era sobre o iOS 6, mas acredito que essa solução será útil para muitas pessoas.

A única coisa que falta nesta solução é implementar o seguinte método delegado para exibir o pino inicial e final

 -(MKAnnotationView *) mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation