Finding the distance between current location and another location (Swift 5) - ios

I'm not a programmer but have been playing around with a side project for fun. I'm using Xcode 10, Swift 5. I've pieced together a handful of things through youtube and SO but I've searched and experimented with this for three days and I'm running into dead ends here.
I am trying to determine the distance between a user's current location and a preset point (in this case the airport). I am able to find the distance between two hard-coded locations. I am also able to find and print the user's current location in the console. But when I try to combine those I am struggling, most often getting the error
'Value of type 'CLLocationCoordinate2D' has no member 'distance''
The code that working is and gives me the distance in meters I would like is:
lazy var phx = CLLocation(latitude: 33.409016, longitude: -111.805576)
lazy var distanceFromPhoenixToVegas = las.distance(from: phx)
And this will print out the current location coordinates:
func printCoordinates(){
if let location = locationManager.location?.coordinate {
print (location)
}
}
If any one could offer guidance I would appreciate it.
Side note, there are a lot of similar questions on Stack Overflow. There are questions about getting a user's current location, and questions about getting distances between two points. I believe this is essentially what I am asking, but it was only answered by the original poster, and the answer seems not exactly correct to me. iOS & Swift: display distance from current location?

coordinate is of type CLLocationCoordinate2D You only need locationManager.location
if let location = locationManager.location {
print(phx.distance(from: location))
}

Related

Get distance from user's location to nearest point on GMSPath

I am working with the Google Maps API and have drawn GMSPolylines on the map. I know which "node" (lat and long position for a turning point) on the map is closest to the user's location, and I know the next upcoming nodes. Given that information, how could one obtain the distance from the user's current location to the nearest point on the closest path? In the diagram below, how could we get x when we know the three GPS coordinates?
try something like this (swift 4.0)
func isApproaching(_ to:CLLocation, from:CLLocation? = nil, byDistanceInMeters:Double) -> Bool {
guard let baseLocation = from ?? ThisIsMyDefaultUserLocationVariable.location else { return false }
let delta = to.distance(from: baseLocation) as Double
if delta < byDistanceInMeters { return true }
return false
}
and this is how to call it:
if isApproaching(lastLocation, byDistanceInMeters: 50.0) ) {
// this is where you are in 50m perimeter
} else {
// here you are outside
}
.
Looking at this as a geometry problem instead of a programming problem might make this a little easier. There probably exists a library or API that does this with little more than a couple lines of code, but this approach should still yield a result with little to no overhead.
Disclaimer: This approach only works with straight lines.
You have two points that are on your path and one point that is not on the path. Using some basic algebra you can find a line that is parallel to the path and runs through the user's location, and then invert that line to find the shortest line between the user's location and the path. Then it's simply finding the intersection of two lines.
One thing to note, the larger the distance between nodes then the less accurate the euclidean distance will be. This should be negligible for nodes closer than ~100 miles.

Wrong distance using distanceFromLocation:

I'm trying to find out what's wrong but it seems like the mentioned method is just returning wrong values. I'm setting coordinates in simulator, then just print them and calculate distance:
(lldb) p location.coordinate
(CLLocationCoordinate2D) $1 = (latitude = 51, longitude = 0.10000000000000001)
(lldb) p _oldLocation.coordinate
(CLLocationCoordinate2D) $2 = (latitude = 51, longitude = 0)
Now I'm calculating distance:
distance = (CGFloat)[location distanceFromLocation:_trackEndLocation];
And when I print it i get:
(lldb) po self.trackDistance
7019.76758
Now, the problem is that users tell me that app returns too big distance. As I wanted to debug it, I've checked the distance at page to calculate distance between two points.
The results are as following:
As you can notice, the distance according to the webpage is 6.997km while Apple method tells me 7.019km. I wonder who is incorrect, Apple or the webpage and what to do with this matter. The difference isn't big, but when you accumulate it between few points it can be disturbing.
The Apple doc page for distanceFromLocation says this:
This method measures the distance between the two locations by tracing a line between them that follows the curvature of the Earth. The resulting arc is a smooth curve and does not take into account specific altitude changes between the two locations.
I suspect Google Maps does consider the terrain between the two points, and that's why you saw a 0.3% difference in your test. However, I doubt your customers are complaining about that level of error. It's more likely the error is from a different cause.
Without knowing more about your algorithm it's hard to say, but if you're tracking distance traveled, the error might be caused by jitter in the phone's GPS coordinates. (That would cause a straight line path to look like a zigzag, which would yield a longer distance.) You could record some real data from your app to see for sure.
The problem was that I was checking the accuracy and filtered some results out if the accuracy was bad. That's why sometimes the distance was wrong. I've fixed the accuracy filtration and everything is working fine.

iOS MKDirections for multiple destinations and a single source

My application needs to get the "closest" walking point to the user from a set of 10. I have seen that MKDirections does this with "calculateDirectionsWithCompletionHandler" for a single source, destination locations. My question is, do I have do make 10 requests and then check which one is the closest one in distance or there is a way of sending multiple destinations and the response would be the closest one walking?
Thank you
As far as I'm aware there is no way to specify more than two points for an MKDirections request. Your proposed solution of creating 10 separate requests should work in theory, though keep in mind that:
MKDirections does not calculate directions locally on the device, but reaches out to a remote API which calculates them and returns the response. If you're calculating 10 routes, there will likely be a noticeable delay for the user while waiting for all the results to be returned.
Because directions are calculated using a remote API, Apple likely would frown on something like this- if you're hitting their directions API too frequently they might throttle you, or they may just reject the app when it's submitted to the App Store if they see that this is happening.
I'm not sure of your exact requirements, but depending on the accuracy you need, one solution could be to calculate the distance between the start point and each end point using the haversine formula, which would give you the distance between the two points as-the-crow-flies. Because you're calculating walking directions (as opposed to driving, where you're limited to following specific roads and subject to traffic), the closest distance from these calculations will likely be the closest in terms of actual walking distance/time, at which point you could then make a single MKDirections request for those details.
// try this annotation.coordinate is the locations and ULManager.location //as the user location
let dirRe = MKDirectionsRequest()
dirRe.source = MKMapItem(placemark: MKPlacemark(coordinate: (self.ULManager.location?.coordinate)!, addressDictionary: nil))
dirRe.destination = MKMapItem(placemark: MKPlacemark(coordinate: (annotation.coordinate), addressDictionary: nil))
dirRe.transportType = .Walking
let requestAll = MKDirections(request: dirRe)
requestAll.calculateDirectionsWithCompletionHandler ({
(response: MKDirectionsResponse?, error: NSError?) in
// do your staff here
})

distanceFromLocation returns huge value without moving the tiniest bit

I'm trying to call a function in case the distance traveled since last checked is > 30 m. Based on the Class description of CLLocationDistance the value returned is in m.
I use this code:
[locationManager startUpdatingLocation];
if (!startingLocation)
startingLocation = locationManager.location;
// NSLog(#"Latitude:%f Longitude:%f", startingLocation.coordinate.latitude, startingLocation.coordinate.longitude);
updatedLocation = locationManager.location;
CLLocationDistance distance = [updatedLocation distanceFromLocation:startingLocation];
if (distance > 30) {
NSLog(#"%f",distance);
NSLog(#"Latitude:%f Longitude:%f", updatedLocation.coordinate.latitude, updatedLocation.coordinate.longitude);
[self stop];
The console output with NSLog returns 7946754.993111, and I did not even touch the phone.
Any suggestions are appreciated!
8000 km between two points? I'll bet the first point is 0, 0 and you're about 8000 km from there, right?
In any case be sure to only use valid locations by checking to make sure the horizontalAccuracy is >=0 before using the location coordinates. Invalid locations have a negative horizontalAccuracy.
Also, you should probably discard any cached locations by checking the age of the location data and don't use it if it is more than a few seconds old.
See the sample code here that does these checks.
Another thing to watch out for is that you can get locations that show false motion due to changes in accuracy. See this answer for details.
Is it possible that because you are calling startUpdatingLocation then getting that location then immediately asking for an update that the location is still trying to figure out exactly where it is, hence the big movement.
Try calling startUpdatingLocation from another method then implement the delegate method from CLLocationManager locationManager:didUpdateToLocation:fromLocation to get the movement.

Order of CLLocation objects in -distanceFromLocation:

This is my first time posting a question here, but I have found a lot of help from other people's questions.
My question refers to the -distanceFromLocation: function in CLLocation. I had assumed that finding the distance between point A and point B would be the same as that between point B and point A, but I have found that it is not. I have tested this in a very simple case:
CLLocation *test1 = [[CLLocation alloc] initWithLatitude:37.529530 longitude:-122.259232];
CLLocation *test2 = [[CLLocation alloc] initWithLatitude:47.900002 longitude:-103.495102];
NSLog(#"%f vs %f",[test2 distanceFromLocation:test1],[test1 distanceFromLocation:test2]);
[test1 release];
[test2 release];
The output to this was 1907269.942754 vs 1908105.959114, a difference of almost 900 meters.
Though 900 meters may be a small percentage, I am trying to determine if the user is closer to something than an annotation and 900 meters can change that.
My question is, which one of these values, if any, is correct? Also if which location I test against is important, do I need to always test against the same location (e.g. user to destination and annotation to destination rather than destination to user and annotation to destination)?
Sorry that the sample is a bit out of context, as my project is rather large and this illustrates my problem. Any help would be appreciated as this has caused me quite a bit of confusion and frustration.
The error you're observing is 1 part in 2000. Presumably the algorithm used in this method is optimized for speed, so sorting a list of locations on distance is fast, rather than accurate to the millimeter.
If you need accurate results, don't use these methods but find another way. If you need reproducible results, order the arguments in a defined way, e.g. always start with the lower latitude.

Resources