Showing multiple location using 'MKMapView' - ios

I am trying to show multiple locations which saved in Mysql using the code below. The data is loading but I have no idea how to show multiple locations depending on latitude and longitude.
Mysql is connected to application via PHP file.
Here is my code, the part which I called from NSObject:
func downloadItems() {
// the download function
// return the nsuserdefaults which hold the lati and longi from the notification table
UserDefaults.standard.string(forKey: "test");
let myUrl = UserDefaults.standard.string(forKey: "test");
let urlPath: String = myUrl!
let url: URL = URL(string: urlPath)!
let defaultSession = Foundation.URLSession(configuration: URLSessionConfiguration.default)
let task = defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON(data!)
}
}
task.resume()
}
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
}
locations.add(location)
}
DispatchQueue.main.async(execute: { () -> Void in
self.delegate.itemsDownloaded(items: locations)
})
}
}
I have no idea how to show few location on map.

try this code....
var locations = NSMutableArray()
var mapView = GMSMapView()
for i in 0..< location.count{
let obj = location[i]
lat = obj["lati"] as? Double
lng = obj["longi"] as? Double
let markerPoint = GMSMarker()
markerPoint.position = CLLocationCoordinate2D(latitude: lat!, longitude: lng!)
markerPoint.iconView = self.avtarImage() // Your image name
markerPoint.map = mapView // your mapview object name
markerPoint.zIndex = Int32(i)
markerPoint.infoWindowAnchor = CGPoint(x: 0, y: 0)
markerPoint.accessibilityLabel = String(format: "%d", i)
}

Related

Zoom on multiple locations using 'MKMapView'

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)
}

'NSRangeException', reason: [__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray -> it is keep getting out of index bounds

[["The Cheesecake Factory", 3.8], ["Dave & Buster\'s", 3.8], ["Paul Martin\'s American Grill", 4.2], ["Yard House", 4.3], ["Javier\'s Restaurant - Irvine", 4], ["CUCINA enoteca Irvine", 4.4], ["SUBWAY®Restaurants", 3.2], ["SUBWAY®Restaurants", 4], ["Wendy\'s", 3.8], ["Izakaya Wasa", 3.7], ["Veggie Grill", 4.5], ["Bruegger\'s Bagels", 4.5], ["Capital Seafood Restaurant - Irvine Spectrum", 4], ["Burger King", 3.5], ["SUBWAY®Restaurants", 2.6], ["Corner Bakery Cafe", 3.9], ["Taiko Japanese Restaurant", 4.3], ["Red Robin Gourmet Burgers", 3.7], ["Johnny Rockets", 2.9], ["Chipotle Mexican Grill", 4]]
20
2017-11-15 13:26:36.367072-0800 E-Bike App[2584:1830704] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
*** First throw call stack:
(0x183ef5d04 0x183144528 0x183e510c4 0x102debe4c 0x102cafc84 0x184480f94 0x1844983b4 0x1848c1310 0x1848019e4 0x1847f1620 0x10532545c 0x105331b74 0x10532545c 0x105331b74 0x105331a34 0x1848c2fe8 0x10532545c 0x105332800 0x10533109c 0x105336b54 0x105336880 0x183b1f120 0x183b1ec20)
libc++abi.dylib: terminating with uncaught exception of type NSException'
Once I get the place name, rating and location (CLLocationCoordinate2D) above and save in the double array and parse index and location to drawPolylineAmongMultiplePoints function to find the distance and duration and append or push to the double array. NSRangeException is keep poping up..
Anyone has good idea to solve this?
func drawPolylineAmongMultiplePoints(coordinate: CLLocationCoordinate2D, pinPoint: Int) {
guard let lat = mapView.myLocation?.coordinate.latitude else {return}
guard let long = mapView.myLocation?.coordinate.longitude else {return}
let aPointCoordinate = "\(lat),\(long)"
let bPointCoordinate = "\(coordinate.latitude),\(coordinate.longitude)"
let url = "http://maps.googleapis.com/maps/api/directions/json?origin=\(aPointCoordinate)&destination=\(bPointCoordinate)&sensor=false&mode=\(DrivingMode.DRIVING)"
guard let urlString = URL(string: url) else {
print("Error: Cannot create URL")
return
}
let urlRequest = URLRequest(url: urlString)
// Set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// Make the request
let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
do{
if error != nil{
print("Error: \(String(describing: error?.localizedDescription))")
} else {
guard let data = data else {
throw JSONError.NoData
}
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary else {
throw JSONError.ConversionFailed
}
let arrayRoutes = json["routes"] as! NSArray
let dicOfPoints = arrayRoutes[0] as! NSDictionary
let dic1 = dicOfPoints["overview_polyline"] as! NSDictionary
let points = dic1["points"] as! String
let arrayLegs = (arrayRoutes[0] as! NSDictionary).object(forKey: "legs") as! NSArray
let arraySteps = arrayLegs[0] as! NSDictionary
let dicDistance = arraySteps["distance"] as! NSDictionary
let totalDistance = dicDistance["text"] as! String
self.totalremainingDistance = (dicDistance["value"] as! Double)*(1/1000)*(1.61)
let dicDuration = arraySteps["duration"] as! NSDictionary
let totalDuration = dicDuration["text"] as! String
self.totalremainingDuration = dicDuration["value"] as! Double
let position = CLLocationCoordinate2D(latitude: coordinate.latitude, longitude: coordinate.longitude)
//self.eachCarouselDataDic[index] += [totalDistance, totalDuration]
//print(self.eachCarouselDataDic)
self.eachCarouselDataDic[pinPoint] += [totalDistance, totalDuration, points, position]
//print(self.eachCarouselDataDic)
}
}catch let error as JSONError {
print(error.rawValue)
}catch let error as NSError {
print(error.debugDescription)
}
})
task.resume()
}
#objc func POIForPlaces(sender: UIButton) {
print("I am here~~~")
//mapView.clear()
//For Carousel view to have access to the POI elements
isThisFirstTime = false
self.eachCarouselDataDic.removeAll()
var typeOfPlace = String()
var markerImage = UIImage()
switch sender.tag {
case 0:
typeOfPlace = "cafe"
markerImage = UIImage(named: "cafe")!
case 1:
typeOfPlace = "restaurant"
markerImage = UIImage(named: "restaurant")!
default:
break
}
let markerView = UIImageView(image: markerImage)
guard let lat = mapView.myLocation?.coordinate.latitude else {return}
guard let long = mapView.myLocation?.coordinate.longitude else {return}
var arrayOfLocations = [CLLocationCoordinate2D()]
arrayOfLocations.removeFirst()
var arrayOfNames = [String()]
arrayOfNames.removeFirst()
var arrayOfAddress = [String()]
arrayOfAddress.removeFirst()
var arrayOfRating = [Double()]
arrayOfRating.removeFirst()
var name = String()
var counter = 0
let jsonURLString = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(lat),\(long)&maxprice=3&radius=3200&opennow&type=\(typeOfPlace)&key=\(Config.GOOGLE_API_KEY)"
guard let urlString = URL(string: jsonURLString) else {
print("Error: Cannot create URL")
return
}
//markerView.tintColor = UIColor.DTIBlue()
let urlRequest = URLRequest(url: urlString)
// Set up the session
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest) { (data, response, error) in
guard let httpResponse = response as? HTTPURLResponse else { return }
switch httpResponse.statusCode {
case 200:
do{
guard let data = data else { return }
guard let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary else { return }
//print(json)
DispatchQueue.global(qos: .background).async {
let arrayPlaces = json["results"] as! NSArray
if arrayPlaces.count > 0 {
for i in 0..<arrayPlaces.count {
print("-----------------------------------------------------------------------------------------------------")
print(arrayPlaces.count)
print("-----------------------------------------------------------------------------------------------------")
self.eachCarouselDataDic.append([])
let arrayForLocations = (((arrayPlaces[i] as! NSDictionary).object(forKey: "geometry") as! NSDictionary).object(forKey: "location") as! NSDictionary)
let lat = arrayForLocations.object(forKey: "lat")
let long = arrayForLocations.object(forKey: "lng")
let position = CLLocationCoordinate2D(latitude: lat as! CLLocationDegrees, longitude: long as! CLLocationDegrees)
let arrayForName = (arrayPlaces[i] as! NSDictionary).object(forKey: "name") as! String
let arrayForAddress = (arrayPlaces[i] as! NSDictionary).object(forKey: "vicinity") as! String
if let arrayForRating = (arrayPlaces[i] as! NSDictionary).object(forKey: "rating") as? NSNumber {
arrayOfRating.append(Double(truncating: arrayForRating).rounded(toPlaces: 1))
self.eachCarouselDataDic[i] += [arrayForName, arrayForRating]
print(self.eachCarouselDataDic)
} else {
arrayOfRating.append(0.0)
self.eachCarouselDataDic[i] += [arrayForName,0.0]
print(self.eachCarouselDataDic)
}
print("-----------------------------------------------------------------------------------------------------")
print(self.eachCarouselDataDic.count)
print("-----------------------------------------------------------------------------------------------------")
arrayOfNames.append(arrayForName)
arrayOfAddress.append(arrayForAddress)
arrayOfLocations.append(position)
//self.eachCarouselDataDic[i] += [arrayForName,arrayForRating]
//self.drawPolylineAmongMultiplePoints(coordinate: position, index: counter)
counter += 1
let nearbyMarker = GMSMarker()
//var position = CLLocationCoordinate2D()
nearbyMarker.iconView = markerView
name = arrayOfNames[i]
nearbyMarker.tracksViewChanges = true
nearbyMarker.title = name
nearbyMarker.position = position
nearbyMarker.snippet = "Rating = \(arrayOfRating[i]) \(self.ratingSystem(rating: arrayOfRating[i]))\n Address = \(arrayOfAddress[i])"
nearbyMarker.map = self.mapView
}
}
for i in 0..<counter {
self.drawPolylineAmongMultiplePoints(coordinate: arrayOfLocations[i], pinPoint: i)
}
}
}catch let error as NSError {
print(error.debugDescription)
}
default:
print("HTTP Reponse Code: \(httpResponse.statusCode)")
}
}
task.resume()
UIView.animate(withDuration: 3, animations: {
self.isCarouselActive = true
self.myLocationButton.setImage(UIImage(named: "carousel"), for: .normal)
})
}
The error tells you the problem:
index 0 beyond bounds for empty NSArray
So you have an empty array when you don't expect one.
Use the debugger and/or print() statements to examine the values of your variables. Work back from the point of failure to discover where it goes wrong.
You really need to do this yourself, you've provided a lot of code for people to work through. If you get stuck and cannot figure out the problem all a new question, include just sufficient code to show issue, explain what you've done and can't figure out, etc. Someone will undoubtedly help you at that point.

PolyLines not showing in google maps

I want show path between destination and source on google map. I am google direction api's getting route between of co-ordinates , I am getting response and set on google map but not showing on map . My code is
func getPolylineRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D){
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=AIzaSyAyU5txJ86b25-_l0DW-IldSKGGYqQJn3M")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
DispatchQueue.main.async {
if error != nil {
print(error!.localizedDescription)
AppManager.dissmissHud()
}
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 {
AppManager.dissmissHud()
}
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
self.showPath(polyStr: points!)
DispatchQueue.main.async {
AppManager.dissmissHud()
let bounds = GMSCoordinateBounds(coordinate: source, coordinate: destination)
let update = GMSCameraUpdate.fit(bounds, with: UIEdgeInsetsMake(75, 20, 20, 20))
self.vwMap!.moveCamera(update)
}
}
else {
DispatchQueue.main.async {
AppManager.dissmissHud()
}
}
}
}
catch {
print("error in JSONSerialization")
DispatchQueue.main.async {
AppManager.dissmissHud()
}
}
}
}
})
task.resume()
}
func drawPlyLineOnMap() {
let source : CLLocationCoordinate2D = CLLocationCoordinate2DMake(Double((model?.fromAddressLatitude)!), Double((model?.fromAddressLongtitude)!))
let destination : CLLocationCoordinate2D = CLLocationCoordinate2DMake(Double((model?.toAddressLatitude)!), Double((model?.toAddressLongtitude)!))
self.vwMap.clear()
//Source pin
let marker = GMSMarker()
let markerImage = UIImage(named: "from_pin")!.withRenderingMode(.alwaysOriginal)
let markerView = UIImageView(image: markerImage)
marker.position = source
marker.iconView = markerView
//marker.userData = dict
marker.map = vwMap
//Destination pin
let markerTo = GMSMarker()
let markerImageTo = UIImage(named: "to_red_pin")!.withRenderingMode(.alwaysOriginal)
let markerViewTo = UIImageView(image: markerImageTo)
markerTo.position = destination
// marker.userData = dict
markerTo.iconView = markerViewTo
markerTo.map = vwMap
var arrAdTemp:[AddressTableModel] = []
arrAdTemp.append(contentsOf: arrAddresses)
arrAdTemp.removeLast()
arrAdTemp.removeFirst()
for obj in arrAdTemp {
print(obj.strLatitude)
print(obj.strLongtitude)
let stopOver : CLLocationCoordinate2D = CLLocationCoordinate2DMake(obj.strLatitude, obj.strLongtitude)
let markerStop = GMSMarker()
let markerImageStop = UIImage(named: "to_red_pin")!.withRenderingMode(.alwaysOriginal)
let markerViewStop = UIImageView(image: markerImageStop)
markerStop.position = stopOver
//marker.userData = dict
markerStop.iconView = markerViewStop
markerStop.map = vwMap
}
self.getPolylineRoute(from: source, to: destination)
}
func showPath(polyStr :String){
let path = GMSPath(fromEncodedPath: polyStr)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 3.0
polyline.strokeColor = UIColor.black
polyline.map = vwMap // Your map view
}
I have tried lot of answer give below but not working for me. Please help me.
1st answer tried
2nd answer tried
3rd answer tried
you are setting wrong bounds so it is not showing on your map . I have tried your code it is working fine . Please change your bounds area as (0,0,0,0)
func getPolylineRoute(from source: CLLocationCoordinate2D, to destination: CLLocationCoordinate2D){
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=AIzaSyAyU5txJ86b25-_l0DW-IldSKGGYqQJn3M")!
let task = session.dataTask(with: url, completionHandler: {
(data, response, error) in
DispatchQueue.main.async {
if error != nil {
print(error!.localizedDescription)
AppManager.dissmissHud()
}
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 {
AppManager.dissmissHud()
}
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
self.showPath(polyStr: points!)
DispatchQueue.main.async {
AppManager.dissmissHud()
let bounds = GMSCoordinateBounds(coordinate: source, coordinate: destination)
//below bounds change as 0 check it on full screen
let update = GMSCameraUpdate.fit(bounds, with: UIEdgeInsetsMake(0, 0, 0, 0))
self.vwMap!.moveCamera(update)
}
}
else {
DispatchQueue.main.async {
AppManager.dissmissHud()
}
}
}
}
catch {
print("error in JSONSerialization")
DispatchQueue.main.async {
AppManager.dissmissHud()
}
}
}
}
})
task.resume()
}
I did the same using this code have a look.
{let url = URL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(self.currentLocation.coordinate.latitude),\(self.currentLocation.coordinate.longitude)&destination=\(33.6165),\(73.0962)&key=yourKey")
let request = URLRequest(url: url!)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
// notice that I can omit the types of data, response and error
do{
let json = JSON(data!)
let errornum = json["error"]
if (errornum == true){
}else{
let routes = json["routes"].array
if routes != nil && (routes?.count)! > 0{
let overViewPolyLine = routes![0]["overview_polyline"]["points"].string
let dict = routes![0].dictionaryValue
let distance = dict["legs"]?[0]["distance"]
_ = distance?["text"].stringValue
let duaration = dict["legs"]?[0]["duration"]
_ = duaration?["text"].stringValue
//dict["legs"]?["distance"]["text"].stringValue
print(overViewPolyLine!)
if overViewPolyLine != nil{
DispatchQueue.main.async() {
self.addPolyLineWithEncodedStringInMap(encodedString: overViewPolyLine!)
}
}
}
}
and then
{
func addPolyLineWithEncodedStringInMap(encodedString: String) {
let path = GMSPath(fromEncodedPath: encodedString)!
let polyLine = GMSPolyline(path: path)
polyLine.strokeWidth = 5
polyLine.strokeColor = UIColor.yellow
polyLine.map = self.googleMapsView
let center = CLLocationCoordinate2D(latitude: 33.6165, longitude: 73.0962)
let marker = GMSMarker(position: center)
marker.map = self.googleMapsView
}
func decodePolyline(encodedString: String){
let polyline = Polyline(encodedPolyline: encodedString)
let decodedCoordinates: [CLLocationCoordinate2D]? = polyline.coordinates
for coordinate in decodedCoordinates! {
let marker = GMSMarker(position: coordinate)
marker.icon = UIImage(named: "mapPin")
marker.map = self.googleMapsView
}
}

How should we show path between two latitudes & longitudes in google maps using Swift

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
}
}
}
}
}

Ambiguous reference to member 'subscript' when using Google maps route

The below code works fine in Xcode 7.1 but when I upgrade to Xcode 8 and swift 3 I get this error, I had searched on internet for same problem in stackoverflow but none of them answer my question.
func drawRoute() {
clearRoute()
// self.routePolyline = nil
// self.routePolyline.map = nil
let route = mapTk.overviewPolyline!["points"] as String //error here
//var overViewPolyLine = routes![0]["overview_polyline"]["points"].string
let path: GMSPath = GMSPath(fromEncodedPath: route)
routePolyline = GMSPolyline(path: path)
routePolyline.map = viewMap
}
here is the class mapTK for more info:
import Foundation
import UIKit
import CoreLocation
import MapKit
///This Class is designed for the detail methods that contact google maps server like GeoCode, GetDirection and Calculate distance and time
class MapTK: NSObject {
let baseURLGeocode = "https://maps.googleapis.com/maps/api/geocode/json?"
var lookupAddressResults: Dictionary<NSObject, AnyObject>!
var fetchedFormattedAddress: String!
var fetchedAddressLongitude: Double!
var fetchedAddressLatitude: Double!
let baseURLDirections = "https://maps.googleapis.com/maps/api/directions/json?"
var selectedRoute: Dictionary<NSObject, AnyObject>!
var overviewPolyline: Dictionary<NSObject, AnyObject>!
var originCoordinate: CLLocationCoordinate2D!
var destinationCoordinate: CLLocationCoordinate2D!
var originAddress: String!
var destinationAddress: String!
var totalDistanceInMeters: UInt = 0
var totalDistance: String!
var totalDurationInSeconds: UInt = 0
var totalDuration: String!
/* private let errorDictionary = ["NOT_FOUND" : "At least one of the locations specified in the request's origin, destination, or waypoints could not be geocoded",
"ZERO_RESULTS":"No route could be found between the origin and destination",
"MAX_WAYPOINTS_EXCEEDED":"Too many waypointss were provided in the request The maximum allowed waypoints is 8, plus the origin, and destination",
"INVALID_REQUEST":"The provided request was invalid. Common causes of this status include an invalid parameter or parameter value",
"OVER_QUERY_LIMIT":"Service has received too many requests from your application within the allowed time period",
"REQUEST_DENIED":"Service denied use of the directions service by your application",
"UNKNOWN_ERROR":"Directions request could not be processed due to a server error. Please try again"]
*/
override init() {
super.init()
}
///the geocodeAddress is getting information from googlemaps with JSON it receive the geocode information
func geocodeAddress(address: String!, withCompletionHandler completionHandler: #escaping ((_: String, _: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
//geocodeURLString = geocodeURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
let geocodeURL = NSURL(string: geocodeURLString)
// dispatch_Dispatch.Queue.mainasync(dispatch_get_main_queue(), { () -> Void in
DispatchQueue.main.async {
let geocodingResultsData = NSData(contentsOf: geocodeURL! as URL)
// let error: NSError?
do
{
let dictionary = try JSONSerialization.jsonObject(with: geocodingResultsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
// try NSJSONSerialization.JSONObjectWithData(directionsData!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
// Get the response status.
let status = dictionary?["status"]as! String
// let status = dictionary["status"] as! [AnyObject]
if status == "OK" {
let allResults = dictionary?["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"]? as? String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("No valid address.", false)
}
}
///getDirection method is using JSON to receive waypoints for making route for drawing polyline, as its name suggests it get the direciton inforamtion, first it send lat and lng the receive a dictionary and that dictionary give all the infrmation need to draw route like waypoints
func getDirections(origin: String!, destination: String!, waypoints: Array<String>!, travelMode: TravelModes!, completionHandler: #escaping ((_ status: String, _ success: Bool) -> Void)) {
if let originLocation = origin {
if let destinationLocation = destination {
var directionsURLString = baseURLDirections + "origin=" + originLocation + "&destination=" + destinationLocation //+ "&key=AIzaSyDsDqj0EMYZ-C4lGF3tmbntZtzurLl6_J4"
if let routeWaypoints = waypoints {
directionsURLString += "&waypoints=optimize:true"
for waypoint in routeWaypoints {
directionsURLString += "|" + waypoint
}
}
if let _ = travelMode {
var travelModeString = ""
switch travelMode.rawValue {
case TravelModes.walking.rawValue:
travelModeString = "walking"
case TravelModes.bicycling.rawValue:
travelModeString = "bicycling"
default:
travelModeString = "driving"
}
directionsURLString += "&mode=" + travelModeString
}
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
// directionsURLString = directionsURLString.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
/// directionsURLString = directionsURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
directionsURLString = directionsURLString.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)!
//let encodedHost = unencodedHost.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)
//var url = NSURL(urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()))
//let savePath = (documentDirectory as NSString).stringByAppendingPathComponent("mergeVideo-\(date).mov")
let directionsURL = NSURL(string: directionsURLString)
// DispatchQueue.main.asynchronously(execute: { () -> Void in
DispatchQueue.main.async {
let directionsData = NSData(contentsOf: directionsURL! as URL)
// var error: NSError?
do {
let dictionary = try JSONSerialization.jsonObject(with: directionsData! as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
/*
if (error != nil) {
print(error)
completionHandler(status: "", success: false)
}
*/
// else {
let status = dictionary?["status"] as! String
if status == "OK" {
self.selectedRoute = (dictionary?["routes"] as! Array<Dictionary<NSObject, AnyObject>>)[0]
self.overviewPolyline = self.selectedRoute["overview_polyline"] as! Dictionary<NSObject, AnyObject>
let legs = self.selectedRoute["legs"] as! Array<Dictionary<NSObject, AnyObject>>
let startLocationDictionary = legs[0]["start_location"] as! Dictionary<NSObject, AnyObject>
self.originCoordinate = CLLocationCoordinate2DMake(startLocationDictionary["lat"] as! Double, startLocationDictionary["lng"] as! Double)
let endLocationDictionary = legs[legs.count - 1]["end_location"]as! Dictionary<NSObject, AnyObject>
self.destinationCoordinate = CLLocationCoordinate2DMake(endLocationDictionary["lat"] as! Double, endLocationDictionary["lng"] as! Double)
self.originAddress = legs[0]["start_address"] as! String
self.destinationAddress = legs[legs.count - 1]["end_address"] as! String
self.calculateTotalDistanceAndDuration()
completionHandler(status, true)
}
else {
completionHandler(status, false)
}
} catch let error as NSError {
print(error)
}
}
}
else {
completionHandler("Destination is nil.", false)
}
}
else {
completionHandler("Origin is nil", false)
}
}
/// this method below is for the calculation of Distance adn Duration of each location for travel
func calculateTotalDistanceAndDuration() {
let legs = self.selectedRoute["legs"] as! Array<NSDictionary>
totalDistanceInMeters = 0
totalDurationInSeconds = 0
for leg in legs {
totalDistanceInMeters += (leg["distance"] as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
totalDurationInSeconds += (leg["duration"]as! Dictionary<NSObject, AnyObject>)["value"] as! UInt
}
let distanceInKilometers: Double = Double(totalDistanceInMeters / 1000)
totalDistance = "Total Distance: \(distanceInKilometers) Km"
let mins = totalDurationInSeconds / 60
let hours = mins / 60
let days = hours / 24
let remainingHours = hours % 24
let remainingMins = mins % 60
let remainingSecs = totalDurationInSeconds % 60
totalDuration = "Duration: \(days) d, \(remainingHours) h, \(remainingMins) mins, \(remainingSecs) secs"
}
}
Use
Dictionary<String, Any>
as JSON dictionary type.
JSON keys are required to be String anyway and
AnyObject has been changed to Any in Swift 3.
Edit :
I recommend to use a type alias for the JSON dictionary type
typealias JSONObject = [String:Any] // synonym of Dictionary<String, Any>
Then you can write
var lookupAddressResults: JSONObject!
...
var selectedRoute: JSONObject!
var overviewPolyline: JSONObject!
and the parsing code
...
if status == "OK" {
let allResults = dictionary?["results"] as! Array<JSONObject>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as? String
let geometry = self.lookupAddressResults["geometry"] as! JSONObject
self.fetchedAddressLongitude = ((geometry["location"] as! JSONObject)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! JSONObject)["lat"] as! NSNumber).doubleValue
completionHandler(status, true)
}
...
You can also replace
( ... as! NSNumber).doubleValue
with
... as! Double

Resources