How to set google map zoom level according to path & markers in swift - swift

I am trying to show path in google maps from one place to another place I am getting something like this. but I need to show like this. Which means the whole path need to show & according to path on map, the zooming level should adjust
Here is the code which I tried to draw path from API. and here in let settingCam am setting camera to adjust to one of the location
func showingPathFromPickupLocToDropLoc(dropLat: Double, dropLong: Double){
let origin = "\(dropLat),\(dropLong)"
let destination = "\(dropLatitude),\(dropLongitude)"
let settingCam: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: CLLocationDegrees(dropLat), longitude: CLLocationDegrees(dropLong))
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&key=\(NEWAPI.GOOGLE_APIKEY)")
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!)
self.PathFromPickupLocToDropLoc = GMSPolyline(path: path)
self.PathFromPickupLocToDropLoc.strokeColor = .gray
self.PathFromPickupLocToDropLoc.strokeWidth = 3.0
self.PathFromPickupLocToDropLoc.map = self.mapView
let camera = GMSCameraPosition.camera(withTarget: settingCam, zoom: 16.0)
self.mapView.animate(toLocation: settingCam)
self.mapView.animate(to: camera)
self.insertingMarkersFromPickupLocToDropLoc(dropLat: dropLat, dropLong: dropLong)
}
})
}
}catch let error as NSError{
print(error)
}
}
}).resume()
}

You need to do like this
DispatchQueue.main.async
{
if self.googleMap != nil
{
let bounds = GMSCoordinateBounds(path: path!)
self.googleMap!.animate(with: GMSCameraUpdate.fit(bounds, withPadding: 50.0))
}
}

Related

How to draw routes on GMSmap view swift (not straight) using google api Swift

I am trying to draw route on mapview using the google direction API . I am using the solution from the stackoverflow itself but i am getting some errors regarding intializers .
Also will the directions route will be same as google map itself or a straight one .
Any help is Appreciated. Also where should i call this methods
I am getting error as
Cannot invoke initializer for type 'GMSCoordinateBounds' with an argument list of type '(coordinate: String, String, coordinate:
String, String)'
Below is the Code .
func getRouteSteps(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D) {
let session = URLSession.shared
let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(lat),\(long)&destination=\(directionlat),\(directionlong)&sensor=false&mode=driving&key=\(API KEY)")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
guard error == nil else {
print(error!.localizedDescription)
return
}
guard let jsonResult = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] else {
print("error in JSONSerialization")
return
}
guard let routes = jsonResult!["routes"] as? [Any] else {
return
}
guard let route = routes[0] as? [String: Any] else {
return
}
guard let legs = route["legs"] as? [Any] else {
return
}
guard let leg = legs[0] as? [String: Any] else {
return
}
guard let steps = leg["steps"] as? [Any] else {
return
}
for item in steps {
guard let step = item as? [String: Any] else {
return
}
guard let polyline = step["polyline"] as? [String: Any] else {
return
}
guard let polyLineString = polyline["points"] as? String else {
return
}
//Call this method to draw path on map
DispatchQueue.main.async {
self.drawPath(from: polyLineString)
}
}
})
task.resume()
}
Function to draw polyline
func drawPath(from polyStr: String){
let mapView: GMSMapView
let path = GMSPath(fromEncodedPath: polyStr)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 3.0
polyline.map = mapView // Google MapView
//
let cameraUpdate = GMSCameraUpdate.fit(GMSCoordinateBounds(coordinate: "\(lat)","\(long)", coordinate: "\(directionlat)","\(directionlong)")) as? [String : AnyObject]
mapView.moveCamera(cameraUpdate)
let currentZoom = mapView.camera.zoom
mapView.animate(toZoom: currentZoom - 1.4)
}
GMSCoordinatesBounds takes CLLocationCoordinates2D type as parameter, not String.
replace
let cameraUpdate = GMSCameraUpdate.fit(GMSCoordinateBounds(coordinate: "\(lat)","\(long)", coordinate: "\(directionlat)","\(directionlong)")) as? [String : AnyObject]
with
let cameraUpdate = GMSCameraUpdate.fit(GMSCoordinateBounds(coordinate: CLLocationCoordinate2D(latitude: Double(lat), longitude: Double(long)), coordinate: CLLocationCoordinate2D(latitude: Double(directionlat), longitude: Double(directionlat))))
and once you have added the mapView to your view controller and got the coordinates, call your function
self.getRouteSteps(from source: CLLocationCoordinate2D(latitude: Double(lat), longitude: Double(long)), destination: CLLocationCoordinate2D(latitude: Double(directionlat), longitude: Double(directionlat)))
You can try this, use the below to fetch direction:
//This function is used to fetch the directions from origin to destination
private func fetchDirection(destinationLat: CLLocationDegrees, destinationLong: CLLocationDegrees) {
//Here you need to set your origin and destination points and mode
if let location = locationManager.location {
guard let url = URL(string: "\("https://maps.googleapis.com/maps/api/directions/json")?origin=\(location.coordinate.latitude),\(location.coordinate.longitude)&destination=\(destinationLat),\(destinationLong)&key=\(Constants.MapKey)") else { return }
let task = URLSession.shared.dataTask(with: url) { [unowned self](data, response, error) -> Void in
do {
guard let data = data else { return }
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
guard let mapData = try? decoder.decode(MapModel.self, from: data) else { return }
if let points = mapData.routes.first?.overviewPolyline.points {
self.drawRoute(points: points)
}
}
}
task.resume()
}
}
Use this to draw the route on the map:
//This function is used to draw the routes
private func drawRoute(points: String) {
let path = GMSPath.init(fromEncodedPath: points)
let singleLine = GMSPolyline.init(path: path)
self.polylines.append(singleLine)
singleLine.strokeWidth = 6.0
let gradientColor: GMSStrokeStyle = GMSStrokeStyle.gradient(from: .red, to: .blue)
singleLine.spans = [GMSStyleSpan.init(style: gradientColor)]
if self.polylines.count > 0 {
self.polylines.forEach{ $0.map = nil }
}
singleLine.map = self.mapView
}
And, here is the MapModel
struct MapModel: Decodable {
let status: String
let routes: [Routes]
}
struct Routes: Decodable {
let overviewPolyline: OverviewPolyline
}
struct OverviewPolyline: Decodable {
let points: String
}
I hope you are familiar with Codables and also, I have called the drawRoute function when I received the points.

route between two markers in google maps - Swift

I want him to draw the route between the two coordinates and go to the route, but he doesn't draw the route. It prints out success but cannot draw the route. What is the problem? How can I draw two routes on the map and show the details of this route?
func drawPath()
{
let kordinatgetir = keychain.get("uyeKordinat")
let doubleKordinat :Double = Double(kordinatgetir!)!
let kordinatgetir1 = keychain.get("uyeKordinat1")
let doubleKordinat1 :Double = Double(kordinatgetir1!)!
let origin = "\(doubleKordinat),\(doubleKordinat1)"
let destination = "\(doubleKordinat1),\(doubleKordinat)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving&key=..."
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
do {
let json = try 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
}
} catch {
print(error)
}
}
}
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=Your Key") else {
let error = NSError(domain: "LocalDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Failed to create object URL"])
print("Error: \(error)")
//completionHandler(nil, error)
return
}
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
SVProgressHUD.show()
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
if error != nil {
print(error!.localizedDescription)
SVProgressHUD.dismiss()
}
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 {
SVProgressHUD.dismiss()
}
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 duration = legs[0]["duration"] as? NSDictionary
let totalDurationInSeconds = duration?["value"] as? Int ?? 0
let miles = Double(distanceValue) / 1609.344
print("\(miles)")
if distanceValue > Int(32186.9){
SVProgressHUD.dismiss()
self.showAlert(title: Appname, message: "Your dropping point is more than 20 miles")
self.txtToLocation.text = ""
self.txtToLocation.becomeFirstResponder()
}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: "icn_pin-1")
marker1.map = self.mapView
let marker2 = GMSMarker()
marker2.position = CLLocationCoordinate2D(latitude:originCoordinate.latitude, longitude: originCoordinate.longitude)
marker2.icon = UIImage(named: "icn_pin2")
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.UIColorFromHex(hex: "#F6881F")
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)
}
}

Can't get directions from GoogleMaps because of "found nil" unrapping URL

I searched through this topic and found some codes I tried to implement into my project, but it won't work!
So, what do I wanna achieve?
I wanna have a button in the UI, and when user tap the button, the app displays directions to a specific point on the GoogleMap. But my function crashes on the URL.
This is my code:
func draw(src: CLLocationCoordinate2D, dst: CLLocationCoordinate2D){
let urlString = "https://maps.googleapis.com/maps/api/directions/json?origin=\(src)&destination=\(dst)&sensor=false&mode=driving&key=**API_KEY**" <- // Here I place API-Key
let url = URL(string: urlString) // Here is the crash!
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]
let routes = json["routes"] as! NSArray
self.mapView.clear()
OperationQueue.main.addOperation({
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 = 3
let bounds = GMSCoordinateBounds(path: path!)
self.mapView!.animate(with: GMSCameraUpdate.fit(bounds, withPadding: 30.0))
polyline.map = self.mapView
}
})
}catch let error as NSError{
print("error:\(error)")
}
}
}).resume()
}
I don't know if the problem could be with the API-key, or if there's something else. I read that spaces etc could cause this issue, but I can't find what's wrong!
Error message:
Fatal error: Unexpectedly found nil while unwrapping an Optional value
2019-06-14 16:50:45 Fatal error: Unexpectedly found nil while unwrapping an Optional value
You wrongly create urlString with directly using CLLocationCoordinate2D as you have to use it's properties latitude/longitude
let urlString = "https://maps.googleapis.com/maps/api/directions/json?origin=\(src)&destination=\(dst)&sensor=false&mode=driving&key=**API_KEY**" <- // Here I place API-Key
it should be
let urlString = "https://maps.googleapis.com/maps/api/directions/json?origin=\(src.latitude),\(src.longitude)&destination=\(dst.latitude),\(dst.longitude)&sensor=false&mode=driving&key=**API_KEY**" <- // Here I place API-Key
Also it's better to avoid ! and do
guard let url = URL(string: urlString) else { return }

how to move marker along polyline in swift

i had obtained the start and end locations through the json, but when i loop through to draw the animation ,it takes only last point of the loop and the marker moves in a straight line
func playAnimation() {
let origin = "\(self.locationManager.location?.coordinate.latitude ?? 0),\(self.locationManager.location?.coordinate.longitude ?? 0)"
let destination = "\(latitude),\(longitude)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving&key=googleApi"
print(url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
guard let requestUrl = URL(string :url) else { return }
let request = URLRequest(url:requestUrl)
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
DispatchQueue.main.async {
if error != nil {
print(error!.localizedDescription)
}else{
do {
if let json : [String:Any] = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]{
if let routes = json["routes"] as? [Any] {
//print(routes)
if let route = routes[0] as? [String: Any] {
//print(route)
if let legs = route["legs"] as? [Any]{
// print(legs)
if let leg = legs[0] as? [String: Any]{
// print(leg)
if let steps = leg["steps"] as? [Any]{
// print("steps:\(steps)")
for step in steps {
//print(step)
let endLocation:NSDictionary = (step as! NSDictionary).value(forKey: "end_location") as! NSDictionary
print("endLocation is : \(endLocation)")
let endLocationLatitude = endLocation.object(forKey: "lat") as! CLLocationDegrees
print(endLocationLatitude)
let endLocationLongitude = endLocation.object(forKey: "lng") as! CLLocationDegrees
print(endLocationLongitude)
let startLocation:NSDictionary = (step as! NSDictionary).value(forKey: "start_location") as! NSDictionary
print("startLocation is : \(startLocation)")
let startLocationLatitude = startLocation.object(forKey: "lat") as! CLLocationDegrees
print(startLocationLatitude)
let startLocationLongitude = startLocation.object(forKey: "lng") as! CLLocationDegrees
print(startLocationLongitude)
// let destinationLocation = CLLocationCoordinate2DMake(19.0178967, 72.8558875)
let destinationLocation = CLLocationCoordinate2DMake(endLocationLatitude,endLocationLongitude)
print("destinationLocation:\(destinationLocation)")
let startLocationDestination = CLLocationCoordinate2DMake(startLocationLatitude, startLocationLongitude)
//self.updateMarker(startlocation: startLocationDestination, endlocation: destinationLocation)
//self.updateMarker(coordinates: destinationLocation)
CATransaction.begin()
CATransaction.setAnimationDuration(5.0)
self.marker.position = destinationLocation
self.marker.map = self.mapView
self.delay(2, closure: {})
// Center Map View
let camera = GMSCameraUpdate.setTarget(destinationLocation)
self.mapView.animate(with: camera)
CATransaction.commit()
}
}
}
}
}
}
}
}catch{
print("error in JSONSerialization")
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
}
}
}
}
})
task.resume()
}
You can move your marker using below code. You need to pass the position and the marker has to be present on the map. If marker is not there you first need to create it and then move
let position = CLLocationCoordinate2D(latitude:
self.locationManager.location?.coordinate.latitude ?? 0.0, longitude: self.locationManager.location?.coordinate.longitude ?? 0.0)
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
myMarker?.position = position
myMarker?.map = mapView
myMarker?.appearAnimation = .pop
CATransaction.commit()

How to refresh the markers on google maps, without refreshing the map using swift

Am showing some vehicles on google maps(latitudes & longitudes am getting from API).
I am getting latitude & longitudes from storingDataOftripLocDetails() method. And am calling that method for every 2 seconds.
After getting lat & long am showing one marker. and here because of timer my google maps is also refreshing. But my requirement is i have to refresh marker only.
how should i achieve this?
var driverLatitude: Double?
var driverLongitude: Double?
Here in the following method am calling timer
override func viewDidLoad() {
self.tripLocDetailsTimer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(tripLocDetailCustomrWebserviceCall), userInfo: nil, repeats: true)
}
Here am getting data from API. and calling showingThePath()
func storingDataOftripLocDetails(_ data: TripLocationDetailsResponse){
self.driverLatitude = data.locationDetails?.driverDetails?.pickupLatitude
self.driverLongitude = data.locationDetails?.driverDetails?.pickupLongitude
self.showingThePath()
}
Here in the following method am showing 1 marker & showing the path
func showingThePath(){
let vehicleLocation = CLLocationCoordinate2D(latitude: CLLocationDegrees(self.driverLatitude!), longitude: CLLocationDegrees(self.driverLongitude!))
let cabIM = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 15))
cabIM.image = UIImage(named: "bike_color")
let vehicleImage = GMSMarker()
vehicleImage.iconView = cabIM
vehicleImage.infoWindowAnchor = CGPoint(x: 0.44, y: 0.40)
vehicleImage.tracksViewChanges = false
vehicleImage.position = vehicleLocation
vehicleImage.map = self.mapView
let origin = "\(String(describing: driverLatitude)),\(String(describing: driverLongitude))"
let destination = "\(String(describing: pickupLatitude!)),\(String(describing: pickupLongitude!))"
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){
}else{
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
print(json)
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!)
self.path = GMSPolyline(path: path)
self.path.strokeColor = .gray
self.path.strokeWidth = 3.0
self.path.map = self.mapView
}
})
}
}catch let error as NSError{
print(error)
}
}
}).resume()
}