34

I am Developing an iOS Application. In that Application i am having 2 Fields From and To. I Entered Address using Google Auto Complete API.and also i am able to Getting the Latitude and Longitude of the 2 places and able to show markers on the GMSMapView.

Now i Want to Draw Route Between these 2 Places. I found a solution when we use MKMapView. But i was Unable to find the solution for GMSMapView. please help me to Draw the route between these 2 points in GMSMapView.

If possible please give me some important links for this.

Thanks.

Soumya Ranjan
  • 4,817
  • 2
  • 26
  • 51
Suresh Peddisetti
  • 3,782
  • 3
  • 23
  • 26

12 Answers12

37
`first get all points coordinates which are coming in route then add these points latitude and longitude in path in will draw path according to that`


GMSCameraPosition *cameraPosition=[GMSCameraPosition cameraWithLatitude:18.5203 longitude:73.8567 zoom:12];
_mapView =[GMSMapView mapWithFrame:CGRectZero camera:cameraPosition];
_mapView.myLocationEnabled=YES;
GMSMarker *marker=[[GMSMarker alloc]init];
marker.position=CLLocationCoordinate2DMake(18.5203, 73.8567);
marker.icon=[UIImage imageNamed:@"aaa.png"] ;
marker.groundAnchor=CGPointMake(0.5,0.5);
marker.map=_mapView;
GMSMutablePath *path = [GMSMutablePath path];   
[path addCoordinate:CLLocationCoordinate2DMake(@(18.520).doubleValue,@(73.856).doubleValue)];
[path addCoordinate:CLLocationCoordinate2DMake(@(16.7).doubleValue,@(73.8567).doubleValue)];

GMSPolyline *rectangle = [GMSPolyline polylineWithPath:path];
rectangle.strokeWidth = 2.f;
rectangle.map = _mapView;
self.view=_mapView;
johny kumar
  • 1,270
  • 2
  • 14
  • 24
29

I've written the following code which should do the trick for you:

- (void)drawRoute
{
    [self fetchPolylineWithOrigin:myOrigin destination:myDestination completionHandler:^(GMSPolyline *polyline)
     {
         if(polyline)
             polyline.map = self.myMap;
     }];
}

- (void)fetchPolylineWithOrigin:(CLLocation *)origin destination:(CLLocation *)destination completionHandler:(void (^)(GMSPolyline *))completionHandler
{
    NSString *originString = [NSString stringWithFormat:@"%f,%f", origin.coordinate.latitude, origin.coordinate.longitude];
    NSString *destinationString = [NSString stringWithFormat:@"%f,%f", destination.coordinate.latitude, destination.coordinate.longitude];
    NSString *directionsAPI = @"https://maps.googleapis.com/maps/api/directions/json?";
    NSString *directionsUrlString = [NSString stringWithFormat:@"%@&origin=%@&destination=%@&mode=driving", directionsAPI, originString, destinationString];
    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];
             if(error)
             {
                 if(completionHandler)
                     completionHandler(nil);
                 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];
             }

             // run completionHandler on main thread                                           
             dispatch_sync(dispatch_get_main_queue(), ^{
                 if(completionHandler)
                      completionHandler(polyline);
             });
         }];
    [fetchDirectionsTask resume];
}
Tarek
  • 2,372
  • 2
  • 23
  • 35
  • 4
    Can we get the Swift Version for this? – Bista Jun 07 '16 at 07:36
  • 2
    @ Tarek ,Be careful ==> Google Maps iOA SDK requires that all drawing events be done on the main thread. So, for your second method, you have to do put all your maker setup code inside the dispatch_get_main_queue() closure. Else be ready to crash your sweet app. – Ravi Feb 28 '17 at 21:01
  • 1
    @Monusingh you're absolutely right! I just modified the code so that the completion handler runs on the main thread. Thank you! – Tarek Mar 02 '17 at 00:04
  • 1
    Hi i'm getting crash on the line `polyline = [GMSPolyline polylineWithPath:path];` of **All calls to the Google Maps SDK for iOS must be made from the UI thread** – Mukesh Lokare Apr 20 '17 at 08:58
  • I converted this code into swift but it's getting error here, if completionHandler { completionHandler(nil) }. The error is : '(GMSPolyline?) -> Void' is not convertible to 'Bool' – Naresh Sep 04 '18 at 12:47
19

For swift 3 to draw polyline

func getPolylineRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D){

        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)

        let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)&sensor=true&mode=driving&key=YOURKEY")!

        let task = session.dataTask(with: url, completionHandler: {
            (data, response, error) in
            if error != nil {
                print(error!.localizedDescription)
                self.activityIndicator.stopAnimating()
            }
            else {
                do {
                    if let json : [String:Any] = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]{

                        guard let routes = json["routes"] as? NSArray else {
                            DispatchQueue.main.async {
                                self.activityIndicator.stopAnimating()
                            }
                            return
                        }

                        if (routes.count > 0) {
                            let overview_polyline = routes[0] as? NSDictionary
                            let dictPolyline = overview_polyline?["overview_polyline"] as? NSDictionary

                            let points = dictPolyline?.object(forKey: "points") as? String

                            self.showPath(polyStr: points!)

                            DispatchQueue.main.async {
                                self.activityIndicator.stopAnimating()

                                let bounds = GMSCoordinateBounds(coordinate: source, coordinate: destination)
                                let update = GMSCameraUpdate.fit(bounds, with: UIEdgeInsetsMake(170, 30, 30, 30))
                                self.mapView!.moveCamera(update)
                            }
                        }
                        else {
                            DispatchQueue.main.async {
                                self.activityIndicator.stopAnimating()
                            }
                        }
                    }
                }
                catch {
                    print("error in JSONSerialization")
                    DispatchQueue.main.async {
                        self.activityIndicator.stopAnimating()
                    }
                }
            }
        })
        task.resume()
    }

    func showPath(polyStr :String){
        let path = GMSPath(fromEncodedPath: polyStr)
        let polyline = GMSPolyline(path: path)
        polyline.strokeWidth = 3.0
        polyline.strokeColor = UIColor.red
        polyline.map = mapView // Your map view
    }

Note : You need to put googleDirection API key in URL.

Hardik Thakkar
  • 15,269
  • 2
  • 94
  • 81
10

If anyone is looking for a Swift 3.0 for @Tarek's answer, you can use this. This also use Alamofire and SwiftyJSON.

func drawPath()
{
    let origin = "\(currentLocation.latitude),\(currentLocation.longitude)"
    let destination = "\(destinationLoc.latitude),\(destinationLoc.longitude)"


    let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving&key=YOURKEY"

    Alamofire.request(url).responseJSON { response in
      print(response.request)  // original URL request
      print(response.response) // HTTP URL response
      print(response.data)     // server data
      print(response.result)   // result of response serialization

      let json = JSON(data: response.data!)
      let routes = json["routes"].arrayValue

      for route in routes
      {
        let routeOverviewPolyline = route["overview_polyline"].dictionary
        let points = routeOverviewPolyline?["points"]?.stringValue
        let path = GMSPath.init(fromEncodedPath: points!)
        let polyline = GMSPolyline.init(path: path)
        polyline.map = self.mapView
      }
    }
  }
Christian Abella
  • 5,747
  • 2
  • 30
  • 42
7

Here's a Swift translation of johny kumar's answer.

let cameraPositionCoordinates = CLLocationCoordinate2D(latitude: 18.5203, longitude: 73.8567)
    let cameraPosition = GMSCameraPosition.cameraWithTarget(cameraPositionCoordinates, zoom: 12)

    let mapView = GMSMapView.mapWithFrame(CGRectZero, camera: cameraPosition)
    mapView.myLocationEnabled = true

    let marker = GMSMarker()
    marker.position = CLLocationCoordinate2DMake(18.5203, 73.8567)
    marker.groundAnchor = CGPointMake(0.5, 0.5)
    marker.map = mapView

    let path = GMSMutablePath()
    path.addCoordinate(CLLocationCoordinate2DMake(18.520, 73.856))
    path.addCoordinate(CLLocationCoordinate2DMake(16.7, 73.8567))

    let rectangle = GMSPolyline(path: path)
    rectangle.strokeWidth = 2.0
    rectangle.map = mapView

    self.view = mapView
Clément Cardonnel
  • 4,232
  • 3
  • 29
  • 36
5

- Swift 3.0 & XCode 8.0 Staright Line :(

let cameraPosition = GMSCameraPosition.camera(withLatitude: 18.5203, longitude: 73.8567, zoom: 12)
        self.mapView = GMSMapView.map(withFrame: CGRect.zero, camera: cameraPosition)
        self.mapView.isMyLocationEnabled = true
        let marker = GMSMarker()
        marker.position = CLLocationCoordinate2DMake(18.5203, 73.8567)
       // marker.icon = UIImage(named: "aaa.png")!
        marker.groundAnchor = CGPoint(x: 0.5, y: 0.5)
        marker.map = mapView
        let path = GMSMutablePath()
        path.add(CLLocationCoordinate2DMake(CDouble((18.520)), CDouble((73.856))))
        path.add(CLLocationCoordinate2DMake(CDouble((16.7)), CDouble((73.8567))))
        let rectangle = GMSPolyline.init(path: path)
        rectangle.strokeWidth = 2.0
        rectangle.map = mapView
        self.view = mapView
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
3

Make an URL request to Google Directions API and when you receive a JSON file go through all steps and decode the points objects.

WWJD
  • 1,104
  • 4
  • 12
  • 31
3

I have made it with AlamoFire and SwiftyJson in xCode 8.3.3 and Swift 3.1. Put the drawing of the path in a function that needs two parameters only

a String origin example "48.7788,9.22222" and a String destination example "49.3212232,8.334151"

func drawPath (origin: String, destination: String) {
    /* set the parameters needed */ 
    String prefTravel = "walking" /* options are driving, walking, bicycling */
    String gmapKey = "Ask Google"
    /* Make the url */
    let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=\(prefTravel)&key=" + gmapKey)

    /* Fire the request */
    Alamofire.request(url!).responseJSON{(responseData) -> Void in
        if((responseData.result.value) != nil) {
            /* read the result value */
            let swiftyJsonVar = JSON(responseData.result.value!)
            /* only get the routes object */
            if let resData = swiftyJsonVar["routes"].arrayObject {
                let routes = resData as! [[String: AnyObject]]
                /* loop the routes */
                if routes.count > 0 {
                    for rts in routes {
                       /* get the point */
                       let overViewPolyLine = rts["overview_polyline"]?["points"]
                       let path = GMSMutablePath(fromEncodedPath: overViewPolyLine as! String)
                       /* set up poly line */
                       let polyline = GMSPolyline.init(path: path)
                       polyline.strokeWidth = 2
                       polyline.map = self.mapView
                    }
                }
            }
        }
    }
}
Charley57
  • 83
  • 1
  • 6
1

Hi You can use "LRouteController", it is a best way to show road route between two points like :

[_routeController getPolyline With Locations: (Array of first and last location)]

Try it, I hope it will resolve your problem.

Vishnu
  • 11,614
  • 6
  • 51
  • 90
Devil
  • 314
  • 3
  • 13
1

DirectionResponse from the Google Directions APIThe NSLogs are useful to see what you are working with.

[[GMDirectionService sharedInstance] getDirectionsFrom:origin to:destination          succeeded:^(GMDirection *directionResponse) {   
if ([directionResponse statusOK]){
    NSLog(@"Duration : %@", [directionResponse durationHumanized]);
    NSLog(@"Distance : %@", [directionResponse distanceHumanized]);
    NSArray *routes = [[directionResponse directionResponse] objectForKey:@"routes"];
    // NSLog(@"Route : %@", [[directionResponse directionResponse] objectForKey:@"routes"]);

    GMSPath *path = [GMSPath pathFromEncodedPath:routes[0][@"overview_polyline"]  [@"points"]];
    GMSPolyline *polyline = [GMSPolyline polylineWithPath:path];
    polyline.strokeColor = [UIColor redColor];
    polyline.strokeWidth = 5.f;
    polyline.map = mapView;

}
} failed:^(NSError *error) {
    NSLog(@"Can't reach the server")
}];
Nil Rathod
  • 450
  • 5
  • 21
1

As you know get directions and routes from google is NOT FREE and last year google changed their api calls prices a lot! So it may not suitable for everyone. So if you have all key coordinates and just want to connect them together, you can use the following.

- Swift 4 Extensions

Make path with coordinates:

extension GMSMutablePath {
    convenience init(coordinates: [CLLocationCoordinate2D]) {
        self.init()
        for coordinate in coordinates {
            add(coordinate)
        }
    }
}

Add path to map:

extension GMSMapView {
    func addPath(_ path: GMSPath, strokeColor: UIColor? = nil, strokeWidth: CGFloat? = nil, geodesic: Bool? = nil, spans: [GMSStyleSpan]? = nil) {
        let line = GMSPolyline(path: path)
        line.strokeColor = strokeColor ?? line.strokeColor
        line.strokeWidth = strokeWidth ?? line.strokeWidth
        line.geodesic = geodesic ?? line.geodesic
        line.spans = spans ?? line.spans
        line.map = self
    }
}

Usage:

let path = GMSMutablePath(coordinates: [<#Coordinates#>])
mapView.addPath(path)
  • NOTE: You can create exact same line as google just once using tools out there or even google itself, store it somewhere and give it to your client as need.
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
1

Swift 5 Its working fine for me

View will appear

self.drawMap(SourceCordinate: CLLocationCoordinate2D(latitude: lat, longitude: long), destinationcordinate: CLLocationCoordinate2D(latitude: latitude, longitude: longitude))



func drawMap(SourceCordinate : CLLocationCoordinate2D, destinationcordinate :CLLocationCoordinate2D)
    {
        self.mapView.clear()
        let str = String(format:"https://maps.googleapis.com/maps/api/directions/json?origin=\(SourceCordinate.latitude),\(SourceCordinate.longitude)&destination=\(destinationcordinate.latitude),\(destinationcordinate.longitude)&key=\(googleServiceKey)")
        print(str)
        Alamofire.request(str).responseJSON { (responseObject) -> Void in
            let resJson = JSON(responseObject.result.value!)
            print(resJson)
            let routes : NSArray = resJson["routes"].rawValue as! NSArray
            if(resJson["status"].rawString()! == "ZERO_RESULTS"){}
            else if(resJson["status"].rawString()! == "NOT_FOUND"){}
            else if routes.count == 0{}
            else{
                let routes : NSArray = resJson["routes"].rawValue as! NSArray
//                let position = CLLocationCoordinate2D(latitude: SourceCordinate.latitude, longitude: SourceCordinate.longitude)
                let markerEnd = GMSMarker()
                markerEnd.position = CLLocationCoordinate2D(latitude: self.latitude, longitude: self.longitude)
                markerEnd.map = self.mapView
                let pathv : NSArray = routes.value(forKey: "overview_polyline") as! NSArray
                let paths : NSArray = pathv.value(forKey: "points") as! NSArray
                let newPath = GMSPath.init(fromEncodedPath: paths[0] as! String)
                let polyLine = GMSPolyline(path: newPath)
                polyLine.strokeWidth = 5
                polyLine.strokeColor =  .black
                let ThemeOrange = GMSStrokeStyle.solidColor( .blue)
                let OrangeToBlue = GMSStrokeStyle.gradient(from:  .blue, to:  .blue)
                polyLine.spans = [GMSStyleSpan(style: ThemeOrange),
                                  GMSStyleSpan(style: ThemeOrange),
                                  GMSStyleSpan(style: OrangeToBlue)]
                polyLine.map = self.mapView

            }
        }
    }
Srinivasan_iOS
  • 972
  • 10
  • 12