How can I remove the GMSPolyline in Swift 3 - google-maps

I'm making the GMSPolyline in the GoogleMap. When I want to remove the GMSPolyline by coding polyline.map = nil, but it can not working for me. I'm copy some coding in the below.
(added the marker before, I need remove the polyline only)
Thank you for your help!!!
my coding:
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
//MARK: create the GMSPolyline
let polyline = GMSPolyline()
//MARK: remove the old polyline from the GoogleMap
polyline.map = nil
let origin = "\(mapView.myLocation!.coordinate.latitude),\(mapView.myLocation!.coordinate.longitude)"
let destination = "\(marker.position.latitude),\(marker.position.longitude)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving"
Alamofire.request(url).responseJSON { response in
let json = JSON(data: response.data!)
let routes = json["routes"].arrayValue
//MARK: print route using Polyline
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
let path = GMSPath(fromEncodedPath: points!)
polyline = GMSPolyline(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor.yellow
polyline.isTappable = true
polyline.map = self.mapView
}
}
return false
}

you should clear the map before adding new polyline on the map.
clear() function of GMSMapView
/**
Clears all markup that has been added to the map, including markers, polylines and ground overlays.
This will not clear the visible location dot or reset the current mapType.
*/
Try this
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
//MARK: create the GMSPolyline
let polyline = GMSPolyline()
//MARK: remove the old polyline from the GoogleMap
mapView.clear()
//TODO:- Redraw all marker here.
let origin = "\(mapView.myLocation!.coordinate.latitude),\(mapView.myLocation!.coordinate.longitude)"
let destination = "\(marker.position.latitude),\(marker.position.longitude)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving"
Alamofire.request(url).responseJSON { response in
let json = JSON(data: response.data!)
let routes = json["routes"].arrayValue
//MARK: print route using Polyline
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
let path = GMSPath(fromEncodedPath: points!)
polyline = GMSPolyline(path: path)
polyline.strokeWidth = 4
polyline.strokeColor = UIColor.yellow
polyline.isTappable = true
polyline.map = self.mapView
}
}
return false
}

Declare polyline outside the function as a class property.
var routePolyline: GMSPolyline? = nil
Then to clear old polyline before adding new one, use-
self.routePolyline?.map = nil
This will work as expected!

for poll in mapkit.overlays {
mapkit.removeOverlay(poll)
}

var polyline: GMSPolyline?
func drawPathOnMap() {
if polyline != nil {
//remove existing the gmspoly line from your map
polyline!.map = nil
}
let points = //Your Point
let path = GMSPath(fromEncodedPath: points)
polyline = GMSPolyline(path: path)
polyline?.strokeWidth = 5.0
polyline?.strokeColor = UIColor.init(rgb: 0x7688ED)
polyline!.map = mapView
}

Related

Parse results from JSON to a list dynamically

I do a JSON Request and get some information.
func parseJSON(poiData: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(POIData.self, from: poiData)
POIManager.POIname_One = decodedData.results[0].name
POIManager.POIplaceid_One = decodedData.results[4].place_id
POIManager.POIvicinity_One = decodedData.results[4].vicinity
POIManager.POIlong_One = decodedData.results[0].geometry.location.lat
POIManager.POIlat_One = decodedData.results[0].geometry.location.lng
POIManager.POIname_Two = decodedData.results[1].name
POIManager.POIplaceid_Two = decodedData.results[1].place_id
POIManager.POIvicinity_Two = decodedData.results[1].vicinity
POIManager.POIlong_Two = decodedData.results[1].geometry.location.lat
POIManager.POIlat_Two = decodedData.results[1].geometry.location.lng
POIManager.POIname_Three = decodedData.results[2].name
POIManager.POIplaceid_Three = decodedData.results[2].place_id
POIManager.POIvicinity_Three = decodedData.results[2].vicinity
POIManager.POIlong_Three = decodedData.results[2].geometry.location.lat
POIManager.POIlat_Three = decodedData.results[2].geometry.location.lng
} catch {
print(error)
}
}
In a different Swift file it put the results from the request in a list like this:
#IBAction func kategorieEins(_ sender: UIButton) {
//Eigene Standort soll hier gezeigt werden/aktualisierter Standort
locationManager.delegate=self
let marker1 = GMSMarker()
marker1.position = CLLocationCoordinate2D(latitude: POIManager.POIlong_One, longitude: POIManager.POIlat_One)
marker1.title = POIManager.POIname_One
marker1.snippet = "Marker1_0"
marker1.map = mapView
let marker2 = GMSMarker()
marker2.position = CLLocationCoordinate2D(latitude: POIManager.POIlong_Two, longitude: POIManager.POIlat_Two)
marker2.title = POIManager.POIname_Two
marker2.snippet = "Marker2_0"
marker2.map = mapView
let marker3 = GMSMarker()
marker3.position = CLLocationCoordinate2D(latitude: POIManager.POIlong_Three, longitude: POIManager.POIlat_Three)
marker3.title = POIManager.POIname_Three
marker3.snippet = "Marker3_0"
marker3.map = mapView
}
As you can see this whole thing is not dynamic it is static. I write down how many markers i want to have created.
Is there a way to do this automatically? Especially when I dont know how much information there is in the json file and how many markers should be created.
I solved it now.
I tried:
decodedData.results.forEach {
print($0.name)
print($0.place_id)
}

Path not show in google map in swift 3

I want to show path in google map. But i am unable to show my path. Please help me . This is my code. I am try lot of time in swift. Any help would be appreciated. Thanks in advance.
let camera = GMSCameraPosition.camera(withLatitude: 48.4843822562792, longitude: 35.0635632500052, zoom: 6)
let mapView = GMSMapView.map(withFrame: .zero, camera: camera)
self.view = mapView
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
manager.responseSerializer.acceptableContentTypes = nil
let urlString = "https://maps.googleapis.com/maps/api/directions/json?origin=48.4843822562792,35.0635632500052&destination=48.4893899423081,35.0640017911792&waypoints=48.4833428800255,35.0710221379995|48.4887622031403,35.0573639944196&key=AIzaSyDvS9TbaQ1WzP_3YX6TBrjoTNGhnFa0MBY"
let urlwithPercentEscapes = urlString.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)
manager.get(urlwithPercentEscapes!, parameters: nil, success: { (operation, response) in
let parsedData = JSON(response)
var path = GMSPath.init(fromEncodedPath: parsedData["routes"][0]["overview_polyline"]["points"].string!)
//GMSPath.fromEncodedPath(parsedData["routes"][0]["overview_polyline"]["points"].string!)
print(path!)
var singleLine = GMSPolyline.init(path: path)
singleLine.strokeWidth = 7
singleLine.strokeColor = UIColor.green
singleLine.map = self.mapView
}, failure: { (operation, error) in
})

Draw polylines between two or more marker on GMSMapView with Swift3

I tried to implement differents solutions for draw polyLines between markers on my GMSMapView but is not working. Do you have others solutions or maybe do you know what's wrong in my code ?
override func viewDidLoad() {
super.viewDidLoad()
let path = GMSMutablePath()
_data = _modelDelivery.getGarageFromDeliver(refDelivery: _delivery._ref)
let camera = GMSCameraPosition.camera(withLatitude: 48.853183, longitude: 2.369144, zoom: 13.0)
self._mapView.camera = camera
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: 48.8531827, longitude: 2.3691443000000163)
path.add(marker.position)
marker.title = "Ma position"
marker.icon = GMSMarker.markerImage(with: UIColor.blue)
marker.map = self._mapView
for elem in _data {
let address = elem._adress + ", " + elem._city + ", " + String(elem._zipCode) + ", France"
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) {
placemarks, error in
let placemark = placemarks?.first
let lat = placemark?.location?.coordinate.latitude
let lon = placemark?.location?.coordinate.longitude
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: lat!, longitude: lon!)
path.add(marker.position)
marker.title = elem._nameOfGarage
marker.snippet = elem._adress + "\nOrdre de passege : "
marker.map = self._mapView
}
}
let rectangle = GMSPolyline(path: path)
rectangle.strokeWidth = 4
rectangle.strokeColor = UIColor.red
rectangle.map = self._mapView
}
I believe you have configured everything fine in https://console.developers.google.com.
We can draw a polyline between two coordinates. You need to create your ServerKey on https://console.developers.google.com where you have created your project for GoogleMaps and then use that Server Key in the URL below.
This draws polyline between two coordinates.
func drawPath() {
//Taken two source and destinations coordinates. Take the coordinates in this way. "Lat,Long" in a String
let sourceString = "28.6562,77.2410"
let destinationString = "27.1750,78.0422"
//GoogleMaps Web API to draw the polyline
let combinedUrl : String = "https://maps.googleapis.com/maps/api/directions/json?" + "origin=\(sourceString)&destination=\(destinationString)&key=YOUR_SERVER_KEY"
let url = URL(string:combinedUrl)
let task = URLSession.shared.dataTask(with: url!) { (data:Data?, response:URLResponse?, error:Error?) in
if error != nil {
print(error.debugDescription)
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary
//We need to get to the points key in overview_polyline object in order to pass the points to GMSPath.
let route = (((json.object(forKey: "routes") as! NSArray).object(at: 0) as! NSDictionary).object(forKey: "overview_polyline") as! NSDictionary).value(forKey: "points") as! String
//Draw on main thread always else it will crash
DispatchQueue.main.async {
let path = GMSPath(fromEncodedPath:route)!
let polyline = GMSPolyline(path: path)
polyline.strokeColor = UIColor.green
polyline.strokeWidth = 5.0
//mapView is your GoogleMaps Object i.e. _mapView in your case
polyline.map = self.mapView
}
} catch {
}
}
task.resume()
}
JSON points key data required.
Later on you can animate to the drawn region first coordinate using the GMSCameraPosition.
Similarly for multiple points, just pass the respective coordinates in a for loop and call drawPath function.
You can have a look at this Video Tutorial too.

Google Driections API return an array from the closure in Swift 3

Im new to Swift and I'm trying to use the Google Directions API. I have modified a function to get a Polyline and put the Google directions into an array. The array of directions I declared as a property of the class I'm working in so I can display the results in a table. I have checked and the array is be properly populated with the directions inside the closure. However when I try to use it outside the closure in the CellForTableAT function it is empty. I'm probably not getting the data out of the closures correctly but I cant figure it out. I begin building my array at //MARK: Start Building Directions Array using swiftyJason
func getDirections(currentDestination: GMSMarker, origin: String!, destination: String!, waypoints: Array<String>!, mode: String!, completionHandler: ((_ status: String, _ success: Bool) -> Void)?) {
if let originLocation = origin {
if let destinationLocation = destination {
var directionsURLString = baseURLDirections + "origin=" + originLocation + "&destination=" + destinationLocation + "&mode=" + mode
if let routeWaypoints = waypoints {
directionsURLString += "&waypoints=optimize:true"
for waypoint in routeWaypoints {
directionsURLString += "|" + waypoint
}
}
directionsURLString = ("\(directionsURLString)&sensor=true&key=???????????????")
print("directons*******")
print(directionsURLString)
directionsURLString = directionsURLString.addingPercentEscapes(using: String.Encoding.utf8)!
let directionsURL = NSURL(string: directionsURLString)
DispatchQueue.main.async( execute: { () -> Void in
let directionsData = NSData(contentsOf: directionsURL! as URL)
do{
let dictionary: Dictionary<String, AnyObject> = try JSONSerialization.jsonObject(with: directionsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! Dictionary<String, AnyObject>
let status = dictionary["status"] as! String
let json = JSON(data: directionsData as! Data)
if status == "OK" {
self.selectedRoute = (dictionary["routes"] as! Array<Dictionary<String, AnyObject>>)[0]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<String, AnyObject>
let legs = self.selectedRoute["legs"] as! Array<Dictionary<String, AnyObject>>
let startLocationDictionary = legs[0]["start_location"] as! Dictionary<String, AnyObject>
self.originCoordinate = CLLocationCoordinate2DMake(startLocationDictionary["lat"] as! Double, startLocationDictionary["lng"] as! Double)
let endLocationDictionary = legs[legs.count - 1]["end_location"] as! Dictionary<String, AnyObject>
self.destinationCoordinate = CLLocationCoordinate2DMake(endLocationDictionary["lat"] as! Double, endLocationDictionary["lng"] as! Double)
let originAddress = legs[0]["start_address"] as! String
let destinationAddress = legs[legs.count - 1]["end_address"] as! String
for (index, leg) in json["routes"][0]["legs"].arrayValue.enumerated() {
var count = 0
//MARK: Start Building Directions Array
for (stepIndex, step) in json["routes"][0]["legs"][index]["steps"].arrayValue.enumerated() {
count += 1
let htmlInstructions = json["routes"][0]["legs"][index]["steps"][stepIndex]["html_instructions"].string
let distance = json["routes"][0]["legs"][index]["steps"][stepIndex]["distance"]["text"].string
let duration = json["routes"][0]["legs"][index]["steps"][stepIndex]["duration"]["text"].string
let direction:Direction = Direction(index: count, htmlInstructions: htmlInstructions, distance: distance, duration: duration)
self.directions.append(direction)
}
self.tableView.reloadData()
}
//end of stepts to get writtine directions
//NOT Plotting markers endpoins
//position markers for ployline endpoints
//let originMarker = GMSMarker(position: self.originCoordinate)
// originMarker.map = self.mapView
//originMarker.icon = UIImage(named: "mapIcon")
// originMarker.title = originAddress
self.destinationMarker = currentDestination
// destinationMarker.map = self.mapView
// destinationMarker.icon = UIImage(named: "mapIcon")
// destinationMarker.title = destinationAddress
// destinationMarker.icon = GMSMarker.markerImage(with: UIColor.green)
if waypoints != nil && waypoints.count > 0 {
for waypoint in waypoints {
let lat: Double = (waypoint.components(separatedBy: ",")[0] as NSString).doubleValue
let lng: Double = (waypoint.components(separatedBy: ",")[1] as NSString).doubleValue
let marker = GMSMarker(position: CLLocationCoordinate2DMake(lat, lng))
marker.map = self.mapView
marker.icon = UIImage(named: "mapIcon")
}
}
self.routePolyline.map = nil
let route = self.overviewPolyline["points"] as! String
let path: GMSPath = GMSPath(fromEncodedPath: route)!
self.routePolyline = GMSPolyline(path: path)
self.routePolyline.map = self.mapView
self.routePolyline.strokeColor = UIColor.red
self.routePolyline.strokeWidth = 3.0
//Fit map to entire polyline
var bounds = GMSCoordinateBounds()
for index in 1...path.count() {
bounds = bounds.includingCoordinate(path.coordinate(at: index))
}
self.mapView.animate(with: GMSCameraUpdate.fit(bounds))
// end of fit map to ployline
}
else {
print("status of poly draw")
//completionHandler(status: status, success: false)
}
}
catch {
print("catch")
// completionHandler(status: "", success: false)
}
})
}
else {
print("Destination is nil.")
//completionHandler(status: "Destination is nil.", success: false)
}
}
else {
print("Origin is nil")
//completionHandler(status: "Origin is nil", success: false)
}
}
I added a return (->([Direction]))to the closure and was able to access the data outside of it.
func getDirections(currentDestination: GMSMarker, origin: String!, destination: String!, waypoints: Array<String>!, mode: String!, completionHandler: ((_ status: String, _ success: Bool) -> Void)?) -> ([Direction])
I'm new so not sure if its best practice to do it that way, but I needed data to display in a table.

Accessing Variables outside of a function in swift

Im getting coordinates from , but I'd like to be able to use them outside the Location Manager Function. Is it possible to make them instance variables? If so, could someone give me some example code for that? In the code below I print within the func and it works, but I'd like to be able to, for example print outside it, and otherwise use the coordinates. Perhaps as a global variable? How would i code that? I cant quite make it out from the documentation.
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
var locationManager: CLLocationManager!
var seenError : Bool = false
var theSpan: MKCoordinateSpan = MKCoordinateSpanMake(0.1, 0.1)
var locationStatus : NSString = "Not Started"
var initialLoc:Int = 1
override func viewDidLoad() {
super.viewDidLoad()
locationManager = CLLocationManager()
locationManager.requestAlwaysAuthorization()
locationManager.delegate = self
var initialLoc:Int = 1
// Do any additional setup after loading the view, typically from a nib.
}
#IBOutlet weak var Map: MKMapView!
#IBAction func Here(sender: AnyObject) {
initLocationManager()
}
func initLocationManager() {
seenError = false
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.startUpdatingLocation()
Map.showsUserLocation = true
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
var coord = locationObj.coordinate
if initialLoc == 1 {
var Region:MKCoordinateRegion = MKCoordinateRegionMake(coord, theSpan)
self.Map.setRegion(Region, animated:true)
initialLoc = 0
}
println(coord.latitude)
println(coord.longitude)
}
// Location Manager Delegate stuff
// If failed
func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
locationManager.stopUpdatingLocation()
if (error) {
if (seenError == false) {
seenError = true
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If I understand you correctly, I think the best option would be to make it an instance variable, as you said. This would be done by declaring it outside of the function with your other instance variables at the top of your class (the asterisks are used to show you what I added):
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
var locationManager: CLLocationManager!
var seenError : Bool = false
var theSpan: MKCoordinateSpan = MKCoordinateSpanMake(0.1, 0.1)
var locationStatus : NSString = "Not Started"
var initialLoc:Int = 1
// Declare coordinate variable
***var coord: CLLocationCoordinate2D?***
The question mark declares the variable as an optional, so you don't have to immediately assign a value to it. Then, you assign the locationObj.coordinate value to coord in your locationManager function, however since you already declared the coord variable outside your function as an instance variable you can remove the var in var coord = locationObj.coordinate :
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var locationArray = locations as NSArray
var locationObj = locationArray.lastObject as CLLocation
//Assign value to coord variable
***coord = locationObj.coordinate***
if initialLoc == 1 {
var Region:MKCoordinateRegion = MKCoordinateRegionMake(coord, theSpan)
self.Map.setRegion(Region, animated:true)
initialLoc = 0
}
Then you can use the variable normally in the rest of your function, in addition to any other function in the class (like a global variable).
Best of luck!
P.S. Learn how to do this well, as it is a method used all the time when working with functions and variables
At the point where you read the location into a local variable, instead read it into an instance variable. So instead of:
var locationObj = locationArray.lastObject as CLLocation
use
self.locationObj = locationArray.lastObject as CLLocation
and declare
var locationObj : CLLocation?
in your ViewController. Then you can access locationObj with 'dot' notation.