Reputation: 29
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
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
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