Google Place auto complete API bounds not working in swift iOS - ios

I have implemented google place autocomplete api and I have applied bounds to restrict my auto complete search to one specific city Orlando, FL, USA but Google place api is not filtering data for only this city. It is searching placing all over USA and other cities name outside USA. I want google autocomplete api to return only this city addresses. Here is the code for same:
let placesClient = GMSPlacesClient()
let searchBound = getCoordinateBounds(latitude: CLLocationDegrees(28.5383), longitude: CLLocationDegrees(81.3792), distance: 1.45)
let filter = GMSAutocompleteFilter()
filter.country = "USA"
placesClient.autocompleteQuery(text!, bounds: searchBound, filter: filter, callback: { (results, error) in
})
//My getCoordinateBounds method
func getCoordinateBounds(latitude:CLLocationDegrees ,
longitude:CLLocationDegrees,
distance:Double = 0.001)->GMSCoordinateBounds{
let center = CLLocationCoordinate2D(latitude: latitude,
longitude: longitude)
let northEast = CLLocationCoordinate2D(latitude: center.latitude + distance, longitude: center.longitude + distance)
let southWest = CLLocationCoordinate2D(latitude: center.latitude - distance, longitude: center.longitude - distance)
return GMSCoordinateBounds(coordinate: northEast,
coordinate: southWest)
}
Note: I want to filter addresses those are around 100 miles radius of this latitude and longitude (that why I have given degree to 1.45). Another thing I added country filer USA after I found that my bounds filter is not working properly. So far I am able to restrict address search to USA.

You should also define the boundsMode parameter in your placesClient.autocompleteQuery() call. Have a look at the documentation:
https://developers.google.com/places/ios-api/reference/interface_g_m_s_places_client
boundsMode must be equal to GMSAutocompleteBoundsMode.Restrict to
interpret bounds as a restrict. By default bounds are interpreted as bias that's why you are getting results from outside.
https://developers.google.com/places/ios-api/reference/group___autocomplete_bounds_mode#gafbfae308afa98048c1e97864baa88568
let placesClient = GMSPlacesClient()
let searchBound = getCoordinateBounds(latitude: CLLocationDegrees(28.5383), longitude: CLLocationDegrees(81.3792), distance: 1.45)
let filter = GMSAutocompleteFilter()
filter.country = "USA"
placesClient.autocompleteQuery(text!, bounds: searchBound, boundsMode: GMSAutocompleteBoundsMode.Restrict, filter: filter, callback: { (results, error) in
})
The parameter boundsMode was introduced in SDK 2.5
I hope this helps!

Related

How to calculate distance between two locations using google api in swift 4

I am currently working on an IOS project in which i have to calculate distance between two location. I have done without using google but i want to use google api to get accurate distance i am sharing my code here
let myLocation = CLLocation(latitude: CLLocationDegrees(latittude[indexPath.row])!, longitude: CLLocationDegrees(longittude[indexPath.row])!)
let lat = UserDefaults.standard.string(forKey: "lat") ?? ""
let long = UserDefaults.standard.string(forKey: "long") ?? ""
let myBuddysLocation = CLLocation(latitude: CLLocationDegrees(lat)!, longitude: CLLocationDegrees(long)!)
Use distance function of CoreLocation Framework,
var startLocation = CLLocation(latitude: startLatitude, longitude: startLongitude)
var endLocation = CLLocation(latitude: endLatitude, longitude: endLongitude)
var distance: CLLocationDistance = startLocation.distance(from: endLocation)
Swift 5+:
As far as I know, there are two ways to find the distance.
If you are looking for driving distance, you can always use MKDirections.
Here is the code for finding driving distance (You can also find walking, and transit distance by changing transport type).
let sourceP = CLLocationCoordinate2DMake( sourceLat, sourceLong)
let destP = CLLocationCoordinate2DMake( desLat, desLong)
let source = MKPlacemark(coordinate: sourceP)
let destination = MKPlacemark(coordinate: destP)
let request = MKDirections.Request()
request.source = MKMapItem(placemark: source)
request.destination = MKMapItem(placemark: destination)
// Specify the transportation type
request.transportType = MKDirectionsTransportType.automobile;
// If you want only the shortest route, set this to a false
request.requestsAlternateRoutes = true
let directions = MKDirections(request: request)
// Now we have the routes, we can calculate the distance using
directions.calculate { (response, error) in
if let response = response, let route = response.routes.first {
print(route.distance) //This will return distance in meters
}
}
If you are only looking for air distance/bird's eye distance/coordinate distance, you can use this code:
let sourceP = CLLocation(latitude: sourceLat, longitude: sourceLong)
let desP = CLLocation(latitude: desLat, longitude: desLong))
let distanceInMeter = sourceP.distance(from: desP)

Restrict GMSMapView to certain bounds

I have two coordinates, and I need to restrict my Google Maps map to the frame bounded by those two coordinates. For example, I have
let bounds = GMSCoordinateBounds(
coordinate: CLLocationCoordinate2D(
latitude: 59.615440364671244,
longitude: -17.978949286043644
), coordinate: CLLocationCoordinate2D(
latitude: 33.963318167747758,
longitude: 21.442294009029865
)
)
Then, I write,
map.cameraTargetBounds = bounds
However, this does nothing to restrict the bounds of the map while it should. According to the documentation,
If not nil, [cameraTargetBounds] constrains the camera target so that gestures cannot cause it to leave the specified bounds.
This question did not help me, partially because I must allow zooming as well as panning––it just must be restricted to a certain area.
Why is this not working, and how can I fix it?
You have to use GoogleFletcher. In this we will implement two filters (GMSCoordinateBound and Type). In start bound cordinate we will put current location and at end bound we will just add 1 in our current location cordinates.
Hopefully it helps.
let location = locations.last!
let neBoundsCorner = CLLocationCoordinate2D(latitude:location.coordinate.latitude,
longitude: location.coordinate.longitude)
let swBoundsCorner = CLLocationCoordinate2D(latitude: location.coordinate.latitude + 1, longitude: location.coordinate.longitude + 1)
let bounds = GMSCoordinateBounds(coordinate: neBoundsCorner,coordinate: swBoundsCorner)
let filter = GMSAutocompleteFilter()
filter.type = .establishment
fetcher = GMSAutocompleteFetcher(bounds: bounds, filter: filter)

Annotation.coordinate does not match coordinate.latitude and coordinate.longitude

In my project I use LocationSearchTable / MKLocalSearch. When a user clicks on an item, my add annotation method is called in MapViewController:
func dropPinZoomIn(placemark: MKPlacemark) {
// cache the pin
selectedPin = placemark
// create the pin
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let streetNumber = placemark.subThoroughfare,
let city = placemark.locality,
let state = placemark.administrativeArea {
annotation.subtitle = "\(streetNumber) \(city) \(state)"
}
// add the pin the the mapView
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.01, 0.01)
let region = MKCoordinateRegionMake(annotation.coordinate, span)
mapView.setRegion(region, animated: true)
print(placemark.coordinate)
print(placemark.coordinate.latitude)
print(placemark.coordinate.longitude)
At the end, when I print the coordinate, I get a longer version whereas when I print the latitude and longitude separately, it rounds off the number near the end. This is causing me troubles later when I need to compare coordinates. How can I prevent this rounding? As an example, here are my results:
CLLocationCoordinate2D(latitude: 37.331413259110334, longitude: -122.03048408031462)
CLLocationCoordinate2D(latitude: 37.3314132591103, . . longitude: -122.030484080315)
Try the following code to prevent rounds off the number.
print(coordinate)
print(coordinate.latitude.debugDescription)
print(coordinate.longitude.debugDescription)
Result:
CLLocationCoordinate2D(latitude: -33.499989999999997, longitude: 150.255)
-33.499989999999997
150.255
Note:
The type of coordinate.latitude/coordinate.longitude is CLLocationDegrees. This type is alias for Double. Comparing Double or Float values with == will not give the expected result in most programming languages, which means that numbers that you think should be equal are in fact slightly different. Instead compute the absolute difference and handle the numbers as equal if the difference is below some threshold. See Compare double to zero using epsilon for more explanations.

Remaining distance and path on GMSPath from current Location

I am trying to update remaning distance and duration while traveling by using Google map API. I have a whole path, duration and distance but when he cover the distance, how can I get the the remaining distance and duration? I have tried GMSGeometryDistance but it's not giving me the correct distance. and I also don't want to call API again, but methods like GMSGeometryDistance.
let to = CLLocationCoordinate2D(latitude: 24.948021, longitude: 67.091186)
let from = CLLocationCoordinate2D(latitude: 24.891592, longitude: 67.084320)
let camera = GMSCameraPosition.cameraWithTarget(to, zoom: 12)
let mapView = GMSMapView.mapWithFrame(CGRectZero, camera:camera)
GoogleMapsServices.drawPath(mapView, fromPoint:from, toPoint: to, completionHandler: {(json: JSON, path: GMSPath) -> () in
if GMSGeometryIsLocationOnPathTolerance(to, path, true, 5) {
print(GMSGeometryDistance(from, to))
}
})
self.view = mapView

Fit entire Google Map in zoom level in Swift project

I have a google map with a bunch of coordinates
path.addCoordinate(CLLocationCoordinate2DMake(-37.813047, 144.959911))
path.addCoordinate(CLLocationCoordinate2DMake(-37.814895, 144.960759))
path.addCoordinate(CLLocationCoordinate2DMake(-37.814361, 144.963140))
path.addCoordinate(CLLocationCoordinate2DMake(-37.812386, 144.962239))
I would like the map to be automatically zoomed to the best level based on the points however I can't find anything relating to this.
I have this working:
var vancouver = CLLocationCoordinate2DMake(-37.813047, 144.959911)
var calgary = CLLocationCoordinate2DMake(-37.814361, 144.963140)
var bounds = GMSCoordinateBounds(coordinate: vancouver, coordinate: calgary)
var camera = viewMap.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
viewMap.camera = camera
however it only accepts 2 coordinates where I may have up to 100
Thanks
You can use GMSCoordinateBounds(path:) to fit all coordinates. But it will display a world size scale if you update the camera right after your another update. So you can use dispatch_after to solve the problem.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.whiteColor();
let camera = GMSCameraPosition.cameraWithLatitude(-37.813047, longitude: -72.8561644, zoom:5)
mapView = GMSMapView.mapWithFrame(CGRectZero, camera:camera)
let marker = GMSMarker()
marker.position = camera.target
marker.snippet = "Hello World"
marker.appearAnimation = kGMSMarkerAnimationPop
marker.map = mapView
self.view = mapView
delay(seconds: 2) { () -> () in
let path = GMSMutablePath()
path.addCoordinate(CLLocationCoordinate2DMake(37.36, -122.0))
path.addCoordinate(CLLocationCoordinate2DMake(37.45, -122.0))
path.addCoordinate(CLLocationCoordinate2DMake(37.45, -122.2))
path.addCoordinate(CLLocationCoordinate2DMake(37.36, -122.2))
path.addCoordinate(CLLocationCoordinate2DMake(37.36, -122.0))
let rectangle = GMSPolyline(path: path)
rectangle.map = self.mapView
let bounds = GMSCoordinateBounds(path: path)
self.mapView!.animateWithCameraUpdate(GMSCameraUpdate.fitBounds(bounds, withPadding: 15.0))
}
}
The delay method uses the dispatch_after:
func delay(#seconds: Double, completion:()->()) {
let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64( Double(NSEC_PER_SEC) * seconds ))
dispatch_after(popTime, dispatch_get_main_queue()) {
completion()
}
}
Iterate through your points. The southwest corner is the minimum latitude and longitude. The northeast corner is the maximum latitude and longitude. Once you have those two points, pass them into your cameraForBounds method.
Try finding an optimal viewing box on google maps by:
-Clicking on the north east corner of the area you think will cover the coordinates in a browser. Note the lat & long.
-Then click the south east corner in the same area which you think will enclose all the coordinates & note the lat & long.
Put the latitudes & longitudes in the respective variables of the below code:
var southWest = CLLocationCoordinate2DMake(latitide,longititude)
var northEast = CLLocationCoordinate2DMake(latitude,longitude)
var bounds = GMSCoordinateBounds(coordinate: northEast, coordinate: southWest)
var camera = mapView.cameraForBounds(bounds, insets:UIEdgeInsetsZero)
mapView.camera = camera;

Resources