How to check if a geographical location has been visited in the "real world"?

Ok, so I already have the answer to this question, but it took me a long time to get to it, so I decided to share it, especially since someone asked me, but under an unrelated question

I created a Phonegap-based navigation app for guided walks that tracks the user's location along a predefined route using GPS. My routes have triggers located at certain geographical points around the route that give instructions to the user depending on their current location. The problem is that GPS is not ultra-thin or reliable, therefore, although I allowed a "radius of visit" around these places of about 20 meters, in the real world testing led to the fact that these triggers were sometimes missed due to the fact that the position update GPS happened a little earlier, the user entered the radius of visit and a little later. I tried to increase the radius, but that meant that the trigger fired too early to relate to the user's current position.

So, how can I solve this problem the way it works using "real" GPS data?

+1
source share
1 answer

I came across this fantastic page on Movable Type Formulas for Geospatial Computing. Moreover, most of the formulas are already written in Javascript , which was super-cool for my Phonegap application. However, the formula that caught my attention was this one for calculating the distance between tracks between two points. As for my application and use in the real world, this means that even if GPS updates are not uncommon to skip the radius of my target location, I can calculate whether the user visited the target based on the path between the last position and its predecessor.

EDIT 24/04/15: I fixed the error in the constrainedCrossTrackDistance function, so anyone who uses it should update their implementation to the one in this answer.

The JS library did not include this formula, so I expanded the Movable Type library to implement it:

 /** * Calculates distance of a point from a great-circle path (also called cross-track distance or cross-track error) * * Formula: dxt = asin(sin(d13/R)*sin(b13-b12)) * R * where * d13 is distance from start point to third point * b13 is (initial) bearing from start point to third point * b12 is (initial) bearing from start point to end point * R is the earth radius * * @param {LatLon} startPoint - Point denoting the start of the great-circle path * @param {LatLon} endPoint - Point denoting the end of the great-circle path * @param {Number} [precision=4] - no of significant digits to use for calculations and returned value * @return {Number} - distance in km from third point to great-circle path */ LatLon.prototype.crossTrackDistance = function(startPoint, endPoint, precision){ var R = this._radius; var d13 = startPoint.distanceTo(this, 10); var b13 = startPoint.bearingTo(this).toRad(); var b12 = startPoint.bearingTo(endPoint).toRad(); var d = Math.asin(Math.sin(d13/R)*Math.sin(b13-b12)) * R; return d.toPrecisionFixed(precision); } 

However, testing in the real world again showed that this did not quite do the job. The problem was that this function gave false positives because it did not take into account its bounding box formed by two endpoints and a radius. This made me add another function to limit the cross-track distance within this bounding box:

 /** * Calculates distance of a point from a great-circle path if the point is within the bounding box defined by the path. * Otherwise, it returns the distance from the point to the closest end of the great-circle path. * * @param {LatLon} startPoint - Point denoting the start of the great-circle path * @param {LatLon} endPoint - Point denoting the end of the great-circle path * @param {Number} [precision=4] - no of significant digits to use for calculations and returned value * @return {Number} - distance in km from third point to great-circle path */ LatLon.prototype.constrainedCrossTrackDistance = function(startPoint, endPoint, precision){ var bAB = startPoint.bearingTo(endPoint); var bAB_plus_90 = Geo.adjustBearing(bAB, 90); var bAB_minus_90 = Geo.adjustBearing(bAB, -90); var bAC = startPoint.bearingTo(this); var bBC = endPoint.bearingTo(this); var dAC = startPoint.distanceTo(this, 10); var dBC = endPoint.distanceTo(this, 10); if(Geo.differenceInBearings(bAC, bBC) > 90 && ((bBC > bAB_plus_90 && bAC < bAB_plus_90) || (bAC > bAB_minus_90 && bBC < bAB_minus_90))){ return Math.abs(this.crossTrackDistance(startPoint, endPoint, precision)); }else if((bBC < bAB_plus_90 && bAC < bAB_plus_90) || (bBC > bAB_minus_90 && bAC > bAB_minus_90)){ return Math.abs(dBC); }else if((bBC > bAB_plus_90 && bAC > bAB_plus_90) || (bBC < bAB_minus_90 && bAC < bAB_minus_90)){ return Math.abs(dAC); }else{ return (Math.abs(dBC) < Math.abs(dAC) ? Math.abs(dBC) : Math.abs(dAC)); } } 

This can then be used to determine if the target position has been visited in the real scenario:

 // Calculate if target location visited if( currentPos.distanceTo(targetPos)*1000 < tolerance || (prevPos && Math.abs(targetPos.constrainedCrossTrackDistance(prevPos, currentPos)*1000) < tolerance) ){ visited = true; }else{ visited = false; } 

Here is a script illustrating a usage example

It took me a lot of time and a lot of testing to come up with this, so I hope this can help other people :-)

+5
source

Source: https://habr.com/ru/post/1482570/


All Articles