How to calculate ETA time with Google Maps API - Swift - swift

I am drawing a route with Google map. I calculate the km distance on the route I drew. I also want to calculate how long it will go. How can I calculate how many minutes the route will take? I calculated the speed in the code below and when I tried to calculate the time using the speed I could not get any output. How can I calculate the duration?
func drowRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
self.mapView.clear()
let origin = "\(source.latitude),\(source.longitude)"
let destinationn = "\(destination.latitude),\(destination.longitude)"
guard let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destinationn)&mode=driving&key=..") else {
let error = NSError(domain: "LocalDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create object URL"])
print("Error: \(error)")
return
}
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
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 {
}
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
DispatchQueue.main.async {
//
let legs = overview_polyline?["legs"] as! Array<Dictionary<String, AnyObject>>
let distance = legs[0]["distance"] as? NSDictionary
let distanceValue = distance?["value"] as? Int ?? 0
let distanceDouleValue = distance?["value"] as? Double ?? 0.0
let duration = legs[0]["duration"] as? NSDictionary
let totalDurationInSeconds = duration?["value"] as? Int ?? 0
let durationDouleValue = duration?["value"] as? Double ?? 0.0
if(distanceValue != 0) {
self.speed = distanceDouleValue / durationDouleValue
print("speed", self.speed)
}
let miles = Double(distanceValue) / 1609.344
print("\(miles)")
let km = Double(distanceValue) * 0.001609
self.kmLabel.text = ("\(Int(km))" + " " + "KM")
if distanceValue > Int(32186.9){
}else{
self.showPath(polyStr: points!)
let startLocationDictionary = legs[0]["start_location"] as! Dictionary<String, AnyObject>
let originCoordinate = CLLocationCoordinate2DMake(startLocationDictionary["lat"] as! Double, startLocationDictionary["lng"] as! Double)
let endLocationDictionary = legs[legs.count - 1]["end_location"] as! Dictionary<String, AnyObject>
let destinationCoordinate = CLLocationCoordinate2DMake(endLocationDictionary["lat"] as! Double, endLocationDictionary["lng"] as! Double)
let marker1 = GMSMarker()
marker1.position = CLLocationCoordinate2D(latitude:destinationCoordinate.latitude, longitude: destinationCoordinate.longitude)
marker1.icon = UIImage(named: "placeholder")
marker1.map = self.mapView
let marker2 = GMSMarker()
marker2.position = CLLocationCoordinate2D(latitude:originCoordinate.latitude, longitude: originCoordinate.longitude)
marker2.icon = UIImage(named: "location")
marker2.map = self.mapView
}
}
}
else {
print(json)
DispatchQueue.main.async {
// SVProgressHUD.dismiss()
}
}
}
}
catch {
print("error in JSONSerialization")
DispatchQueue.main.async {
// SVProgressHUD.dismiss()
}
}
}
})
task.resume()
}
func showPath(polyStr :String){
// SVProgressHUD.dismiss()
let path = GMSPath(fromEncodedPath: polyStr)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 5.0
polyline.strokeColor = UIColor.red
polyline.map = mapView
DispatchQueue.main.async {
let bounds = GMSCoordinateBounds(path: path!)
let update = GMSCameraUpdate.fit(bounds, with: UIEdgeInsets(top: 170, left: 30, bottom: 30, right: 30))
self.mapView.moveCamera(update)
}
}

Related

Google Maps API v3 - Duration from Matrix API Swift

I am trying to get the duration time from google maps API using["legs"] but I always get a null value.
do {
let jsonData = try JSON(data: data)
let routes = jsonData["routes"].arrayValue
for route in routes {
let overview_polyline = route["overview_polyline"].dictionary
let points = overview_polyline?["points"]?.string
let path = GMSPath.init(fromEncodedPath: points ?? "")
let polyline = GMSPolyline.init(path: path)
polyline.strokeColor = .systemYellow
polyline.strokeWidth = 5
polyline.map = self.mapView
}
}
catch let error {
print(error.localizedDescription)
}
I just added this code
do {
let jsonData = try JSON(data: data)
let routes = jsonData["routes"].arrayValue
for route in routes {
let overview_polyline = route["overview_polyline"].dictionary
let legs = route["legs"][0]
let duration = legs["duration"]["text"]
print("This is the distance ", duration)
let points = overview_polyline?["points"]?.string
let path = GMSPath.init(fromEncodedPath: points ?? "")
let polyline = GMSPolyline.init(path: path)
polyline.strokeColor = .systemYellow
polyline.strokeWidth = 5
polyline.map = self.mapView
}
}
catch let error {
print(error.localizedDescription)
}

Get Distance between two points Swift 5

I'm trying to get the distance between tow points, current location, and another point.
All the data is calculate great, but I can't get the value to asign it to the object, and then create the table.
The distance is created later than the object even I hace make an async queue.
I can't find the problem maybe I'm too new in this.
Thanks for the help
DispatchQueue.global(qos: .utility).async {
for snap in snapshot.children {
let postSnap = snap as! DataSnapshot
if let dict = postSnap.value as? [String:AnyObject] {
let farcoordenadas = dict["coordenadas"] as! [Double]
let lat = (farcoordenadas[0] as! Double)
let long = (farcoordenadas[1] as! Double)
let location = CLLocationCoordinate2D(latitude: lat, longitude: long)
let farcalle = (dict["calle"] as! String)
let fartelefono = (dict["telefono"] as! String)
let farpoblacion = (dict["poblacion"] as! String)
let farhorario = (dict["horario"] as! String)
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate:self.userLocation!))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: location))
request.transportType = .automobile
let directions = MKDirections(request: request)
directions.calculate { (response, error) -> Void in
if let response = response, let route = response.routes.first {
print(route.distance)
//converts from meters to miles
var routeDistance = route.distance/1000
//formats the string to two decimals.
distancia = String(format: "%.0f" , routeDistance)
}
return
}
DispatchQueue.main.async {
let farmaciapueblo = Farmacia(fecha:dateString, horario:farhorario, poblacion: farpoblacion, telefono:fartelefono, calle:farcalle, coordenadas:farcoordenadas, distancia:distancia)
self.farmaciasrural.append(farmaciapueblo)
self.farmaciasrural.sort(by: myLocation) // mutating version
self.activityIndicator.stopAnimating()
self.tableView.reloadData()
}
}
}
}

Unable to get the data from a nested json swift

I'm learning swift.
I have a json from server.
[
{
"dId": 1,
"vendor": {
"vendorId": 1,
"name": "Gems",
"about": "Get Good quality stones",
"address": "JacksonVille Road",
"latitude": 12.232323,
"longitude": 77.230802,
},
"name": "Gems Delight",
}
]
I'm unable to parse this json and get the data from lat and lon and name from vendor.
My url method and my for loop how can I exactly get the latitude and longitude from the loop and put them on the map ?
My View did load method
override func viewDidLoad() {
super.viewDidLoad()
guard let gitUrl = URL(string: "localhost:8080/deals") else { return }
URLSession.shared.dataTask(with: gitUrl) { (data, response
, error) in
guard let data = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
var lat:Double
var lon:Double
var nam = ""
for items in json as! [AnyObject]{
// let te = type(of: items)
// print(te)
let new = items["vendor"]
for (it,key) in new as! [String:Any]{
// print(it,key)
// print(it["longitude"])
if it == "longitude"{
print(it,key)
lon = key as! Double
}
if it == "latitude"{
print(it,key)
lat = key as! Double
}
if it == "name"{
nam = key as! String
}
if (nam.isEmpty == false){
print("falsdalsdasldasd")
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
let camera = GMSCameraPosition.camera(withLatitude:lat, longitude:lon, zoom: 4.0)
let subView = GMSMapView.map(withFrame: self.view.bounds,camera:camera)
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude:lat, longitude:lon)
marker.title = nam
// marker.snippet = "Australia"
marker.map = subView
self.mapView.addSubview(subView)
}
// print(it["longitude"])
}
// let te = type(of: items)
// for it in new as![AnyObject]{
//// print(it"])
// print(it)
//
// }
// print(items["vendor"].["latitude"])
// print(items.vendor)
// print(items[""])
// let nam = items["name"];
// let desc = items["description"];
// self.locationNames.append(nam as! String)
// self.locationDescription.append(desc as! String)
//
}
// self.colecVw.delegate = self
// self.colecVw.dataSource = self
// self.colecVw.reloadData()
// }
} catch let err {
print("Err", err)
}
}.resume()
print("coming here")
// Create a GMSCameraPosition that tells the map to display the
// coordinate -33.86,151.20 at zoom level 6.
// let mapView = GMSMapView(frame: CGRect(x: 0, y: 64, width: self.currentDeviceSize.width, height: self.bottomBgView.frame.minY - 64))
// view = mapView
//
// // Creates a marker in the center of the map.
// let marker = GMSMarker()
// marker.position = CLLocationCoordinate2D(latitude: -33.86, longitude: 151.20)
// marker.title = "Sydney"
// marker.snippet = "Australia"
// marker.map = mapView
}
Please help me as I'm learning to code my code might not be correct can please dont mind and load the data of lat and long onto the map. Should I construct an object and put them there ?
Variable 'lon' used before being initialized
Variable 'lat' used before being initialized
This is the error I get from the code.
The errors occur because lat and lon must have a value in the line
let camera = GMSCameraPosition.camera(withLatitude:lat, longitude:lon, zoom: 4.0)
which is not guaranteed if the key comparisons fail.
The condition is fulfilled if you write
var lat = 0.0 // The type Double is inferred
var lon = 0.0
But rather than enumerating the dictionary get the values for the keys directly and safely with optional binding
do {
// no mutableContainers !!
let json = try JSONSerialization.jsonObject(with: data) as! [[String:Any]]
for item in json {
if let vendor = item["vendor"] as? [String:Any],
let lat = vendor["latitude"] as? Double,
let lon = vendor["longitude"] as? Double,
let name = item["name"] as? String, !name.isEmpty {
print("falsdalsdasldasd")
// self.locationManager.delegate = self
// self.locationManager.requestWhenInUseAuthorization()
// self.locationManager.startUpdatingLocation()
let camera = GMSCameraPosition.camera(withLatitude:lat, longitude:lon, zoom: 4.0)
let subView = GMSMapView.map(withFrame: self.view.bounds,camera:camera)
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude:lat, longitude:lon)
marker.title = name
marker.map = subView
self.mapView.addSubview(subView)
}
}
} catch {
print("Err", error)
}
And it's nonsensical to call the LocationManager methods in each iteration. Call them once at the beginning of viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
self.locationManager.delegate = self
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
...
You can try
struct Root: Codable {
let dID: Int
let vendor: Vendor
let name: String
enum CodingKeys: String, CodingKey {
case dID = "dId"
case vendor, name
}
}
struct Vendor: Codable {
let vendorID: Int
let name, about, address: String
let latitude, longitude: Double
enum CodingKeys: String, CodingKey {
case vendorID = "vendorId"
case name, about, address, latitude, longitude
}
}
--
let arr = try? JSONDecoder().decode([Root].self, from:data)
print(arr?.forEach {$0.vendor.latitude })
See this one
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
Pass json to
if let data = json as? NSArray {
for data in data {
if let data = data as? [String: AnyObject] {
let dataID = data["dId"] as? Int
if let data = data[“vendor”] as? [String: AnyObject] {
let vendorID = data["vendorId"] as? Int
}
}
}
}

Distance from Current Location to Annotation. (Firebase)

I want to have a the distance from my Currentlocation to a annotation that's in the FireData Base. I tried to make it word but i can't ;(. I would like to have the distance between the two locations in a var. I hope you guys can help me.
func reload(){
//get data
Database.database().reference().child("Rollerbanken").observe(.value, with: { (snapshot) in
for item in snapshot.children{
if let value = snapshot.value as? Dictionary<String, Any> {
for key in value.keys {
if let itemDict = value[key] as? Dictionary<String, AnyObject> {
let annotation = MKPointAnnotation()
annotation.title = itemDict["TypeControle"] as! String
let tijd = itemDict["Tijd"] as! String
annotation.subtitle = "Geplaatst om \(tijd)"
let getLatitude = itemDict["Latitude"] as? String
let getLongitude = itemDict["Longitude"] as? String
if let lat = getLatitude, let long = getLongitude {
annotation.coordinate = CLLocationCoordinate2D(latitude: Double(lat)!, longitude: Double(long)!)
self.map.addAnnotation(annotation)
let directionRequest = MKDirectionsRequest()
directionRequest.source = MKMapItem.forCurrentLocation()
if #available(iOS 10.0, *) {
directionRequest.destination = MKMapItem(placemark: MKPlacemark.init(coordinate: CLLocationCoordinate2DMake(Double(lat)!, Double(long)!)))
} else {
// Fallback on earlier versions
}
directionRequest.transportType = .walking
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: { (response, error) in
if error != nil {
print("Error while build route")
} else {
let route = response?.routes.last
let distance = route?.distance
print(distance)
}
})
}
}
}
}
}
})
}
Here is my Structure:
Try to use this code. Don't forget to enable your current location on map
let directionRequest = MKDirectionsRequest()
directionRequest.source = MKMapItem.forCurrentLocation()
directionRequest.destination = MKMapItem(placemark: MKPlacemark.init(coordinate: CLLocationCoordinate2DMake(YOURPOINTLATITUDE, YOURPOINTLONGITUDE)))
directionRequest.transportType = .walking
let direction = MKDirections(request: directionRequest)
direction.calculate(completionHandler: { (response, error) in
if error != nil {
print("Error while build route")
} else {
let route = response?.routes.last
let distance = route?.distance
I have used similar function, NOTE this was my function therefore it has rider and driver.. however you can change it to use annotation and location from firebase.
if let rideRequestDictionary = snapshot.value as? [String:AnyObject] {
// Getting the rider location and email
if let email = rideRequestDictionary["email"] as? String {
if let lat = rideRequestDictionary["lat"] as? Double{
if let lon = rideRequestDictionary["lon"] as? Double{
// Getting the Driver location and email
let driverCLLocation = CLLocation(latitude: driverLocation.latitude, longitude: driverLocation.longitude)
let riderCLLocation = CLLocation(latitude: lat, longitude: lon)
// getting the distance between the two people
let distance = driverCLLocation.distance(from: riderCLLocation) / 1000
// rounding the distances
let roundedDistance = round(distance * 100) / 100
// putting the rounded distance and email in label
cell.textLabel?.text = "\(email) - \(roundedDistance)km away"
}
}
}

SWIFT: google maps draw waypoint polyline

Hi I wonder if there is a method to draw a waypoint between two or more markers in google maps iOS. I don't want to draw straight lines... but use just public roads. Here is some of my code to draw straight lines but its not what i am looking for.
#objc private func makeGpsPath(){
for i in 0 ..< trailArr.count {
path.add(trailArr[i])
}
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 5.0
polyline.strokeColor = UIColor.black
polyline.map = mapViewContainer
}
To draw polylines between points two ore more you should use google map request read this link https://developers.google.com/maps/documentation/directions/intro#Waypoints
in my case i did
func drawRoute() {
ServerCommunicator.getDotsToDrawRoute(positions: positions, completion: { path in
self.route.countRouteDistance(p: path)
self.polyline.path = path
self.polyline.strokeColor = UserSession.tintColor
self.polyline.strokeWidth = 4.0
self.polyline.map = self._mapView
})
}
and the part with request
static func getDotsToDrawRoute(positions : [CLLocationCoordinate2D], completion: #escaping(_ path : GMSPath) -> Void) {
if positions.count > 1 {
let origin = positions.first
let destination = positions.last
var wayPoints = ""
for point in positions {
wayPoints = wayPoints.characters.count == 0 ? "\(point.latitude),\(point.longitude)" : "\(wayPoints)|\(point.latitude),\(point.longitude)"
}
let request = "https://maps.googleapis.com/maps/api/directions/json"
let parameters : [String : String] = ["origin" : "\(origin!.latitude),\(origin!.longitude)", "destination" : "\(destination!.latitude),\(destination!.longitude)", "wayPoints" : wayPoints, "key" : googleAPI_KEY]
Alamofire.request(request, method:.get, parameters : parameters).responseJSON(completionHandler: { response in
guard let dictionary = response.result.value as? [String : AnyObject]
else {
return
}
if let routes = dictionary["routes"] as? [[String : AnyObject]] {
if routes.count > 0 {
var first = routes.first
if let legs = first!["legs"] as? [[String : AnyObject]] {
let fullPath : GMSMutablePath = GMSMutablePath()
for leg in legs {
if let steps = leg["steps"] as? [[String : AnyObject]] {
for step in steps {
if let polyline = step["polyline"] as? [String : AnyObject] {
if let points = polyline["points"] as? String {
fullPath.appendPath(GMSMutablePath(fromEncodedPath: points))
}
}
}
completion(path: fullPath)
}
}
}
}
}
})
}
}
extension GMSMutablePath {
func appendPath(path : GMSPath?) {
if let path = path {
for i in 0..<path.count() {
self.add(path.coordinate(at: i))
}
}
}
}
To draw polyline between two markers on GoogleMap in Swift 3.
// Pass your source and destination coordinates in this method.
func getPolylineRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D){
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let url = URL(string: "http://maps.googleapis.com/maps/api/directions/json?origin=\(source.latitude),\(source.longitude)&destination=\(destination.latitude),\(destination.longitude)&sensor=false&mode=driving")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
}else{
do {
if let json : [String:Any] = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]{
let routes = json["routes"] as? [Any]
let overview_polyline = routes?[0] as?[String:Any]
let polyString = overview_polyline?["points"] as?String
//Call this method to draw path on map
self.showPath(polyStr: polyString!)
}
}catch{
print("error in JSONSerialization")
}
}
})
task.resume()
}
To draw polyline on map .
func showPath(polyStr :String){
let path = GMSPath(fromEncodedPath: polyStr)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 3.0
polyline.map = mapView // Your map view
}
private func drowRoute(){
let path = GMSMutablePath()
path.addLatitude(self.lat!, longitude: self.long!)
path.addLatitude(self.destLat!, longitude: self.destLong!)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 2.0
polyline.strokeColor = UIColor.blue
polyline.geodesic = true
polyline.map = mappView
}
let point3 = CLLocationCoordinate2D(latitude: Double(30.7173), longitude: Double(76.8329))
let point4 = CLLocationCoordinate2D(latitude: Double(30.6942), longitude: Double(76.8606))
let point5 = CLLocationCoordinate2D(latitude: Double(30.7465), longitude: Double(76.7872))
var arrOfWayPoints : NSMutableArray = NSMutableArray()
arrOfWayPoints.insert(point3, at: 0)
arrOfWayPoints.insert(point4, at: 1)
arrOfWayPoints.insert(point5, at: 2)
self.drawRouteWithWaypoint(positions: arrOfWayPoints as! [CLLocationCoordinate2D])
static var distance = Double()
func drawRouteWithWaypoint(positions:[CLLocationCoordinate2D]) {
LiveJob.getDotsToDrawRoute(positions: positions, completion: { path in
//self.route.countRouteDistance(p: path)
self.polyline.path = path
self.polyline.strokeColor = UIColor.blue
self.polyline.strokeWidth = 2.0
self.polyline.map = self.mapView
})
self.lblDistance.text = String(LiveJob.distance)
}
static func getDotsToDrawRoute(positions : [CLLocationCoordinate2D], completion: #escaping(_ path : GMSPath) -> Void) {
if positions.count > 1 {
let origin = positions.first
let destination = positions.last
var wayPoints = ""
for point in positions {
wayPoints = wayPoints.characters.count == 0 ? "\(point.latitude),\(point.longitude)" : "\(wayPoints)|\(point.latitude),\(point.longitude)"
}
let request = "https://maps.googleapis.com/maps/api/directions/json"
let parameters : [String : String] = ["origin" : "\(origin!.latitude),\(origin!.longitude)", "destination" : "\(destination!.latitude),\(destination!.longitude)", "wayPoints" : wayPoints,"mode" : "Transit","key" : "AIzaSyCtMHyxPEModWK8IgzBD96hQMFL-UCIjcY"]
Alamofire.request(request, method:.get, parameters : parameters).responseJSON(completionHandler: { response in
guard let dictionary = response.result.value as? [String : AnyObject]
else {
return
}
if let routes = dictionary["routes"] as? [[String : AnyObject]] {
if routes.count > 0 {
var first = routes.first
if let legs = first!["legs"] as? [[String : AnyObject]] {
let newLeg = legs[0]
let distance = newLeg["distance"]
// LiveJob.distance = LiveJob.distance + distance!.doubleValue
let fullPath : GMSMutablePath = GMSMutablePath()
for leg in legs {
if let steps = leg["steps"] as? [[String : AnyObject]] {
for step in steps {
if let polyline = step["polyline"] as? [String : AnyObject] {
if let points = polyline["points"] as? String {
fullPath.appendPath(path: GMSMutablePath(fromEncodedPath: points))
}
}
}
completion(fullPath)
}
}
}
}
}
})
}
}