私は地理空間計算のための数式の Movable Type によってこの素晴らしいページに出くわしました。さらに良いことに、数式のほとんどはすでに Javascriptで記述されており、これは私の Phonegap アプリにとって非常にクールでした。しかし、私の注意を引いたのは、2 点間のクロス トラック距離を計算するための次の式でした。私のアプリと実際の使用に関して言えば、これは、GPS の更新がターゲットの位置の半径を逃すほど頻繁ではない場合でも、ユーザーがターゲットにアクセスしたかどうかを、最新の位置とその前身。
EDIT 24/04/15:constrainedCrossTrackDistance
関数のバグを修正したので、それを使用している人は誰でも実装をこの回答のものに更新する必要があります。
JS ライブラリにはこの数式が含まれていなかったので、Movable Type ライブラリを拡張して実装しました。
/**
* 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's 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);
}
ただし、実際のテストでは、これがうまくいかないことが再び示されました。問題は、この関数が 2 つの端点と半径によって形成されるバウンディング ボックスを考慮していなかったため、誤検知が発生していたことです。これにより、クロストラック距離をその境界ボックス内に制限する関数をさらに追加することになりました。
/**
* 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));
}
}
これを使用して、実際のシナリオでターゲット位置が訪問されたかどうかを判断できます。
// 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;
}
これはユースケースを示すフィドルです
これを思いつくのに長い時間と多くのテストが必要だったので、他の人の助けになることを願っています:-)