1

好的,所以我已经有了这个问题的答案,但我花了很长时间才找到它,所以我想我会分享它,特别是因为有人问我但在一个不相关的问题下

我为引导步行创建了一个基于 Phonegap 的导航应用程序,该应用程序使用 GPS 跟踪用户在预定义路线周围的位置。我的路线在路线周围的特定地理位置放置了触发器,这些触发器根据用户的当前位置向用户提供指示。问题是 GPS 不是超级准确或可靠,所以即使我允许在大约 20 米的位置周围有一个“访问半径”,实际测试导致这些触发器有时会被错过,因为 GPS 位置更新会稍早发生用户输入访问半径并稍稍稍晚一些。我尝试增加半径,但这意味着触发器触发得太早,无法与用户的当前位置相关。

那么如何以使用“真实世界”GPS 数据的方式解决这个问题呢?

4

1 回答 1

5

我偶然发现了可移动类型的地理空间计算公式的精彩页面。更好的是,大多数公式已经用 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;
    }

这是一个说明用例的小提琴

我花了很长时间和大量测试才想出这个,所以我希望它可以对其他人有所帮助:-)

于 2013-06-10T19:26:19.830 回答