Maps Direction Line doesn't showing up on Maps - ios

On trying to give direction line between User location to defined location. The pinpoints were right and exactly at the desired location but there is no 'Line' direction which leads User location to defined location.
It was done like this image:
and my code for drawing the maps along with parsing google maps API JSON:
func DrawDirection(url: String, cord: CLLocationCoordinate2D){
request(url).responseJSON { response in
let parsed = try? JSONSerialization.jsonObject(with: response.data!, options: []) as! [String:Any]
let routes = parsed!["routes"] as! [[String:Any]]
for route in routes {
let dictArr = route["legs"] as? [[String:Any]]
let dict = dictArr![0]["steps"] as? [[String:Any]]
let start = dict![0]["start_location"] as? [String:Any]
let lat = start!["lat"] as! Double
let long = start!["lng"] as! Double
let dotcoordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(lat), longitude: CLLocationDegrees(long))
let routePoly = route["overview_polyline"] as! [String:Any]
let points = routePoly["points"] as! String
let line = points
DispatchQueue.main.async {
let bounds = GMSCoordinateBounds(coordinate: cord, coordinate: dotcoordinate)
let update = GMSCameraUpdate.fit(bounds, with: UIEdgeInsetsMake(170, 30, 30, 30))
self.CinemaLoc.moveCamera(update)
}
self.AddingPolyLine(encString: line, dir: cord, dot: dotcoordinate)
}
}
}
func AddingPolyLine(encString: String, dir: CLLocationCoordinate2D, dot: CLLocationCoordinate2D){
let dotpath: GMSMutablePath = GMSMutablePath()
dotpath.add(CLLocationCoordinate2DMake(dir.latitude, dir.longitude)) //original loc
dotpath.add(CLLocationCoordinate2DMake(dot.latitude, dot.longitude)) //starting points
let dotpoly = GMSPolyline(path: dotpath)
dotpoly.map = self.CinemaLoc
dotpoly.strokeWidth = 3.0
let styles:[Any] = [GMSStrokeStyle.solidColor(.blue), GMSStrokeStyle.solidColor(.clear)]
let lengths:[Any] = [10,5]
dotpoly.spans = GMSStyleSpans(dotpoly.path!,styles as!
[GMSStrokeStyle], lengths as! [NSNumber], GMSLengthKind.rhumb)
let poly = GMSPolyline(path: dotpath)
poly.strokeWidth = 3.0
poly.strokeColor = .red
poly.map = self.CinemaLoc
}
cord is User location
been trying all possible way on stack overflow but not getting any change and sometimes there are log saying "Failed to load optimized model at path ~/GoogleMaps.bundle/GMSCacheStorage.momd/"
is there any wrong with my code?

Path should be created only once, for a specific path, and then you can add coordinates to the path. You're doing wrong by putting let dotpath: GMSMutablePath = GMSMutablePath() inside your method call, which gets called for every loop.

Sorry guys, problem fixed. there are misplaced on my url. and for adding polyline should be like this
let path = GMSMutablePath(fromEncodedPath: "String from overview json")
let poly = GMSPolyline(path: dotpath)
poly.strokeWidth = 3.0
poly.strokeColor = .red
poly.map = self.CinemaLoc
On routes loop, it should be no problem since google maps api only give 1 array of route, but for better practice. It should out of loop
Thank you and happy coding!

Related

GoogleMaps : How to draw dash line to connect with starting point of direction path on google map ios

I am using google map in the my project for direction & show route. I had done all things but I am not able to draw the dash line to connect with starting point of route from my location or building.
See my app screenshot screenshot 1
Now I want to draw dash line like google-maps screenshot 2
Any advice and suggestions will be greatly appreciated
For add dash line to connect with starting & end point you have to do little more stuffs in direction path json response, I have drawn straight dash polyline somewhere, if you got code related curved line then update your answer also,
Russell suggested link will little helpful to you,
Drawing Route Between Two Places on GMSMapView in iOS
You have to do following stuffs,
func drawRoute(coordinate: CLLocationCoordinate2D) {
// let directionURL = "https://maps.googleapis.com/maps/api/directions/json?" +
// "origin=\(19.0760),\(72.8777)&destination=\(18.520),\(72.9781)&" +
// "key=AIzaSyBdzgSm8g6w3daxTQvtlG9pqBxrj3lkaN0"
//
var directionURL = "https://maps.googleapis.com/maps/api/directions/json?origin=\(coordinate.latitude),\(coordinate.longitude)&destination=\(18.5767),\(73.6881)&key=AIzaSyARoB09HGFjDy3IKfLpZq-ZQd3YwUT-3_E"
//AIzaSyDxSgGQX6jrn4iq6dyIWAKEOTneZ3Z8PtU
directionURL += "&mode=" + "walking"
print("drawRoute")
Alamofire.request(directionURL).responseJSON
{ response in
if let JSON = response.result.value {
let mapResponse: [String: AnyObject] = JSON as! [String : AnyObject]
let routesArray = (mapResponse["routes"] as? Array) ?? []
let routes = (routesArray.first as? Dictionary<String, AnyObject>) ?? [:]
//print("routes : \(routes)")
//--------Dash line lat-long for starting point ----------\\
let dictArray = (routes["legs"] as? Array) ?? []
let dict = (dictArray.first as? Dictionary<String, AnyObject>) ?? [:]
let steps = (dict["steps"] as? Array) ?? []
let stepsDict = (steps.first as? Dictionary<String, AnyObject>) ?? [:]
let startLocation = stepsDict["start_location"]
let lat = startLocation!["lat"] as! NSNumber
let lng = startLocation!["lng"] as! NSNumber
print("lat : \(lat) lng : \(lng)")
let dotCoordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(lat), longitude: CLLocationDegrees(lng))
//--------Route polypoints----------\\
let overviewPolyline = (routes["overview_polyline"] as? Dictionary<String,AnyObject>) ?? [:]
let polypoints = (overviewPolyline["points"] as? String) ?? ""
let line = polypoints
self.addPolyLine(encodedString: line, coordinate:coordinate , dotCoordinate:dotCoordinate)
}
}
}
Now draw route polyline & dash line like following way,
func addPolyLine(encodedString: String, coordinate: CLLocationCoordinate2D ,dotCoordinate : CLLocationCoordinate2D) {
//--------Dash line to connect starting point---------\\
let dotPath :GMSMutablePath = GMSMutablePath()
// add coordinate to your path
dotPath.add(CLLocationCoordinate2DMake(coordinate.latitude, coordinate.longitude))
dotPath.add(CLLocationCoordinate2DMake(dotCoordinate.latitude, dotCoordinate.longitude))
let dottedPolyline = GMSPolyline(path: dotPath)
dottedPolyline?.map = self.viewMap
dottedPolyline?.strokeWidth = 3.0
let styles: [Any] = [GMSStrokeStyle.solidColor(UIColor.green), GMSStrokeStyle.solidColor(UIColor.clear)]
let lengths: [Any] = [10, 5]
dottedPolyline?.spans = GMSStyleSpans(dottedPolyline?.path!, styles as! [GMSStrokeStyle], lengths as! [NSNumber], kGMSLengthRhumb)
//---------Route Polyline---------\\
let path = GMSMutablePath(fromEncodedPath: encodedString)
let polyline = GMSPolyline(path: path)
polyline?.strokeWidth = 5
polyline?.strokeColor = .blue
polyline?.map = self.viewMap
}
See output:
create a MKPolyLine like this
var pointsToUse: [CLLocationCoordinate2D] = []
pointsToUse += [CLLocationCoordinate2DMake(55.4, -3.2)] // current location
pointsToUse += [CLLocationCoordinate2DMake(55.6, -3.4)] // lat lon of starting point
let polyline = MKPolyline(coordinates: &pointsToUse, count: pointsToUse.count)
mapView.add(polyline, level: MKOverlayLevel.aboveRoads)
and then update the MapView renderer like this
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer
{
if (overlay is MKPolyline)
{
let pr = MKPolylineRenderer(overlay: overlay)
pr.strokeColor = UIColor.blue.withAlphaComponent(0.5)
pr.lineWidth = 2
pr.lineDashPattern = [10, 10]
return pr
}
}

Google Maps iOS Reverse Geocoding

I am having an issue with Google Maps Reverse Geocoding when it comes to setting a UILabel's text to the reverse geocoded address. The UILabel is in an XIB use as a custom infowindow. I have another custom infowindow for other data that is working correctly however, it appears that when I try to set the label's text within the reverse geocode callback / completion handler, it doesn't work. Here is the code I have so far and I have tried the code multiple ways, including assigning it to variables and I have not been able to get anything to work.
let infoWindow = NSBundle.mainBundle().loadNibNamed("InfoWindowCurrent", owner: self, options: nil)[0] as! InfoWindowCurrent
let geocoder = GMSGeocoder()
let coordinate = CLLocationCoordinate2DMake(Double(self.locLatitude)!, Double(self.locLongitude)!)
var currentAddress = String()
geocoder.reverseGeocodeCoordinate(coordinate) { response , error in
if let address = response?.firstResult() {
let lines = address.lines! as [String]
currentAddress = lines.joinWithSeparator("\n")
}
}
infoWindow.labelAddressStreet.text = currentAddress
return infoWindow
I have also tried this:
let infoWindow = NSBundle.mainBundle().loadNibNamed("InfoWindowCurrent", owner: self, options: nil)[0] as! InfoWindowCurrent
let geocoder = GMSGeocoder()
let coordinate = CLLocationCoordinate2DMake(Double(self.locLatitude)!, Double(self.locLongitude)!)
geocoder.reverseGeocodeCoordinate(coordinate) { response , error in
if let address = response?.firstResult() {
let lines = address.lines! as [String]
infoWindow.labelAddressStreet.text = lines.joinWithSeparator("\n")
}
}
return infoWindow
Everything is connected correctly because the following code works:
let infoWindow = NSBundle.mainBundle().loadNibNamed("InfoWindowCurrent", owner: self, options: nil)[0] as! InfoWindowCurrent
let geocoder = GMSGeocoder()
let coordinate = CLLocationCoordinate2DMake(Double(self.locLatitude)!, Double(self.locLongitude)!)
geocoder.reverseGeocodeCoordinate(coordinate) { response , error in
if let address = response?.firstResult() {
let lines = address.lines! as [String]
}
}
infoWindow.labelAddressStreet.text = "Unable to reverse geocode location!"
return infoWindow
Any help is definitely appreciated!
You are using a return out of the braces of the geocoding, for this reason the last code works. You should do something like this:
func getAddress(currentAdd : ( returnAddress :String)->Void){
let infoWindow = NSBundle.mainBundle().loadNibNamed("InfoWindowCurrent", owner: self, options: nil)[0] as! InfoWindowCurrent
let geocoder = GMSGeocoder()
let coordinate = CLLocationCoordinate2DMake(Double(self.locLatitude)!,Double(self.locLongitude)!)
var currentAddress = String()
geocoder.reverseGeocodeCoordinate(coordinate) { response , error in
if let address = response?.firstResult() {
let lines = address.lines! as [String]
currentAddress = lines.joinWithSeparator("\n")
currentAdd(returnAddress: currentAddress)
}
}
}
And call that function
getAddress() { (returnAddress) in
print("\(returnAddress)")
}
So, I ended up going another route. It's not pretty, but it works. Each GMSMarker has a "userData" attribute that can be used to pass data. What I did was moved the marker creation into the reverse geocode completion handler and assigned the address to the "userData" attribute. Then, when the user taps to show the current address, the reverse geocode is kicked off, the marker is created and placed on the map.
geocoder.reverseGeocodeCoordinate(position) { response, error in
if let location = response?.firstResult() {
let marker = GMSMarker(position: position)
let lines = location.lines! as [String]
marker.userData = lines.joined(separator: "\n")
marker.title = lines.joined(separator: "\n")
marker.infoWindowAnchor = CGPoint(x: 0.5, y: -0.25)
marker.accessibilityLabel = "current"
marker.map = self.mapView
self.mapView.animate(toLocation: position)
self.mapView.selectedMarker = marker
}
}
And when the marker is selected, the label is set to the address as passed in the "userData" attribute:
let infoWindow = Bundle.main.loadNibNamed("InfoWindowCurrent", owner: self, options: nil)?[0] as! InfoWindowCurrent
infoWindow.labelAddress.text = marker.userData as? String
return infoWindow
The first response worked for me. As an alternative to
marker.title = lines.joined(separator: "\n")`
try this, it will give you the street address:
marker.title = response?.firstResult()?.thoroughfare
marker.snippet = response?.firstResult()?.locality
For the city, try
marker.snippet = response?.firstResult()?.locality

Cannot assign value of type '[String]' to type 'String? Swift 2

I am receiving this error message "Cannot assign value of type '[String]' to type 'String?'" associated with this line "point.title = r" and this line "point.subtitle = z". I've tried lots of things like place.description which makes the entire array a string...
//I have multiple arrays, lat, long and pass besides place.
var place = [String]()
//I have four of the chunks of code for each array
let json = try NSJSONSerialization.JSONObjectWithData(jSONData, options: .AllowFragments)
if let dataFile3 = json["data"] as? [[String: AnyObject]] {
for c in dataFile3 {
if let placeName = c["PlaceName"] {
place.append((placeName as? String)!)
for var (i,x) in zip(lat, long) {
for _ in place {
for _ in pass {
print(i)
print(x)
//print(i + ", " + x)
// String to double conversion, can I do this when I set the array as a string?
let lati = NSString(string: i).doubleValue
let longi = NSString(string: x).doubleValue
//I have a list of values array so I need to have a for loop that inputs the value over and over again.
var point = MGLPointAnnotation()
point.coordinate = CLLocationCoordinate2D(latitude: lati, longitude: longi)
point.title = place
point.subtitle = pass
mapView.addAnnotation(point)
}}}
//var i and x for loop
}
//viewDidLoad
}
//creates the markers popup for each point with data
func mapView(mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
// Always try to show a callout when an annotation is tapped.
return true
}
How do I fix the error above?
EDIT:
With Santosh's help this is what I changed...
let point = MGLPointAnnotation()
var i = lat
var x = long
//lat and long is served and then all the r,z points, then another lat, long...need to fix this and it may work.
for (i,x) in zip(lat, long) {
for aPlace in place {
for aPass in pass {
print(i)
print(x)
// String to double conversion, can I do this when I set the array as a string?
let lati = NSString(string: i).doubleValue
let longi = NSString(string: x).doubleValue
point.coordinate = CLLocationCoordinate2D(latitude: lati, longitude: longi)
point.title = aPlace
print(aPlace)
point.subtitle = aPass
print(aPass)
self.mapView.addAnnotation(point)
}}}
This is running correctly now or at least reporting the correct values but the loop just keeps a loopin...
Try this:
//I have four of the chunks of code for each array
let json = try NSJSONSerialization.JSONObjectWithData(jSONData, options: .AllowFragments)
if let dataFile3 = json["data"] as? [[String: AnyObject]] {
for c in dataFile3 {
if let placeName = c["PlaceName"] {
place.append((placeName as? String)!)
for var (i,x) in zip(lat, long) {
for aPlace in place {
for aPass in pass {
print(i)
print(x)
//print(i + ", " + x)
// String to double conversion, can I do this when I set the array as a string?
let lati = NSString(string: i).doubleValue
let longi = NSString(string: x).doubleValue
//I have a list of values array so I need to have a for loop that inputs the value over and over again.
var point = MGLPointAnnotation()
point.coordinate = CLLocationCoordinate2D(latitude: lati, longitude: longi)
point.title = aPlace
point.subtitle = aPass
mapView.addAnnotation(point)
}}}
//var i and x for loop
}
//viewDidLoad
}
}

Remove Object From Array Using Enumeration

I have an array of dictionaries. Each dictionary contains latitude and longitude so I'm getting the distance of each item from the current user location. If the distance in miles is greater than 20, that particular dictionary should be removed from the array. If the dictionary is not removed from the array, an annotation is created and added to an annotation array which is then used to add annotations to a map once the enumeration is finished. I'm only getting one annotation added when I should be getting three so I know I'm doing something wrong in my enumeration.
func checkDistanceAndAddPins() {
for gym in gyms {
var index = 0
let gymLatitude = gym["latitude"]!!.doubleValue
let gymLongitude = gym["longitude"]!!.doubleValue
let gymLocation = CLLocation(latitude: gymLatitude, longitude: gymLongitude)
let distance = gymLocation.distanceFromLocation(myLocation!)
let distanceInMeters = NSNumber(double: distance)
let metersDouble = distanceInMeters.doubleValue
let miles = metersDouble * 0.00062137
if miles > maxDistance {
gyms.removeAtIndex(index)
} else {
let location = CLLocationCoordinate2D(latitude: gymLatitude, longitude: gymLongitude)
gymAnnotation.title = gym["Name"] as? String
gymAnnotation.subtitle = gym["Address"] as? String
gymAnnotation.coordinate = location
gymAnnotation.gymPhoneNumber = gym["Phone"] as? String
if let website = gym["Website"] as? String {
gymAnnotation.gymWebsite = website
}
gymLocations.append(gymAnnotation)
}
index += 1
}
dispatch_async(dispatch_get_main_queue()) {
self.gymMap.addAnnotations(self.gymLocations)
}
}

How do I iterate through JSON co-ordinates and build annotations as one function?

I am struggling to get the annotations being placed using JSON data. I have tried iterating the coordinates from the JSON into a new array but when I try pass an array to where I need the coordinates it fails because it cannot take arrays. How can I fix this?
Can anyone help?
Alamofire.request(.GET, "https://demo1991046.mockable.io/score/locations").responseJSON { (responseData) -> Void in
let swiftyJsonVar = JSON(responseData.result.value!)
if let resData = swiftyJsonVar["users"].arrayObject as? [NSArray] {
self.newArray = (resData as? [NSArray])
}
print("\([self.newArray])")
for var i = 0; i < self.newArray!.count; ++i {
self.longitude.append(self.newArray[i]["lon"] as! String!)
print("longitude: \(self.longitude)")
self.latitude.append(self.newArray[i]["lat"] as! String!)
print("latitude: \(self.latitude)")
}
let doubleLat = self.latitude.map {
Double(($0 as NSString).doubleValue)
}
let doubleLon = self.longitude.map {
Double(($0 as NSString).doubleValue)
}
print("doublelat: \(doubleLat)")
print("doubleLon: \(doubleLon)")
// 1
self.locationManager.delegate = self
// 2
self.locationManager.requestAlwaysAuthorization()
// 3
let theSpan:MKCoordinateSpan = MKCoordinateSpanMake(0.01 , 0.01)
let location:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: doubleLat, longitude: doubleLon) // <- here is where I get an error: "Cannot convert value of type '[Double]' to expect argument type 'CLLocationDegrees' (aka 'Double")"
// print("lat: \((locationManager.location?.coordinate.latitude)!)")
// print("lon: \((locationManager.location?.coordinate.longitude)!)")
let theRegion:MKCoordinateRegion = MKCoordinateRegionMake(location, theSpan)
self.mapView.setRegion(theRegion, animated: true)
let anotation = MKPointAnnotation()
anotation.coordinate = location
anotation.title = "The Location"
anotation.subtitle = "This is the location !!!"
self.mapView.addAnnotation(anotation)
}
}
I have done soem modifies below to your code
Didn't convert the json to NSArray (by using .array instead of .arrayObject)
moved adding anotation to the map inside the for loop to add all of them.
Moved setting a region to the map out side the for loop and left it to you to set the location you like.
Alamofire.request(.GET, "https://demo1991046.mockable.io/score/locations").responseJSON { (responseData) -> Void in
let swiftyJsonVar = JSON(responseData.result.value!)
// get the users from the json var, no need to convert it to Array
guard let usersJsonArray = swiftyJsonVar["users"].array else {
// users not found in the json
return
}
// the usersJsonArray is array of json which will be much easier for work with.
// No need for 1,2 and 3 to be in the for loop.
// 1
self.locationManager.delegate = self
// 2
self.locationManager.requestAlwaysAuthorization()
// 3
let theSpan:MKCoordinateSpan = MKCoordinateSpanMake(0.01 , 0.01)
for userJson in usersJsonArray {
let longitudeString = userJson["lon"].stringValue
print("longitude: \(longitudeString)")
let latitudeString = userJson["lat"].stringValue
print("latitude: \(latitudeString)")
let doubleLat = Double(latitudeString)
let doubleLon = Double(longitudeString)
print("doublelat: \(doubleLat)")
print("doubleLon: \(doubleLon)")
// by having the next code block inside the for loop you will be able to add all the user locations to the map as anotations.
let location:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: doubleLat, longitude: doubleLon) // Now should work fine
let anotation = MKPointAnnotation()
anotation.coordinate = location
anotation.title = "The Location"
anotation.subtitle = "This is the location !!!"
self.mapView.addAnnotation(anotation)
} // for usersJson
// you need to figure out the loaction you will set for the mapView region.
let location = .... // set the location you like.
let theRegion:MKCoordinateRegion = MKCoordinateRegionMake(location, theSpan)
self.mapView.setRegion(theRegion, animated: true)
}

Resources