finding distance of a Autocomplete resulted location from current location IOS - ios

I m working on application like Uber and Careem. When user searches for a location using Google Places Autocomplete API, I want to show the distance as well along with the name of the autocomplete resulted place like Uber and Careem does this.
Plz have a look on this image for further clarification

Edit:
where cuGMS is array of GMSAutocompletePrediction
let cuGMS = self.resultsArray[indexPath.row]
let placesClient = GMSPlacesClient()
placesClient.lookUpPlaceID(cuGMS.placeID!, callback: { (response , error) in
print("response \(response)")
if(error == nil)
{
let coor = response?.coordinate
let lat = coor?.latitude
let lon = coor?.longitude
}
else
{
}
})
try this
let loc1:CLLocation = CLLocation.init(latitude: 30.0000, longitude:29.2545)
let loc2:CLLocation = CLLocation.init(latitude: 28.02002, longitude:27.00021)
let dis = loc1.distance(from: loc2)

Related

iOS - Generate map image for the latitude and longitude

I have searched the internet but couldn't find if I can get the map image from the latitude and logitude.
Is there any api which can give me the map image .
Because I got start location and end location.
When I give the start and end location , it should give me map image with route .
Is the possible .
If I am on mapview I can manage snapshot , but here I won't have any mapview.
Any links, ideas , suggestions highly appreciated.
Regards.
I don´t know if there are any API´s available for this, but when I do this I just call the Apple Maps app and provide a latitude and longitude for my position and open up that app.
In that way the user can use the Apple Maps application to navigate and get full information about roads etc.
Here is how I call this:
func navigateWithAppleMaps(latitude: Double, longitude: Double) {
let regionDistance:CLLocationDistance = 10000
let coordinates = CLLocationCoordinate2DMake(latitude, longitude)
let regionSpan = MKCoordinateRegionMakeWithDistance(coordinates, regionDistance, regionDistance)
let options = [
MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center),
MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span)
]
let placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = "Start"
mapItem.openInMaps(launchOptions: options)
}
I think you have to use google developer static map api for this
https://developers.google.com/maps/documentation/static-maps/intro
You can get image from just passing latitude and longitude from following code. You can set zoom on mapview in URL.
var staticMapUrl: String = "http://maps.google.com/maps/api/staticmap?markers=color:red|\(YOUR LATITUDE HERE),\(YOUR LONGITUDE HERE)&\("zoom=16&size=400x400")&sensor=true"
var mapUrl = URL(string: staticMapUrl.addingPercentEscapes(using: String.Encoding.utf8))
var image = UIImage(data: Data(contentsOf: mapUrl))
SWIFT 5 creating two pins on map and path between them
Lets first get path between 2 points using google api:
//=== get points ===
var encodedPathPoints = "" //we'll get this from google api
let pointsUrl = "https://maps.googleapis.com/maps/api/directions/json?origin=\(location1.lat!),\(location1.long!)&destination=\(location2.lat!),\(location2.long!)&sensor=false&mode=driving&key=\(ServiceConstants.MAP_KEY)"
if Reachability.isConnectedToNetwork(){
AF.request(pointsUrl).responseJSON { response in
let json = try? JSON(data: response.data!)
if json != nil {
print("JSON map results: \(json)")
let routes = json!["routes"].arrayValue
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
if points != nil {
encodedPathPoints = points!
}
}
}
}
}
Once i have path between my two points let set static map image
let MAP_BASE_URL = "https://maps.googleapis.com/maps/api/staticmap?"
let MAP_IMAGE_SIZE = "size=\(2 * Int(imageView.frame.size.width))x\(2 * Int(imageView.frame.size.height))"
let MAP_MARKER = "&markers=icon:https://www.google.com|";//your icon url
let MAP_deliveryLocation = "\(location.lat!),\(location.long!)"
let MAP_WAYPOINT = "\(storeLocation.lat!),\(storeLocation.long!)"
let MAP_KEY = "&key=\(ServiceConstants.MAP_KEY)"
let MAP_STYLE = "&format=png&maptype=roadmap&style=element:geometry|color:0xf5f5f5&style=element:labels.icon|visibility:off&style=element:labels.text.fill|color:0x616161&style=element:labels.text.stroke|color:0xf5f5f5&style=feature:administrative.land_parcel|element:labels.text.fill|color:0xbdbdbd&style=feature:poi|element:geometry|color:0xeeeeee&style=feature:poi|element:labels.text.fill|color:0x757575&style=feature:poi.park|element:geometry|color:0xe5e5e5&style=feature:poi.park|element:labels.text.fill|color:0x9e9e9e&style=feature:road|element:geometry|color:0xffffff&style=feature:road.arterial|element:labels.text.fill|color:0x757575&style=feature:road.highway|element:geometry|color:0xdadada&style=feature:road.highway|element:labels.text.fill|color:0x616161&style=feature:road.local|element:labels.text.fill|color:0x9e9e9e&style=feature:transit.line|element:geometry|color:0xe5e5e5&style=feature:transit.station|element:geometry|color:0xeeeeee&style=feature:water|element:geometry|color:0xc9c9c9&style=feature:water|element:labels.text.fill|color:0x9e9e9e&size=480x360"
let PATH = "&path=color:0x11C856|weight:5|enc:\(encodedPathPoints)" //use 'enc:' if you're using encoded path just like me, you can ignore this if you're using normal lat lng points concatenated in string.
let mapStaticImgUrlStr = "\(MAP_BASE_URL)\(MAP_IMAGE_SIZE)\(PATH)&center=\(MAP_deliveryLocation)|\(MAP_WAYPOINT)\(MAP_MARKER)\(MAP_deliveryLocation)\(MAP_MARKER)\(MAP_WAYPOINT)\(MAP_STYLE)\(MAP_KEY)"
let url = URL(string: mapStaticImgUrlStr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)
do {
let data = try NSData(contentsOf: url3!, options: NSData.ReadingOptions())
imageView.image = UIImage(data: data as Data)
} catch {
imageView.image = UIImage()
}

Google places web service: How to get next page of results in Swift 3

I'm using the Google Web Service API in my iOS app to find hospice locations near the user's location. I can get the first page of results, but have been unsuccessful with using the pagetoken to retrieve the next page of results. Below is my search function. Any help on where I am going wrong (have never used URLSession before) would be appreciated.
func performGoogleQuery(url:URL)
{
print("PERFORM GOOGLE QUERY")
let task = URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) in
if error != nil
{
print("An error occured: \(error)")
return
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any]
// Parse the json results into an array of MKMapItem objects
if let places = json?["results"] as? [[String : Any]]
{
print("Places Count = \(places.count)") // Returns 20 on first pass and 0 on second.
for place in places
{
let name = place["name"] as! String
print("\(name)")
if let geometry = place["geometry"] as? [String : Any]
{
if let location = geometry["location"] as? [String : Any]
{
let lat = location["lat"] as! CLLocationDegrees
let long = location["lng"] as! CLLocationDegrees
let coordinate = CLLocationCoordinate2DMake(lat, long)
let placemark = MKPlacemark(coordinate: coordinate)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = name
self.mapitems.append(mapItem)
}
}
}
print("mapItems COUNT = \(self.mapitems.count)") // Remains at 20 after 2 passes.
}
// If there is another page of results,
// configure the new url and run the query again.
if let pageToken = json?["next_page_token"]
{
let newURL = URL(string: "https://maps.googleapis.com/maps/api/place/textsearch/json?pagetoken=\(pageToken)&key=\(self.googleAPIKey)")
//print("PAGETOKENURL = \(newURL)")
self.performGoogleQuery(url: newURL!)
}
}catch {
print("error serializing JSON: \(error)")
}
})
task.resume()
}
Update (Based on Dima's response): Change
self.performGoogleQuery(url: newURL!)
to this
let when = DispatchTime.now() + 2 // change 2 to desired number of seconds
DispatchQueue.main.asyncAfter(deadline: when) {
self.performGoogleQuery(url: newURL!)
}
According to the documentation:
There is a short delay between when a next_page_token is issued, and
when it will become valid.
I think you are probably fetching the next page too quickly. Try to set a delay of at least a few seconds and see if that solves your issue.
From what I see though, I don't think you're meant to automatically fetch pages in quick succession. They seem to want you to let the user trigger fetching of additional content.
You can request a new page up to two times following the original
query. Each page of results must be displayed in turn. Two or more
pages of search results should not be displayed as the result of a
single query.

Google geo coding keeps returning the city centre location for different places

I am using google auto complete(GMSAutocompletePrediction) to get the attributedFullText(result.attributedFullText.string), the chosen suggestions is used in the following code, which is the geocoding to get the latitude and longitude from the attributedFullText. For many different places the returned origin is the city centre of the city i am looking on.
Knowing that the language used is Arabic
if let correctedAddress = self.allAddresses[0][(indexPath as NSIndexPath).row].full.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let url = URL(string: "https://maps.googleapis.com/maps/api/geocode/json?address=\(correctedAddress)&sensor=false") {
print(correctedAddress)
print(self.allAddresses[0][(indexPath as NSIndexPath).row].full)
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
// 3
do {
if data != nil{
if let jsonObject = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:Any],
let results = jsonObject["results"] as? [[String:Any]], !results.isEmpty,
let geometry = results[0]["geometry"] as? [String:Any],
let location = geometry["location"] as? [String:Double],
let lat = location["lat"], let lng = location["lng"] {
print("\(lat) - \(lng)")
let newAddress = Address(name: self.allAddresses[0][(indexPath as NSIndexPath).row].name , detail: self.allAddresses[0][(indexPath as NSIndexPath).row].detail, latitude: String(lat), longitude: String(lng))
// go back to the previous view controller
self.delegate?.userDidChooseLocation(newAddress)
self.dismiss(animated: true, completion: nil)
}
}
}catch {
print("Error")
}
})
// 5
task.resume()
}
}
}
You'll be much better of using the Place ID:
https://developers.google.com/places/ios-api/reference/interface_g_m_s_autocomplete_prediction.html#a1c5f68fbd615425c3dcc5cb60b8b16c7
You can also query the Google Maps Geocoding API web service with the Place ID from Autocomplete:
https://maps.googleapis.com/maps/api/geocode/json?place_id=(placeId)&key=YOUR_API_KEY
Note that an API key is required for this kind of requests.
Also, the sensor parameter was retired long ago.

Get Walking Directions to Location

I'm trying to incorporate a feature in my app that will give the user walking directions to a location via MapKit. I've seen it on the Maps app but I don't know how to do it myself. I got directions for a car but am stumped on how to do it for walking. This is what I have so far:
func getDirections(){
let request = MKDirectionsRequest()
request.source = MKMapItem.mapItemForCurrentLocation()
request.destination = destination
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler { (response, error) in
if error != nil {
print("Error \(error)")
} else {
//self.dispLayRout(response)
var overlays = self.mapView.overlays
self.mapView.removeOverlays(overlays)
for route in response!.routes as! [MKRoute] {
self.mapView.addOverlay(route.polyline,
level: MKOverlayLevel.AboveRoads)
var instructionNumber = 0
for next in route.steps {
instructionNumber += 1
print(next.instructions)
}
}
}
}
Also I don't want it to have to open the Maps app up. I need it to find the route inside my app.
MKDirectionsRequest has a transportType property. Add this right after you create your transport request:
let request = MKDirectionsRequest()
request.transportType = .Walking
(I've never tried to get walking directions before, but I found that in the Xcode docs in less than 30 seconds. Learn your way around the Xcode "Documentation and API reference" in the help menu. It contains a wealth of information.)

Open Apple Maps programmatically

I want to open the Apple Maps App in my own Swift App, but I have only zipcode, city & street. I have NO Coordinates. I researched a lot, but there were only ways with using coordination information.
You can just pass your address information as URL parameters in the URL with which you open the maps app. Say you wanted the maps app to open centered on The White House.
UIApplication.sharedApplication().openURL(NSURL(string: "http://maps.apple.com/?address=1600,PennsylvaniaAve.,20500")!)
The Maps app opens with the ugly query string in the search field but it shows the right location. Note that the city and state are absent from the search query, it's just the street address and the zip.
A potentially better approach, depending on your needs, would be to get the CLLocation of the address info you have using CLGeocoder.
let geocoder = CLGeocoder()
let str = "1600 Pennsylvania Ave. 20500" // A string of the address info you already have
geocoder.geocodeAddressString(str) { (placemarksOptional, error) -> Void in
if let placemarks = placemarksOptional {
print("placemark| \(placemarks.first)")
if let location = placemarks.first?.location {
let query = "?ll=\(location.coordinate.latitude),\(location.coordinate.longitude)"
let path = "http://maps.apple.com/" + query
if let url = NSURL(string: path) {
UIApplication.sharedApplication().openURL(url)
} else {
// Could not construct url. Handle error.
}
} else {
// Could not get a location from the geocode request. Handle error.
}
} else {
// Didn't get any placemarks. Handle error.
}
}
Swift 4 and above
Use this version to get the coordinates for the Address, there is also a way to open it directly with the address, but that is susceptible to errors
import CoreLocation
let myAddress = "One,Apple+Park+Way,Cupertino,CA,95014,USA"
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(myAddress) { (placemarks, error) in
guard let placemarks = placemarks?.first else { return }
let location = placemarks.location?.coordinate ?? CLLocationCoordinate2D()
guard let url = URL(string:"http://maps.apple.com/?daddr=\(location.latitude),\(location.longitude)") else { return }
UIApplication.shared.open(url)
}
Apple has a Documentation about the Map URL Scheme. Look here: https://developer.apple.com/library/archive/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html#//apple_ref/doc/uid/TP40007899-CH5-SW1
Using Swift 4 and Xcode 9
At the top:
import CoreLocation
Then:
let geocoder = CLGeocoder()
let locationString = "London"
geocoder.geocodeAddressString(locationString) { (placemarks, error) in
if let error = error {
print(error.localizedDescription)
} else {
if let location = placemarks?.first?.location {
let query = "?ll=\(location.coordinate.latitude),\(location.coordinate.longitude)"
let urlString = "http://maps.apple.com/".appending(query)
if let url = URL(string: urlString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
}
}
Swift 5: Directly open maps app
///Opens text address in maps
static func openAddressInMap(address: String?){
guard let address = address else {return}
let geoCoder = CLGeocoder()
geoCoder.geocodeAddressString(address) { (placemarks, error) in
guard let placemarks = placemarks?.first else {
return
}
let location = placemarks.location?.coordinate
if let lat = location?.latitude, let lon = location?.longitude{
let destination = MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon)))
destination.name = address
MKMapItem.openMaps(
with: [destination]
)
}
}
}

Resources