Alberto Amador Gil
Alberto Amador Gil

Reputation: 29

route between user and marker in swift, JSON and google maps API

I want to create a route between the user position and a marker when it is touched using JSON and google directions server API on swift (Google MAPS API).

I have a extension with this code:

extension MapViewController: GMSMapViewDelegate {
    
func mapView(mapView: GMSMapView!, didTapMarker marker: GMSMarker!) -> Bool {
        reverseGeocodeCoordinate(marker.position)
        
    originAddresslong = "\(userLocation.coordinate.longitude)"
    originAddresslat = "\(userLocation.coordinate.latitude)"
    destinationAddresslong = "\(marker.position.longitude)"
    destinationAddresslat = "\(marker.position.latitude)"
    
    
    var directionsURLString = baseURLDirections + "origin=" + originAddresslat + "," + originAddresslong + "&destination=" + destinationAddresslat + "," + destinationAddresslong + "&key=Mykey"
        
directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
        let directionsURL = NSURL(string: directionsURLString)
        
        func crearruta(withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
            
        
        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            let directionsData = NSData(contentsOfURL: directionsURL!)
            
            let dictionary: Dictionary<NSObject, AnyObject>
            
            do {
                
                dictionary = try NSJSONSerialization.JSONObjectWithData(directionsData!, options: NSJSONReadingOptions.MutableContainers) as! Dictionary<NSObject, AnyObject>
                
                // Get the response status.
                
                let status = dictionary["status"] as! String
                
                if status == "OK" {
                    
                    let allResults = dictionary["results"]as! Array<Dictionary<NSObject, AnyObject>>
                    
                    self.lookupAddressResults = allResults[0]
                    
                    // Keep the most important values.
                    
                    self.overviewPolyline = self.lookupAddressResults["overview_polyline"] as! Dictionary<NSObject, AnyObject>
                    
                    let legs = self.selectedRoute["legs"] as! Array<Dictionary<NSObject, AnyObject>>
                    
                    self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as! String
                    
                    let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
                    
                    self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
                    
                    self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue

                    
                       
                }
                    
                else {
                    
                    completionHandler(status: status, success: false)
                    
                }
                
            } catch {
                
                completionHandler(status: "", success: false)
                
            }
            
        })
        }
      
                    let route = overviewPolyline["points"] as! String
                    let path: GMSPath = GMSPath(fromEncodedPath: route)
      
        
                         if routePolyline != nil {
                            routePolyline.map = nil
                            routePolyline = nil
                            }
        
                        routePolyline = GMSPolyline(path: path)
                        routePolyline.strokeWidth = 5
                        routePolyline.strokeColor = UIColor.yellowColor()
                        routePolyline.map = mapView
                        
        
        return false
    }

1- userlocation.coordinate is working, because i can read longitude and latitude in a label.

2- marker position is showing coordinates too in a label.

3- Mykey is my api key for Google directions (on a web search it works)

what is the problem? the conversion to string values of coordinates? the crearruta function?

The error message is:

fatal error: unexpectedly found nil while unwrapping an Optional value

Thanks for all.

Edit: I have made a change to the originaddress and destinationaddress variables. Now if I use

UIApplication.sharedApplication().openURL(NSURL(string: directionsURLString)!)

I can see the Json properly. I need now to use it.

OK. I wanna create a polyline with the JSON information. I have read that i need only the polyline points. I must use a dictionary to get the info and then draw the line.

I have used this code but also fails.

          func crearruta(withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {


    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        let directionsData = NSData(contentsOfURL: directionsURL!)
        let dictionary: Dictionary<NSObject, AnyObject>

        do {

            dictionary = try NSJSONSerialization.JSONObjectWithData(directionsData!, options: NSJSONReadingOptions.MutableContainers) as! Dictionary<NSObject, AnyObject>

            // Get the response status.

            let status = dictionary["status"] as! String

            if status == "OK" {

                self.selectedRoute = (dictionary["routes"] as! Array<Dictionary<NSObject, AnyObject>>)[0]
                self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<NSObject, AnyObject>

                let legs = self.selectedRoute["legs"] as! Array<Dictionary<NSObject, AnyObject>>

                let startLocationDictionary = legs[0]["start_location"] as! Dictionary<NSObject, AnyObject>
                self.originCoordinate = CLLocationCoordinate2DMake(startLocationDictionary["lat"] as! Double, startLocationDictionary["lng"] as! Double)

                let endLocationDictionary = legs[legs.count - 1]["end_location"] as! Dictionary<NSObject, AnyObject>
                self.destinationCoordinate = CLLocationCoordinate2DMake(endLocationDictionary["lat"] as! Double, endLocationDictionary["lng"] as! Double)

                self.originAddress = legs[0]["start_address"] as! String
                self.destinationAddress = legs[legs.count - 1]["end_address"] as! String

                //ruta con curvas en coche
                let route = self.overviewPolyline["points"] as! String
                self.path = GMSPath(fromEncodedPath: route)

                completionHandler(status: status, success: true)


            }

            else {

                completionHandler(status: status, success: false)

            }

        } catch {

            completionHandler(status: "", success: false)

        }

    })
    }        

    if routePolyline != nil {
        routePolyline.map = nil
        routePolyline = nil
    }

    routePolyline = GMSPolyline(path: path)
    routePolyline.strokeWidth = 5
    routePolyline.strokeColor = UIColor.yellowColor()
    routePolyline.map = mapView




    return false
}

Upvotes: 1

Views: 1356

Answers (2)

Alberto Amador Gil
Alberto Amador Gil

Reputation: 29

Finally I got it!

I have used Alamofire and SwiftyJSON pods as suggested here: Swift iOS google Map, path to coordinate

The final code for the extension has been

extension MapViewController: GMSMapViewDelegate {

func mapView(mapView: GMSMapView!, didTapMarker marker: GMSMarker!) -> Bool {
    reverseGeocodeCoordinate(marker.position)

    originAddresslong = "\(userLocation.coordinate.longitude)"
    originAddresslat = "\(userLocation.coordinate.latitude)"
    destinationAddresslong = "\(marker.position.longitude)"
    destinationAddresslat = "\(marker.position.latitude)"


    var directionsURLString = baseURLDirections + "origin=" + originAddresslat + "," + originAddresslong + "&destination=" + destinationAddresslat + "," + destinationAddresslong + "&key=AIzaSyB4xO_8B0ZoA8lsAgRjqpqJjgWHbb5X3u0"
    directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
    let directionsURL = NSURL(string: directionsURLString)


    Alamofire.request(.GET, directionsURL!, parameters: nil).responseJSON { response in

        switch response.result {

        case .Success(let data):

            let json = JSON(data)
            print(json)

            let errornum = json["error"]


            if (errornum == true){



            }else{
                let routes = json["routes"].array

                if routes != nil{

                    let overViewPolyLine = routes![0]["overview_polyline"]["points"].string
                    print(overViewPolyLine)
                    if overViewPolyLine != nil{

                        if self.routePolyline != nil {
                            self.routePolyline.map = nil
                            self.routePolyline = nil
                        }


                        let path = GMSMutablePath(fromEncodedPath: overViewPolyLine)
                        self.routePolyline = GMSPolyline(path: path)
                        self.routePolyline.strokeWidth = 5
                        self.routePolyline.strokeColor = UIColor.yellowColor()
                        self.routePolyline.map = mapView

                    }

                }
            }
        case .Failure(let error):

            print("Hubo un problema con el servidor de direcciones: \(error)")
        }

    }

    return false
}

Upvotes: 0

Dravidian
Dravidian

Reputation: 9945

You are initialising self.overviewPolyline in a async block, which would run on a separate thread but you are trying to access self.overviewPolyline even before it has been initialised in let route = self.overviewPolyline["points"] as! String

Just put

let route = overviewPolyline["points"] as! String
let path: GMSPath = GMSPath(fromEncodedPath: route)

inside the do{ block after

self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue

or if you want to make them a global variable , create their instances outside the func crearruta, but initialise them inside the do block

Upvotes: 0

Related Questions