我偶然发现了可移动类型的地理空间计算公式的精彩页面。更好的是,大多数公式已经用 Javascript 编写,这对我的 Phonegap 应用程序来说非常酷。然而,引起我注意的公式是计算两点之间的跨轨距离的公式。就我的应用程序和现实世界的使用而言,这意味着即使 GPS 更新频率不足以错过我的目标位置的半径,我也可以根据最近位置和最近位置之间的路径计算用户是否访问了目标它的前身。
编辑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);
}
然而,现实世界的测试再次表明这并不能完全发挥作用。问题是这个函数给出了误报,因为它没有考虑到由两个端点和半径形成的边界框。这导致我添加了一个进一步的函数来将横向距离限制在该边界框内:
/**
* 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;
}
这是一个说明用例的小提琴
我花了很长时间和大量测试才想出这个,所以我希望它可以对其他人有所帮助:-)