Ordering Geofire results on Distance - ios

I've been experimenting with Geofire for iOS but can't seem to find any way of returning the distance from the search position in a circle query. The GFQueryResultBlock only returns the key and position. Am I right in assuming that I have to calculate the distance myself?
Let's say I am making a nearby restaurant app with Firebase, and want to display the 20 closest restaurants to the user, ordered by how close they are. I could create a circle query and then increase the search radius via a loop until I find 20 restaurants. Then calculate the distance for each one and sort them before displaying them to the user. Is this a reasonable approach, given that a large amount of work is being done in the app to structure the data (calculating distance & sorting)?
I've noticed that javascript Geofire queries return distance from the center, but I guess the iOS and android versions are different from this.

When you are querying from Geofire, relevant results are automatically ordered by ascending order. So in order to get the distance , Im just using the distanceFromLocation function:
Here my code:
func getGeoFirePlaces(){
let geofireRef = FIRDatabase.database().reference().child("testForGeofire")
let geoFire = GeoFire(firebaseRef: geofireRef)
//geoFireRef is pointing to a firebase reference where I previously set all places' location
let userPosition = CLLocation(latitude: 32.0776067, longitude: 34.78912)
let circleQuery = geoFire.queryAtLocation(userPosition, withRadius: 2)
circleQuery.observeEventType(.KeyEntered, withBlock: { (key: String!, location: CLLocation!) in
print("Key '\(key)' entered the search area and is at location '\(location)'")
//getting distance of each Place return with the callBack
let distanceFromUser = userPosition.distanceFromLocation(location)
print(distanceFromUser)
})
}
Hope this help!

Related

Cumulative distance travelled SwiftUI

There are a few Q/A's on this topic e.g. here and here but I'm trying to adapt these in Swift UI to calculate an array of distances between consecutive points during a run. The idea being that eventually I can get a list of 'distance travelled every 0.2 miles' or similar.
But what I want first is... distance between location 1 and location 2, location 2 and location 3, location 3 and location 4 etc - to calculate total distance travelled on a run.
To do this, I'm trying this code :
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
lastSeenLocation = locations.last
fetchCountryAndCity(for: locations.last)
print (locations)
This works fine and prints an updating list of locations to simulator but then the below is meant to get each location that is added, use that as the start point and the next one as the end point and for now simply print each distance to console.
Then calculate the distance between them using the getDistance function below.
This doesn't work with error "Type of expression is ambiguous without more context" on the let distance = ... line.
var total: Double = 00.00
for i in 0..<locations.count - 1 {
let start = locations[i]
let end = locations[i + 1]
let distance = getDistance(from: start,to: end)
total += distance
}
print (total)
This is my function for getting the actual distance between 2 points, which I've taken from one of the other questions/answers posted above.
func getDistance(from: CLLocationCoordinate2D, to: CLLocationCoordinate2D) -> CLLocationDistance {
let from = CLLocation(latitude: from.latitude, longitude: from.longitude)
let to = CLLocation(latitude: to.latitude, longitude: to.longitude)
return from.distance(from: to)
}
Help greatly appreciated! I have tried to format my question and code carefully after some feedback from another question but please tell me if I'm doing something wrong or can make it easier!
The reason you are getting the error about an ambiguous expression is because the arguments you are passing doesn't match the type of the arguments in your function. locations[i]is a CLLocation while you function wants CLLocationCoordinate2D.
Your function then creates CLLocations anyway, so you can just fix the parameter types for your function.
You have a bigger problem, however. You are relying on the locations array that is passed to the delegate function. Although this may contain multiple locations in the case where location updates were deferred for some reason, in practice it will only contain a single location update.
You will need to create your own locations array to keep the history for calculation purposes.

Move and/or Remove Markers in ArcGIS Graphics Layer

I'm using the ArcGIS 100.6 iOS SDK and am populating the map with markers on a Graphics Overlay Layer
whose locations are stored in a database common to all users of my application. Each marker is stored in a unique record and each record contains the Latitude & Longitude of the marker.
When the App is launched it reads the location of all markers in the database and adds each one to the Graphics Overlay as shown below:
let areaMarker = AGSPictureMarkerSymbol(image: UIImage(named: "CustomMarker")!)
let areaMarkerLocation = AGSPointMakeWGS84(y ?? 0.0, x ?? 0.0)
let markerIcon = AGSGraphic(geometry: areaMarkerLocation, symbol: areaMarker, attributes: >["marker": markerKey])
self.overlay.graphics.add(markerIcon)
As shown above, each marker is assigned an attribute "marker:markerKey" that is the unique database record number (key) where the marker location information is stored and serves as the marker ID.
Once the initial markers are added to the Overlay, the App "Listens" to the database for the following events:
A new marker is added
An existing marker is moved to a new location
An existing marker is deleted
When a marker is moved or deleted the database listener is notified and passed the record number (key) of the marker that was moved (or deleted). If the marker was moved, the record will then contain the new Latitude & Longitude information.
I have experimented with reading the graphics overlay and determined that it is a Collection contained in a NSMutable Array. I can read all attributes as follows:
let graphicsCollection = self.overlay.graphics.mutableArrayValue(forKey: "attributes")
print(graphicsCollection)
The result is:
(
{
marker = "-KlRW2_rba1zBrDPpxSl";
},
{
marker = "-Lu915xF3zQp4dIYnsP_";
}
)
I can do the same for "geometry" and get the array of AGSPoints:
let graphicsCollection = self.overlay.graphics.mutableArrayValue(forKey: "geometry")
print(graphicsCollection)
The result is:
(
"AGSPoint: (-117.826127, 44.781139), sr: 4326",
"AGSPoint: (-112.056906, 33.629829), sr: 4326"
)
I have been unable to determine how to get the "index" of the attribute array (e.g. marker "-KlRW2_rba1zBrDPpxSl" above should have an index of [0]) so I can then use that "index" to access the appropriate AGSPoint and update the Latitude & Longitude or remove the marker.
Thanks in advance for the help.
If you want to move the marker (i.e. an AGSGraphic) you'll want to get the AGSGraphic itself and modify the geometry property. I think that by jumping to the "geometry" in your mutableArrayValue() call you're kind of shooting yourself in the foot a bit.
I would tackle it this way:
let searchMarker = "-KlRW2_rba1zBrDPpxSl"
let newLocation = AGSPointMakeWGS84(40.7128, -74.0060) // NYC
if let graphic = (overlay.graphics as? [AGSGraphic])?.first(where: {
($0.attributes["marker"] as? String) == searchMarker
}) {
// Move the graphic
graphic.geometry = newLocation
// Or remove the graphic
overlay.graphics.remove(graphic)
}

How to calculate distance of two lat long of different table in firestore iOS swift

Here, I have different tables firestore but calculating distance from two tables namely Driver_details and Current_booking from firestore. In firestore driver_details table looks like Driver_details -> City(like bangalore, chennai, etc) -> SubLocality(like st.thomas, rajivi street, etc) -> Driver document id -> parameter value(contains: lat, long, name, id, etc..) and Current_booking table looks like Current_booking -> document id -> parameter value(Start_lat, Start_long, etc..).
How can i calculate distance of driver_detaisl -> city -> sublocality and Current_booking of lat long and then filter location driver which is nearest user lat long
Here is the screenshots of my firestore db:
I am using the core location.
import CoreLocation
let driverLocation = CLLocation(latitude: 59.244696, longitude: 17.813868)
let startLocation = CLLocation(latitude: 59.326354, longitude: 18.072310)
//this is the distance between driverLocation and startLocation (in km)
let distance = driverLocation.distance(from: startLocation) / 1000
//Display the result in km
print(String(format: "The distance to driver is %.01fkm", distance))*
Hope this will help you.

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!

How To Tell If User Is At A Specific Location?

Essentially what I need to do is find out if a user is at a specific place (IE at a venue). And if the user is, allow access to a specific ViewController.
I've been looking high and low for an answer to this problem online and surprisingly I haven't found anything. I will say I'm pretty new to iOS development.
I don't need anything as complex as geofencing like in the Ray Wenderlich tutorial, and I don't need to run it in the background. I also don't need to know if they entered or left. Just whether or not they are within that area or not when the user clicks a button.
I've gotten as far as being able to get the users location using CoreLocation, but I'm confused as to how I will go about identifying if the user is at the specific location. Ideally, I will want a radius of about 5 miles (It's a big location).
if you have the user's location as well as the venue's location you can do the following:
let radius: Double = 5 // miles
let userLocation = CLLocation(latitude: 51.499336, longitude: -0.187390)
let venueLocation = CLLocation(latitude: 51.500909, longitude: -0.177366)
let distanceInMeters = userLocation.distanceFromLocation(venueLocation)
let distanceInMiles = distanceInMeters * 0.00062137
if distanceInMiles < radius {
// user is near the venue
}
If you have the latitude and longitude of the venue, just create a CLLocation object for that and see how far the user is from that location.
// get the current user location, then...
let MinDistance = 100.0 // meters
let distance = venueLocation.distanceFromLocation(userLocation)
if distance < MinDistance {
// I'm close enough to the venue!
}

Resources