Google AutoComplete Query in Swift - ios

I am using a google maps auto complete query as you seen in the below I determine the bounds of autocomplete around my location but I am still getting a places so far from my location. I also check the bounds.southwest and bounds.northeast, they are also true but places are not arsounds here.
var northRegion = CLLocationCoordinate2DMake((locationManager.location?.coordinate.latitude)!*1.0001, (locationManager.location?.coordinate.longitude)!*1.01)
var southRegion = CLLocationCoordinate2DMake((locationManager.location?.coordinate.latitude)!*0.99, (locationManager.location?.coordinate.longitude)!*0.9999)
var bounds = GMSCoordinateBounds(coordinate: northRegion, coordinate: southRegion)
print(locationManager.location?.coordinate.latitude)
print(locationManager.location?.coordinate.longitude)
print(bounds.northEast)
print(bounds.southWest)
var filter = GMSAutocompleteFilter()
filter.type = GMSPlacesAutocompleteTypeFilter.Geocode
var yourLat = locationManager.location?.coordinate.latitude
var yourLon = locationManager.location?.coordinate.longitude
placesClient = GMSPlacesClient()
placesClient?.autocompleteQuery("[YOUR TEXTFIELD'S TEXT HERE]", bounds: bounds, filter: filter, callback: { (result, error) -> Void in
for item in result! {
print(item.attributedFullText)
}
})
For example, my location is Istanbul and when I wrote this, I am getting Cape Town datas.

OK I found this in GMSPlacesClient
* #param bounds The bounds used to bias the results. This is not a hard restrict - places may still * be returned outside of these bounds. This parameter may be nil.
So autocomplete query can not filter the places in bounds way.

Maybe a little late to answer but you can try using filter.country = countryCode, I use that to limit the result to just my country.

Related

Get nearby places from Google API sorted by distance

I am currently stuck on this functionality where user need nearby places results on the basis of distance.
For e.g :-
If i search "Kochi" and i am in India
Then kochi is in India as well as in Japan
So result should be like
1. Kochi, India
2. Kochi, Japan
This is just an example, user can also search landmarks, city, streets etc.. But i want all results to be sorted by distance.Closer results will display first and then far places. But not able to get results according to requirement.
Similar functionality is done on android and they are using it by passing radius (Like 500 km from current location)
What i have tried so far :-
Using GMSPlacesClient.autocompleteQuery and passing bounds in it for current location
GMSPlacesClient().autocompleteQuery(txtLocation.text!, bounds: bounds, filter: filter, callback: {(results, error) -> Void in
if let error = error {
print("Autocomplete error \(error)")
return
}
if let results = results {
}
})
Using GooglePlaces.placeAutocomplete
GooglePlaces.placeAutocomplete(forInput: self.txtLocation.text!, offset: 0, locationCoordinate: nil, radius: nil, language: nil, types: [GooglePlaces.PlaceType.Regions], components: nil, completion: { (response, error) in
// To do
})
I also used this url (https://maps.googleapis.com/maps/api/place/autocomplete/json?input=pan&location=30.704316,76.712106&radius=50000&components=country:IN) for Google API but for these kind of alternatives i have to do custom parsing.
I am playing around with the same GoogleMaps API. I do the same as you requested but through a different way code is attached, I do this on a button press for 'Search' you can either set the bounds to coordinates or the map view frame. In the below it is currently using the users current location for the bounds and it then works its way out from that I think this works near enough perfectly:
let autoCompleteController = GMSAutocompleteViewController()
autoCompleteController.delegate = self as! GMSAutocompleteViewControllerDelegate
// Set bounds to map viewable region
let visibleRegion = googleMaps.projection.visibleRegion()
let bounds = GMSCoordinateBounds(coordinate: visibleRegion.farLeft, coordinate: visibleRegion.nearRight)
// New Bounds now set to User Location so choose closest destination to user.
let predictBounds = GMSCoordinateBounds(coordinate: userLocation.coordinate, coordinate: userLocation.coordinate)
autoCompleteController.autocompleteBounds = predictBounds
// Set autocomplete filter to no filter to include all types of destinations.
let addressFilter = GMSAutocompleteFilter()
addressFilter.type = .noFilter
autoCompleteController.autocompleteFilter = addressFilter
// Change text color
UISearchBar.appearance().setTextColor(color: UIColor.black)
self.present(autoCompleteController, animated: true, completion: nil)
I am having a problem with Google Directions and getting the calculated distance and miles if you have any code for this that may be of help for me!

Find distance of location to route in Google Maps SDK

I´m developing an iPhone app, and I need some help with this case:
I need to check, if user leave google maps route (GMSPolyline) and if distance from user location to nearest point of route is more than 40 meters -- I need to rebuild route.
I can't find the right algorithm to detect if distance from user to route is more than 40 meters.
I've tried to use this method to find projection of user location (converted to CGPoint by CGPointMake) on route :
+ (CGPoint)projectionOfPoint:(CGPoint)origPoint toSegmentP1:(CGPoint)p1 p2:(CGPoint)p2 {
// for case line is parallel to x axis
if (p2.y == p1.y) {
return CGPointMake(origPoint.x, p1.y);
// for case line is parallel to y axis
} else if (p2.x == p1.x) {
return CGPointMake(p1.x, origPoint.y);
}
// line from segment
CGFloat kKoefLine1 = (p2.x - p1.x)/(p2.y - p1.y);
CGFloat bKoefLine1 = p1.y - kKoefLine1*p1.x;
// perpendicular line
CGFloat kKoefLine2 = -1/kKoefLine1;
CGFloat bKoefLine2 = origPoint.y - kKoefLine2*origPoint.x;
// cross point
CGFloat krossX = (bKoefLine2 - bKoefLine1)/(kKoefLine1 - kKoefLine2);
CGFloat krossY = kKoefLine2*krossX + bKoefLine2;
return CGPointMake(krossX, krossY);}
Then I calculate distance from returned projection (converted to CLLocation) and user location, but it doesn't works.
P.S.: I will be thankful if solution would be written on swift.
There is a GMSGeometryIsLocationOnPath function in the GMSGeometryUtils module in the Google Maps SDK.
You should be able to use that to calculate what you need.
Pseudocode (not tested):
let currentLocation: CLLocationCoordinate2D = ...
let routePath: GMSPath = routePolyline.path
let geodesic = true
let tolerance: CLLocationDistance = 40
let within40Meters = GMSGeometryIsLocationOnPath(currentLocation, routePath, geodesic, tolerance)
for swift 5.0 and based on #Arthur answer I wrote follwoing function
func isInRoute(posLL: CLLocationCoordinate2D, path: GMSPath) -> Bool
{
let geodesic = true
let tolerance: CLLocationDistance = 40
let within40Meters = GMSGeometryIsLocationOnPathTolerance(posLL, path, geodesic, tolerance)
return within40Meters
}
While I don't recall much about the GMS SDK off the top of my head, before I give you an answer, I will say that nobody on here will write your code for you. That's your job and should be done on your time. You haven't given any background as to how far you've gotten in terms of calculating routes, whether or not you've figured out how to calculate distance at all, etc.
With that being said, routes on Google Maps are comprised of "legs", which denote a path to take before a turn is made in efforts to reach the end destination. By querying your "route" dictionary, you can extract an array of dictionaries where each element (which is a dictionary) contains metadata about a "leg". You can then loop through that array, go through each dictionary and extract the "distance" value, and sum them to a single "distance" var.
You can recalculate this as often as needed and use a conditional to check whether or not the leg distance sum is < 40M, else rebuild.
link to an article that should help (I didn't have the time to go through the entire thing for you, so do your due diligence and research) here.

Realm filter CLLocation

I have warpper class for CLLoaction
Wrapper
class Location: Object {
dynamic var long: Double = 0
dynamic var lat: Double = 0
}
I have to filter stored Location for those which are in 1km radius based on my current location. I thought NSPredicate with block would do the job, but realm doesn't support it. So my question is how other way I can achieve it?
Of course I could do something like this:
let locations = realm.objects(Location)
var locationsInRadius = [Location]()
for l in locations {
let location = CLLocation(latitude: l.lat, longitude: l.long)
if (location.distanceFromLocation(currentLocation) < radius){
locationsInRadius.append(l)
}
}
But it feels wrong according to whole realm concept of filters.
You can't search for objects by distance, but you can search by using a bounding box. Simply add latitude and longitude fields to your object, then:
Get current location
Create a bounding box around that location
Filter your objects by bounding box
In code, that could like this:
// 0. This example uses MapKit to calculate the bounding box
import MapKit
// 1. Plenty of answers for this one...
let currentLocation = CLLocationCoordinate2DMake(37.7749295, -122.4194155)
// 2. Create the bounding box with, 1km radius
let region = MKCoordinateRegionMakeWithDistance(currentLocation, 1000, 1000)
let northWestCorner = CLLocationCoordinate2DMake(
currentLocation.latitude + (region.span.latitudeDelta),
currentLocation.longitude - (region.span.longitudeDelta)
)
let southEastCorner = CLLocationCoordinate2DMake(
currentLocation.latitude - (region.span.latitudeDelta),
currentLocation.longitude + (region.span.longitudeDelta)
)
// 3. Filter your objects
let predicate = NSPredicate(format: "lat BETWEEN {%f, %f} AND lon BETWEEN {%f, %f}",
northWestCorner.latitude,
southEastCorner.latitude,
northWestCorner.longitude,
southEastCorner.longitude
)
let nearbyLocations = realm.objects(MyLocation).filter(predicate)
Note that you can still store your CLLocation object for other purposes, but you won't need it for the search.
Also note that as this searches a box rather than what you wanted, a circle with a 1km radius, this can return results of greater than 1km. If that's not ok, you would need to reduce the radius or make a fancier predicate.
For some reason, using a single predicate (lat BETWEEN {%f, %f} AND lon BETWEEN {%f, %f}) doesn't work with the current version of Realm. I'm using this nice lib: https://github.com/mhergon/RealmGeoQueries
This is how the predicate is built inside and it works fine: https://github.com/mhergon/RealmGeoQueries/blob/master/GeoQueries.swift:
let topLeftPredicate = NSPredicate(format: "%K <= %f AND %K >= %f", latitudeKey, box.topLeft.latitude, longitudeKey, box.topLeft.longitude)
let bottomRightPredicate = NSPredicate(format: "%K >= %f AND %K <= %f", latitudeKey, box.bottomRight.latitude, longitudeKey, box.bottomRight.longitude)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [topLeftPredicate, bottomRightPredicate])
Basically, here is how to use it:
let results = try! Realm()
.findNearby(YourModelClass.self, origin: location.coordinate, radius: 500, sortAscending: nil)
You can also change the default "lat" and "lng" keys by passing 2 extra parameters with your own naming (latitudeKey and longitudeKey).
Thanks to https://github.com/mhergon for providing this lib.

Count Number of GMSCircles in View Google Maps SDK...or use MarkerManager Instead? Does this exist for iOS?

I'm trying to count the number of GMSCircles in the users current view in iOS Swift. For example, when the user zooms in or out I want it to count the number of circles currently on the screen. I have something like this right now to append the all my circles to screen...
var circleCenter = CLLocationCoordinate2DMake(latitude, longitude);
circle.append(GMSCircle(position: circleCenter, radius: 1))
circle[index].fillColor = UIColor(red: 0.35, green: 0, blue: 0, alpha: 0.05)
circle[index].strokeColor = UIColor.redColor()
circle[index].strokeWidth = 10
circle[index].map = self.mapView;
I saw in java that there is a MarkerManager for Markers (which are different than Circles i know) where you can count the number of markers in the view like this:
count markers displayed on map after zoom in and out
but I couldn't find anything like MarkerManager for Swift. Can someone point me in the right direction...I would like to use 'circles'...But i think even in java that may be hard to do. Does the iOS have something like MarkerManager that I can use to count Markers instead of circles? Any help would be really great, thanks.
I don't think there's a explicit method for it, but what you can do call [cameraForBounds][1] to get the bounding coordinates of your viewport. With that, you can check to see if any of your circleCenters fall within the bounds of the viewport.
Something like this is what I wanted to do. Using a class level array named longitudeCollection instead of using GMSCircles though
var visibleRegion : GMSVisibleRegion = mapView.projection.visibleRegion()
var bounds = GMSCoordinateBounds(coordinate: visibleRegion.nearLeft, coordinate: visibleRegion.farRight)
var numberOfCirclesInBounds = 0
for var index = 0; index<=longitudeCollection.count-1; index++ {
var foo = longitudeCollection[index].coordinateValue
var bar = latitudeCollection[index].coordinateValue
var circleCenter = CLLocationCoordinate2DMake(bar, foo);
if bounds.containsCoordinate(circleCenter) {
numberOfCirclesInBounds++
}

Where is MapKit Step polyline?

I am trying to print coordinates of all route steps, similar to Google Maps SDK's "legs".
But it tells me that I cannot use polyline property to obtain a coordinate?
Try this:
for step in self.route!.steps as [MKRouteStep] {
otherwise it treats step as AnyObject (which doesn't have a polyline property defined so you get that compiler error).
By the way, note that polyline.coordinate just gives the average center of the polyline or one endpoint. A polyline can have more than one line segment.
If you need to get all the line segments and coordinates along the polyline, see latitude and longitude points from MKPolyline (Objective-C).
Here's one possible translation to Swift (with help from this answer):
for step in route!.steps as [MKRouteStep] {
let pointCount = step.polyline.pointCount
var cArray = UnsafeMutablePointer<CLLocationCoordinate2D>.alloc(pointCount)
step.polyline.getCoordinates(cArray, range: NSMakeRange(0, pointCount))
for var c=0; c < pointCount; c++ {
let coord = cArray[c]
println("step coordinate[\(c)] = \(coord.latitude),\(coord.longitude)")
}
cArray.dealloc(pointCount)
}
As the first linked answer warns, you may get hundreds or thousands of coordinates per step depending on the route.
Swift 4.1, as of July 2018, based on the other answer.
let pointCount = step.polyline.pointCount
let cArray = UnsafeMutablePointer<CLLocationCoordinate2D>.allocate(capacity: pointCount)
step.polyline.getCoordinates(cArray, range: NSMakeRange(0, pointCount))
for c in 0..<pointCount {
let coord = cArray[c]
print("step coordinate[\(c)] = \(coord.latitude),\(coord.longitude)")
}
cArray.deallocate()

Resources