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

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.

Related

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

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))
}

Finding a gps locations near another one based on a dynamic radius

I have aGpsLocation model, this model has a latitude, a longitude and a radius property.
I want to find a GpsLocation based on its latitude and longitude and radius.
So lets say i am on location [52, 4], i want to find a GpsLocation instance that has these exact coordinates or is radius meters away.
I am using the geocoder gem but the near function does not do the job. Also tried monkey patching the near function so i can use a database column instead of a variable inside the query but still no luck, there are some mechanics that transform the lat/long to a range when providing a radius parameter.
Any help is appreciated
Calculating the distance between two points on a sphere can be done using the Haversine formula. There is a gem called haversine that can help. Or, if you're using PostgreSQL, there is also the earthdistance module.
However, you may find it easier to relax your definition of "near" somewhat, as dealing with circles can be annoying. Instead, consider using squares or rectangles.
Say you define "near" as within 10 miles. If you treat that as a plus or minus factor around the latitude and longitude of a given point, then you can do a simple query to find all nearby points at once, rather than a series of Haversine calculations. You query might look something like this:
# Only accurate in the U.S.
MILES_PER_LATITUDE = 69.0
MILES_PER_LONGITUDE = 55.0
min_latitude = thisLocation.latitude - (10.0 / MILES_PER_LATITUDE)
max_latitude = thisLocation.latitude + (10.0 / MILES_PER_LATITUDE)
min_longitude = thisLocation.longitude - (10.0 / MILES_PER_LONGITUDE)
max_longitude = thisLocation.longitude + (10.0 / MILES_PER_LONGITUDE)
nearby_points = GpsLocation.where(latitude: min_latitude..max_latitude).where(longitude: min_longitude..max_longitude)

Shortest Path between multiple locations in MKMapView

I am trying to develop an application using MKMapView which needs to plot shortest path.Following is the scenario.
I have 5 to 10 coordinates,which are plotted on the map.I have to draw the shortest path to cover all these locations from my current location.This is a travel app,so need to show optimum distance .Please share your thoughts.I had a thought of finding distance to each point from current location but this needs to send multiple request at a time ,which seems to be very hectic process because the point can be 10 to 100.
Is there any solution for this available in Google Maps SDK?
Looking forward for great ideas and suggestions.
Thanks in advance...
You can use the google maps directions api (https://developers.google.com/maps/documentation/directions/#JSON) to find the shortest path. Then you have two options:
Draw it in a GMSMapView
Decode the encoded polilyne returned by google with the Google maps SDK and translate it to a MKPolyline to show it in a MKMapView.
Here's a snippet in Obj-c for the second option:
GMSPath *path = [GMSPath pathFromEncodedPath:encodedPolyline];
if (path.count != 0){
CLLocationCoordinate2D points[path.count];
for (NSInteger i = 0; i < path.count; i++){
CLLocationCoordinate2D coordinate = [path coordinateAtIndex:(NSUInteger) i];
points[i] = coordinate;
}
MKPolyline *p = [MKPolyline polylineWithCoordinates:points count:path.count];
[self.mapView addOverlay:p];
}
You have some limitation using the directions API, you can see it here: https://developers.google.com/maps/documentation/directions/usage-limits
You can use Directions API. You can plot multiple locations by setting a start point, waypoints(optional) and an endpoint. For example you are to plot 5 different locations, your current location(startpoint), then three waypoints or stopovers and the endpoint. Directions API automatically calculates distance based on the order in which you put the waypoints. Routes can recalculated if you use optimize:true, by doing so it will rearrange the waypoints which will give you the shortest possible distance from your start point to the endpoint. This is only effective for a maximum of 8 places because of the number of limits of waypoints. There is a workaround for this, the idea is to make the last waypoint(end route) of the first route to be the start point of the next route. Here is a link for your reference and the code which is also in the reference.

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.

Check if user is near route checkpoint with GPS

Here's the situation:
I have a predetermined GPS route that the user will run. The route has some checkpoints and the user should pass near all of them (think of them as a racing game checkpoint, that prevents the user from taking shortcuts). I need to ensure that the user passes through all the checkpoints.
I want to determine an area that will be considered inside a checkpoint's radius, but I don't want it to be just a radial area, it should be an area taking into consideration the form of the path.
Didn't understand it? Neither did I. Look at this poorly drawn image to understand it better:
The black lines represents the pre-determined path, the blue ball is the checkpoint and the blue polygon is the wanted area. The green line is a more precise user, and the red line is a less accurate user (a drunk guy driving maybe? lol). Both lines should be inside the polygon, but a user who skips totally the route shouldn't.
I already saw somewhere here a function to check is the user is inside a polygon like this, but I need to know how to calculate the polygon.
Any suggestions?
EDIT:
I'm considering using the simple distanceTo() function to just draw an imaginary circle and check if the user is there. That's good because is so much simple to implement and understand, and bad because to make sure the most erronic user passes whithin the checkpoint I would need a big radius, making the correct user enter the checkpoint area sooner than expected.
And just so you guys understand the situation better, this is for an app that is supposed to be used in traffic (car or bus), and the checkpoints should be landmarks or spots that divides your route, for example, somewhere where traffic jam starts or stops.
You could just check the distance between the two, assuming you know the GeoLocation of the checkpoint.
Use the distanceTo function and setup a threshold of however many meters the user needs to be from the checkpoint to continue on.
Edit
Since you want to avoid distanceTo, here is a small function I wrote a while back to check if a point is in a polygon:
public boolean PIP(Point point, List<Point> polygon){
boolean nodepolarity=false;
int sides = polygon.size();
int j = sides -1;
for(int i=0;i<sides;i++){
if((polygon.get(i).y<point.y && polygon.get(j).y>=point.y) ||(polygon.get(j).y<point.y && polygon.get(i).y>=point.y)){
if (polygon.get(i).x+(point.y-polygon.get(i).y)/(polygon.get(j).y-polygon.get(i).y)*(polygon.get(j).x-polygon.get(i).x)<point.x) {
nodepolarity=!nodepolarity;
}
}
j=i;
}
return nodepolarity; //FALSE=OUTSIDE, TRUE=INSIDE
}
List<Point> polygon is a list of the points that make up a polygon.
This uses the Ray casting algorithm to determine how many intersections a ray makes through the polygon.
All you would need to do is create the 'boundary' around the area you need with GeoPoints being translated into pixels using the toPixels method.
Store those points into a List<> of points, and you should be all set.
check a few algos to do this in the link below
http://geospatialpython.com/2011/01/point-in-polygon.html
I know this is an old question, but maybe it would be useful for someone.
This is a simpler method, with much less computation needed. This would not trigger the first time the user comes inside the threshold area, it only gets the closest point where the user has passed near the checkpoint AND (s)he has come close enough.
The idea is to maintain a 3 item list of distances for every checkpoint, with the last three distances in it (so it would be [d(t), d(t-1), d(t-2)]). This list should be rotated on every distance calculation.
If on any distance calculation the previous d(t-1) distance is smaller than the current one d(t) and bigger than the preceding d(t-2), then the moving point has passed the checkpoint. Whether this was a real passing, or it was only a glitch, can be decided by checking the actual distance d(t-1).
private long DISTANCE_THRESHOLD = 2000;
private Checkpoint calculateCheckpoint(Map<Checkpoint, List<Double>> checkpointDistances)
{
Map<Checkpoint, Double> candidates = new LinkedHashMap<Checkpoint, Double>();
for (Checkpoint checkpoint: checkpointDistances.keySet())
{
List<Double> distances = checkpointDistances.get(checkpoint);
if (distances == null || distances.size() < 3)
continue;
if (distances.get(0) > distances.get(1) && distances.get(1) < distances.get(2) && distances.get(1) < (DISTANCE_THRESHOLD)) //TODO: make this depend on current speed
candidates.put(checkpoint, distances.get(1));
}
List<Entry<Checkpoint, Double>> list = new LinkedList<Entry<Checkpoint,Double>>(candidates.entrySet());
Collections.sort(list, comp);
if (list.size() > 0)
return list.get(0).getKey();
else
return null;
}
Comparator<Entry<Checkpoint, Double>> comp = new Comparator<Entry<Checkpoint,Double>>()
{
#Override
public int compare(Entry<Checkpoint, Double> o1, Entry<Checkpoint, Double> o2)
{
return o1.getValue().compareTo(o2.getValue());
}
};
The function gets one parameter - a Map<Checkpoint, List<Double>> with the checkpoints and the list of the last three distances. It outputs the closest Checkpoint passed or null (if there were none).
The DISTANCE_THRESHOLD should be chosen wisely.
The Comparator is just to be able to sort the Checkpoints based on their distance to the user to get the closest one.
Naturally this has some minor flaws, e.g. if the moving point is moving criss-cross, or the error movement from GPS precision is commensurable with the actual speed of the user, than this would give multiple pass marks, but this would hit almost any algorithm.

Resources