Objective-C: Draw route using Google Map API

Screen Shot 2017-04-24 at 23.43.20

In this post we will try to get current geolocation and draw route on Google map. It is interesting that we need to use one API to show a map and another API to get coordinates of lines of route.

Step 1.

First of all we need to get API key. You can create project and get API key for map on this page: Google console.

Screen Shot 2017-04-24 at 11.14.26

Screen Shot 2017-04-24 at 11.15.40

 

You need to get API key as string that contains numbers and latin letters. My key length was 39 symbols.

Step 2.

Create project. Add permission in Info.plist to get location while app is active.

Screen Shot 2017-04-25 at 15.15.08

Step 3.

Install GoogleMaps pod.

Screen Shot 2017-04-25 at 15.33.50

Post: How to install pods?

Step 4.

UILabel *mapStatusLabel;
GMSMapView *mapView;
UITextField *destinationField;
CLLocationManager *locationManager;
CLLocationCoordinate2D coordinate;

- (void)viewDidLoad {
    [super viewDidLoad];
    [self buildView];
}

- (void) buildView {
    CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
    
    UIView *containerView = [[UIView alloc] initWithFrame:CGRectMake(
                                                                          0,
                                                                          0,
                                                                          screenWidth,
                                                                          550)];
    containerView.layer.borderColor = [UIColor grayColor].CGColor;
    containerView.layer.borderWidth = 1.0f;
    [self.view addSubview:containerView];
    
    CGFloat currentY = 20;
    
    mapStatusLabel = [[UILabel alloc] initWithFrame:CGRectMake(
                                                                 30,
                                                                 currentY,
                                                                 containerView.frame.size.width - 60,
                                                                 20)];
    mapStatusLabel.text = @"Let me determine your location";
    mapStatusLabel.textAlignment = NSTextAlignmentCenter;
    [containerView addSubview:mapStatusLabel];
    currentY += mapStatusLabel.frame.size.height + 10;
    
    UIButton *getMyLocationButton = [[UIButton alloc] initWithFrame:CGRectMake(
                                                                             (screenWidth - 200)/2,
                                                                             currentY,
                                                                             200,
                                                                             30)];
    [getMyLocationButton setTitle:@"Get my location" forState:UIControlStateNormal];
    getMyLocationButton.showsTouchWhenHighlighted = true;
    getMyLocationButton.backgroundColor = [UIColor grayColor];
    [getMyLocationButton addTarget:self action:@selector(getMyLocation:) forControlEvents:UIControlEventTouchUpInside];
    [containerView addSubview:getMyLocationButton];
    currentY += getMyLocationButton.frame.size.height + 10;
    
    destinationField = [[UITextField alloc] initWithFrame:CGRectMake(
                                                                                  (screenWidth - 200)/2,
                                                                                  currentY,
                                                                                  200,
                                                                                  30)];
    destinationField.layer.borderColor = [UIColor grayColor].CGColor;
    destinationField.layer.borderWidth = 1.0f;
    [containerView addSubview:destinationField];
    currentY += destinationField.frame.size.height + 10;
    
    UIButton *drawRoute = [[UIButton alloc] initWithFrame:CGRectMake(
                                                                     (screenWidth - 200)/2,
                                                                     currentY,
                                                                     200,
                                                                     30)];
    [drawRoute setTitle:@"Draw route destination" forState:UIControlStateNormal];
    drawRoute.showsTouchWhenHighlighted = true;
    drawRoute.backgroundColor = [UIColor grayColor];
    [drawRoute addTarget:self action:@selector(drawRoute:) forControlEvents:UIControlEventTouchUpInside];
    [containerView addSubview:drawRoute];
    currentY += drawRoute.frame.size.height + 10;
   
    [GMSServices provideAPIKey:@"[API_KEY]"];
    
    mapView = [[GMSMapView alloc] initWithFrame:CGRectMake(
                                                              10,
                                                              currentY,
                                                              containerView.frame.size.width - 20,
                                                              300)];
    mapView.backgroundColor = [UIColor grayColor];
    [containerView addSubview:mapView];
}

You should get similar view:

Screen Shot 2017-04-24 at 23.43.06

Step 5.

Get user location.

- (void) getMyLocation:(UIButton *) sender {
    NSLog(@"getMyLocation");
    
    mapStatusLabel.text = @"Determining location";
    
    locationManager = [[CLLocationManager alloc] init];
    locationManager.delegate = self;
    locationManager.distanceFilter = kCLDistanceFilterNone;
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters;
    [locationManager requestWhenInUseAuthorization];
    [locationManager startUpdatingLocation];
}

- (void) locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    NSLog(@"didUpdateLocations");

    mapStatusLabel.text = @"Your location found";
    coordinate = locations.lastObject.coordinate;
    
    GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:coordinate.latitude longitude:coordinate.longitude zoom:4];
    mapView.camera = camera;
    
    [locationManager stopUpdatingLocation];
}

When “Get my location” button pressed we run location manager, so we periodically get current location in method didUpdateLocations. I want get location only once, so after receiving coordinate I stopped location manager.

At receiving coordinate we set camera to this place.

Step 6.

At drawing route we use another API called Google Directions API. In documentation of this API we see that we need to use API key, but it works without key (April 2017). Anyway lets add key into request.

- (void) drawRoute:(UIButton *) sender {
    NSLog(@"drawRoute");
    
    mapStatusLabel.text = @"Drawing route";
    [mapView clear];
   
    [self fetchPolylineWithDestination:destinationField.text];
}

- (void)fetchPolylineWithDestination:(NSString *)destinationString
{
    NSString *originString = [NSString stringWithFormat:@"%f,%f", coordinate.latitude, coordinate.longitude];
    NSString *destinationEncodedString = [destinationString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    NSString *directionsAPI = @"https://maps.googleapis.com/maps/api/directions/json?";
    NSString *directionsUrlString = [NSString stringWithFormat:@"%@&origin=%@&destination=%@&mode=driving&key=[API_KEY]", directionsAPI, originString, destinationEncodedString];
    NSURL *directionsUrl = [NSURL URLWithString:directionsUrlString];
    
    NSURLSessionDataTask *fetchDirectionsTask = [[NSURLSession sharedSession] dataTaskWithURL:directionsUrl completionHandler:
        ^(NSData *data, NSURLResponse *response, NSError *error) {
            NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
            
            dispatch_sync(dispatch_get_main_queue(), ^{
                if (error) {
                    mapStatusLabel.text = @"Drawing route failed";
                    NSLog(@"error: %@", error);
                    return;
                }
                
                NSArray *routesArray = [json objectForKey:@"routes"];
                
                GMSPolyline *polyline = nil;
                if ([routesArray count] > 0) {
                    NSDictionary *routeDict = [routesArray objectAtIndex:0];
                    NSDictionary *routeOverviewPolyline = [routeDict objectForKey:@"overview_polyline"];
                    NSString *points = [routeOverviewPolyline objectForKey:@"points"];
                    GMSPath *path = [GMSPath pathFromEncodedPath:points];
                    polyline = [GMSPolyline polylineWithPath:path];
                }
                
                if(polyline)
                    polyline.map = mapView;
                mapStatusLabel.text = @"Drawing route completed";
                
            });
        }];
    [fetchDirectionsTask resume];
}

In fetchPolylineWithDestination we make request to API and get route’s lines coordinates in JSON format. Origin point is our current location and destination is place that entered in text field. I use method that was written by Tarek on stackoverflow. I made some modifications to it.

Step 7.

The result:

Screen Shot 2017-04-24 at 23.43.20

Screen Shot 2017-04-24 at 23.42.23

Project on GitHub.

If you have found a spelling error, please, notify us by selecting that text and pressing Ctrl+Enter.

Profile photo of Doszhan Kalibek

Doszhan Kalibek