11 Set, 2013 13:15

Utilizando o Google Maps no seu Aplicativo iOS - Parte 3

Baixando a rota da API do Google Maps

Infelizmente, até o momento, o Google Maps SDK do iOS não possui a função de calcular rotas embutida. Para realizar essa tarefa, precisamos recorrer ao Serviços da Web da API do Google Maps que contem, entre outras, a função desejada.

Para simplificar, assumirei que você já possui dois pontos entre os quais você deseja traçar a rota. Esse pontos podem ser obtidos de qualquer modo: pegando a posição do usuário, tocando no mapa, etc.

- (void)tracarRotaDe:(CLLocationCoordinate2D)origem para:(CLLocationCoordinate2D)destino {
    NSString* origemString = [NSString stringWithFormat:@"%f,%f", origem.latitude, origem.longitude];
    NSString* destinoString = [NSString stringWithFormat:@"%f,%f", destino.latitude, destino.longitude];

    // 1
    NSString* apiUrlStr = [NSString stringWithFormat:@"https://maps.googleapis.com/maps/api/directions/json?origin=%@&destination=%@&sensor=true", origemString, destinoString];
    NSURL* apiUrl = [NSURL URLWithString:apiUrlStr];

    // 2
    [self performSelectorInBackground:@selector(baixarRota:) withObject:apiUrl]; 
}

O método acima, monta uma url de acordo com as especificações do serviço de rotas (1). Você pode customizar a url para traçar rotas de carro (padrão), a pé, ou com várias outras opções.

Como é necessário conectar ao serviço (2), a conexão não pode ser feita na Main Thread para não bloquear a interface e então chamamos assincronamente o método a seguir.

- (void)baixarRota:(NSURL *)apiUrl {
    NSData *apiResponse = [NSData dataWithContentsOfURL:apiUrl];

    // 1
    NSDictionary *json = [NSJSONSerialization JSONObjectWithData:apiResponse options:0 error:nil];

    NSString *encodedPoints = nil;

    if ([json[@"routes"] count] > 0) {
        // 2
        encodedPoints = json[@"routes"][0][@"overview_polyline"][@"points"];
    }

    if ([encodedPoints length] > 0) {
        @try {
            // 3
            GMSPath *path = [GMSPath pathFromEncodedPath:encodedPoints]; 

            // 4
            [self performSelectorOnMainThread:@selector(atualizarRota:) withObject:path waitUntilDone:YES]; 
            return;
        }
        @catch (NSException *exception) {
            NSLog(@"Error calculating path from encoded points: %@", exception);
        }
    }

    rota = nil;
    [self performSelectorOnMainThread:@selector(rotaNaoEncontrada) withObject:nil waitUntilDone:YES];
}

A chamada ao serviço retorna um JSON (1) com vários detalhes sobre a rota que você pode usar caso deseje mostrar um passo-a-passo para o usuário.

No nosso caso, vamos mostrar apenas a rota completa traçada no mapa. Para isso, o JSON contem a rota codificada sob o atributo overview_polyline ->points (2). O SDK possui uma classe, GMSPath, que sabe como decodificar essa string com o método pathFromEncodedPath: (3).

Como desenhar a rota é uma operação de interface, deve ser executada na Main Thread (4). Então, chamamos finalmente o método abaixo.

- (void)atualizarRota:(GMSPath *)path {
    // *
    [self eliminarRota];

    // 1
    rota = [GMSPolyline polylineWithPath:path];

    // 2
    rota.strokeWidth = 8;
    rota.strokeColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.7]; 

    if (rota) {
        // 3
        rota.map = self.mapView;

        // 4
        GMSCameraUpdate *update = [GMSCameraUpdate fitBounds:[[GMSCoordinateBounds alloc] initWithPath:rota.path]
                                                 withPadding:50.0f];
        [self.mapView animateWithCameraUpdate:update];
    }
}

Com o GMSPath, podemos montar uma GMSPolyline (1) que é a classe que representa um caminho a ser desenhado no mapa. Você pode customizar, entre outros atributos, a espessura e a cor da linha conforme a sua necessidade (2). Depois, basta adicionar a rota no mapa (3) e, caso queira, realizar um GMSCameraUpdate para enquadrar a rota na tela (4).

A menos que sua intenção seja sobrepor várias rotas, é saudável remover a rota atual antes de adicionar uma nova (*). Felizmente, é muito fácil fazer isso:

- (void)eliminarRota {
    rota.map = nil;
    rota = nil;
}

Basta "desconectar" a rota do mapa e depois eliminá-la (Neste exemplo eu usei o ARC, por isso não há nenhum release nos código acima).

Ainda existem outros recursos bem legais do SDK, fiquem ligados nos próximos posts.

Ao navegar neste site, você consente o uso de cookies nossos e de terceiros, que coletam informações anônimas e são essenciais para melhorar sua experiência em nosso site.