I'm trying to build a route using Google Directions API. It returns an array of waypoints, however, the number of waypoints is not enough to build a smoothed route, and as the result I receive quite inaccurate route what is super crucial and inappropriate for transport application. I also tried HERE maps, almost the same result.
Is there any other services that may build more accurate route, or any solution applicable to the Google Directions API?
Here is the function:
public func getWaypoints(startLocation: [Double]!, endLocation: [Double]!, mode:String? = "walking", lang:String? = "en") -> [Dictionary<String,Any>] {
var resultedArray:[Dictionary<String,Any>] = []
let routeGM = Just.get("https://maps.googleapis.com/maps/api/directions/json?", params: ["origin":"\(startLocation[0]),\(startLocation[1])", "destination": "\(endLocation[0]),\(endLocation[1])", "mode": mode!, "key":self.KEY, "language": lang!])
if let _ = routeGM.error {
} else {
do {
if let data = jsonToNSData(routeGM.json! as AnyObject), let jsonData = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary {
let status = jsonData["status"] as! String
if(status == "OK") {
let results = jsonData["routes"] as! Array<Dictionary<String, AnyObject>>
let legs = results[0]["legs"] as! Array<Dictionary<String, AnyObject>>
let steps = legs[0]["steps"] as! Array<Dictionary<String, AnyObject>>
for i in 0...steps.count-1 {
let item = steps[i]
let start = item["start_location"] as! Dictionary<String,Any>
let end = item["end_location"] as! Dictionary<String,Any>
resultedArray.append(["start_location_lat": start["lat"]!,"start_location_lng": start["lng"]!,"end_location_lat": end["lat"]!,"end_location_lng": end["lng"]!])
}
}
else {
print("not found")
}
}
} catch {
print(error)
}
}
return resultedArray
}
Calling function:
func buildRoute(startCoord:[Double]!, endCoord:[Double]!) {
DispatchQueue.global(qos: .background).async {
let route:[Dictionary<String,Any>] = self.GMSRequest.getWaypoints(startLocation: startCoord,endLocation: endCoord, mode: "driving")
DispatchQueue.main.async {
let path = GMSMutablePath()
for item in route {
path.add(CLLocationCoordinate2D(latitude: item["start_location_lat"]! as! CLLocationDegrees, longitude: item["start_location_lng"]! as! CLLocationDegrees))
path.add(CLLocationCoordinate2D(latitude: item["end_location_lat"]! as! CLLocationDegrees, longitude: item["end_location_lng"]! as! CLLocationDegrees))
}
// Create the polyline, and assign it to the map.
self.polyline.path = path
self.polyline.strokeColor = Styles.colorWithHexString("#3768CD")
self.polyline.strokeWidth = 3.0
self.polyline.geodesic = true
self.polyline.map = self.mapView
}
}
}
Related
Basically, I have searched a lot but mostly answer are for javaScrip and I want some code example in swift and according to google we can not use more than 25 way points in a single request
I already have seen, the links blow.
StackOverFlow.
Documentation
How to draw polyline using more than hundred waypoints (may be more than 300) swift 5?
I am using GoogleMaps.
My code and api.
func getDotsToDrawRoute(positions: [CLLocationCoordinate2D]) {
if positions.count > 0 {
let origin = positions.first
let destination = positions.last
var wayPoint = ""
var wayPoints: [String] = []
for point in positions {
wayPoint = wayPoint.count == 0 ? "\(point.latitude), \(point.longitude)" : "\(wayPoint)|\(point.latitude), \(point.longitude)"
wayPoints.append(wayPoint)
}
print("exactWayPoint:\(wayPoint)")
print("exactWayPoint:\(wayPoints)")
let stringURL = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin!.latitude),\(origin!.longitude)&destination=\(destination!.latitude),\(destination!.longitude)&wayPoints=\(wayPoints)&key=\(Google_API_Key)&sensor=false&mode=walking&waypoints"
let url = URL(string: stringURL.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)!
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if error != nil {
print("error in task: \(error!.localizedDescription)")
} else {
do {
if let dictionary: [String: Any] = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] {
print("Dictionary: \(dictionary)")
if let routes = dictionary["routes"] as? [[String: Any]] {
if routes.count > 0 {
let first = routes.first
if let legs = first!["legs"] as? [[String: Any]] {
let fullPath: GMSMutablePath = GMSMutablePath()
for leg in legs {
if let steps = leg["steps"] as? [[String: Any]] {
for step in steps {
if let polyline = step["polyline"] as? [String: Any] {
if let points = polyline["points"] as? String {
fullPath.appendPath(path: GMSMutablePath(fromEncodedPath: points))
self.drawRoutes(polyStr: points)
}
}
}
}
}
}
}
}
}
} catch let parseError {
print("JSON Error: \(parseError.localizedDescription)")
}
}
})
task.resume()
} else {
showAlert(title: "OOps!", message: "No data found")
}
}
func drawRoutes(polyStr: String) {
if let path = GMSPath.init(fromEncodedPath: polyStr) {
let polyline = GMSPolyline.init(path: path)
polyline.path = path
polyline.strokeWidth = 4.0
polyline.strokeColor = .blue
polyline.geodesic = true
self.mapView?.isMyLocationEnabled = true
polyline.map = self.mapView
} else {
print("path is nill")
}
}
// MARK - PATHDelegate
extension GMSMutablePath {
func appendPath(path: GMSPath?) {
if let path = path {
for i in 0..<path.count() {
self.add(path.coordinate(at: i))
}
}
}
}
I am using the code below to retrieve data from MySQL to show multiple locations on MKMapView using Swift.
The data and locations shows on the map, but what I could not figure out is how to adjust the zoom to cover all locations in that area.
func parseJSON(_ data:Data) {
var jsonResult = NSArray()
do {
jsonResult = try JSONSerialization.jsonObject(with: data, options:JSONSerialization.ReadingOptions.allowFragments) as! NSArray
} catch let error as NSError {
print(error)
}
var jsonElement = NSDictionary()
let locations = NSMutableArray()
for i in 0 ..< jsonResult.count
{
jsonElement = jsonResult[i] as! NSDictionary
let location = LocationModel()
//the following insures none of the JsonElement values are nil through optional binding
if let evIdL = jsonElement["id"] as? String,
let evUserNameL = jsonElement["username"] as? String,
let evNotikindL = jsonElement["notikind"] as? String,
let evLatiL = jsonElement["lati"] as? String,
let evLongiL = jsonElement["longi"] as? String,
let evLocatL = jsonElement["locat"] as? String,
let evTimedateL = jsonElement["timedate"] as? String,
let evDistanceL = jsonElement["distance"] as? String
{
location.evId = evIdL
location.evUsername = evUserNameL
location.evNotikind = evNotikindL
location.evLati = evLatiL
location.evLongi = evLongiL
location.evLocat = evDistanceL
location.evTimedate = evTimedateL
location.evDisatnce = evDistanceL
location.evLocat = evLocatL
// the code to show locations
let latiCon = (location.evLati as NSString).doubleValue
let longiCon = (location.evLongi as NSString).doubleValue
let annotations = locations.map { location -> MKAnnotation in
let annotation = MKPointAnnotation()
annotation.title = evNotikindL
annotation.coordinate = CLLocationCoordinate2D(latitude:latiCon, longitude: longiCon)
return annotation
}
self.map.showAnnotations(annotations, animated: true)
self.map.addAnnotations(annotations)
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.itemsDownloaded(items: locations)
})
}
I am using PHP file to connect with MySQL, as I said the code working and showing the locations but the zoom focus on one location only.
You can try
DispatchQueue.main.async {
self.map.addAnnotations(annotations)
self.map.showAnnotations(annotations, animated: true)
// make sure itemsDownloaded needs main ??
self.itemsDownloaded(items: locations)
}
I have this code but for some reason it's just drawing a rout between 2 points (first, and last points) ignoring all other points which is [index == 1 to index == n-1 ]
output : route between only 2 markers
expected output : route between all markers (5 markers)
Is any body knows what is the wrong with my code ?
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)"
}
print("this is fullPath :: \(wayPoints)")
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, "stopover": "true", "key" : kyes.google_map]
Alamofire.request(request, method:.get, parameters : parameters).responseJSON(completionHandler: { response in
guard let dictionary = response.result.value as? [String : AnyObject]
else {
return
}
print ("route iss ::: \(dictionary["routes"])")
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(path: GMSMutablePath(fromEncodedPath: points))
}
}
}
let polyline = GMSPolyline.init(path: fullPath)
polyline.path = fullPath
polyline.strokeWidth = 4.0
polyline.map = self._map }
}
}
}
}
})
}
}
Check out this solution working fine with me
func drawpath(positions: [CLLocationCoordinate2D]) {
let origin = positions.first!
let destination = positions.last!
var wayPoints = ""
for point in positions {
wayPoints = wayPoints.characters.count == 0 ? "\(point.latitude),\(point.longitude)" : "\(wayPoints)%7C\(point.latitude),\(point.longitude)"
}
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin!.latitude),\(origin!.longitude)&destination=\(destination.latitude),\(destination.longitude)&mode=driving&waypoints=\(wayPoints)&key=KEY"
Alamofire.request(url).responseJSON { response in
print(response.request as Any) // original URL request
print(response.response as Any) // HTTP URL response
print(response.data as Any) // server data
print(response.result as Any) // result of response serialization
let json = try! JSON(data: response.data!)
let routes = json["routes"][0]["overview_polyline"]["points"].stringValue
let path = GMSPath.init(fromEncodedPath: routes)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor.red
polyline.map = self._map
}
}
try with:
func getPolylineRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D){
self.mapView.clear()
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=YOUR_API_KEY")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print("Error" + 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 {
return
}
print(routes)
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!)
let distancia = overview_polyline?["legs"] as? NSArray
let prueba = distancia![0] as? NSDictionary
let distancia2 = prueba?["distance"] as? NSDictionary
let control = distancia2?.object(forKey: "text") as! String
DispatchQueue.main.async {
let bounds = GMSCoordinateBounds(coordinate: source, coordinate: destination)
let update = GMSCameraUpdate.fit(bounds, with: UIEdgeInsetsMake(170, 30, 30, 30))
self.mapView!.moveCamera(update)
}
}
}
}
catch {
print("error in JSONSerialization")
}
}
})
task.resume()
}
You call the method with
self.getPolylineRoute(from: origin, to: destination)
Remember in the google url to put your api key where it says YOUR_API_KEY
You can use this function for drawing the polyline which covers your waypoints.
Make sure that positions doesn't have same waypoint coordinates. If
so, it will just draw the polylines from source to destination
func drawPolyLine(for positions: [CLLocationCoordinate2D]) {
let origin = positions.first!
let destination = positions.last!
var wayPoints = ""
for point in positions {
wayPoints = wayPoints.count == 0 ? "\(point.latitude),\(point.longitude)" : "\(wayPoints)%7C\(point.latitude),\(point.longitude)"
}
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin.latitude),\(origin.longitude)&destination=\(destination.latitude),\(destination.longitude)&mode=driving&waypoints=\(wayPoints)&key=\(YOUR_GOOLE_MAP_API_KEY)"
Alamofire.request(url).responseJSON { response in
do {
let json = try JSONSerialization.jsonObject(with: response.data!, options: .allowFragments) as? [String: Any]
guard let routes = json?["routes"] as? NSArray else {
return
}
for route in routes {
let routeOverviewPolyline: NSDictionary = ((route as? NSDictionary)!.value(forKey: "overview_polyline") as? NSDictionary)!
let points = routeOverviewPolyline.object(forKey: "points")
let path = GMSPath.init(fromEncodedPath: (points! as? String)!)
let polyline = GMSPolyline.init(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor(red: 95/255.0, green: 233/255.0, blue: 188/255.0, alpha: 1.0)
polyline.map = self.mapView
}
} catch {
print("Unexpected Error")
}
}
}
I am new to swift and Maps. I am facing problem with displaying user live location. I have to display user location like Uber and Ola. I am getting array of coordinates from server.
This is the way i am fetching coordinates from server. I want to show moving user location. see following my code.
func SetUpMapsUI()
{
AdminAPIManager.sharedInstance.getAdminRunningStatusFromURL(){(resignationsJson)-> Void in
let swiftyJsonVar = JSON(resignationsJson)
let status = swiftyJsonVar["status"].rawString() as! String
print("status",status)
let message = swiftyJsonVar["message"].rawString()
if status.isEqual("0"){
if (message?.isEqual("No trips done so far."))!
{
self.mapViewBottomCons.constant = 0
}else
{
self.mapViewBottomCons.constant = 70
}
self.Bottom_view.isHidden = true
Toast.short(message: message as! String)
return
}
let busVar = swiftyJsonVar["bus_details"].rawString()!
let jsonData = busVar.data(using: .utf8)!
let LocationArray = try? JSONSerialization.jsonObject(with: jsonData, options: []) as! Array< Any>
for data in LocationArray!
{
let dic = data as! NSDictionary
guard let lat = dic.value(forKey: "latitude") as? Double else {
return
}
print("latlatlatlat",lat)
guard let lon = dic.value(forKey: "longitude") as? Double else {
return
}
print("longitude",lon)
self.arrayMapPath.append(NewMapPath(lat: Double(lat), lon: Double(lon)))
}
if self.arrayMapPath.count > 0
{
self.drawPathOnMap()
}
}
here is library that does that functionality it is written for both swift and ObjC..
ARCarMovement
here is a Stackoverflow SO answer
Here,
i have source latitude & longitude
in the same way i also have destination latitude & longitude
Now, i want to show the path between these two lat & long's
sourcelatitude = 12.9077869892472
sourcelongitude = 77.5870421156287
print(sourcelatitude!)
print(sourcelongitude!)
destinationlatitude = 12.907809
destinationlongitude = 77.587066
print(destinationlatitude!)
print(destinationlongitude!)
Could any one help me with this
Try this
let origin = "\(12.9077869892472),\(77.5870421156287)"
let destination = "\(12.907809),\(77.587066)"
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)")
URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in
if(error != nil){
print("error")
}else{
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
if json["status"] as! String == "OK"
{
let routes = json["routes"] as! [[String:AnyObject]]
OperationQueue.main.addOperation({
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"] as! [String:String]
let points = routeOverviewPolyline["points"]
let path = GMSPath.init(fromEncodedPath: points!)
let polyline = GMSPolyline(path: path)
polyline.strokeColor = .blue
polyline.strokeWidth = 4.0
polyline.map = mapViewX//Your GMSMapview
}
})
}
}catch let error as NSError{
print(error)
}
}
}).resume()
var aPosition = "30.9621,40.7816"
let bPosition = "26.9621,75.7816"
let urlString = "https://maps.googleapis.com/maps/api/directions/json?origin=\(aPosition)&destination=\(bPosition)&key=\(your google key)"
//let urlString = "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=\(aPosition)&destinations=\(bPosition)&key=AIzaSyCaSIYkkv41RTn5vFLiSoZFlCUhIg-Db5c"
print(urlString)
Alamofire.request(urlString)
.responseJSON { response in
if let array = json["routes"] as? NSArray {
if let routes = array[0] as? NSDictionary{
if let overview_polyline = routes["overview_polyline"] as? NSDictionary{
if let points = overview_polyline["points"] as? String{
print(points)
// Use DispatchQueue.main for main thread for handling UI
DispatchQueue.main.async {
// show polyline
let path = GMSPath(fromEncodedPath:points)
self.polyline.path = path
self.polyline.strokeWidth = 4
self.polyline.strokeColor = UIColor.blue
self.polyline.map = self.mapView
}
}
}
}
}