如何测试在"现实世界"中是否访问过地理位置?

Dav*_*den 1 geolocation geospatial geo cordova

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

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

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

Dav*_*den 5

我偶然发现了Movable Type公式用于地理空间计算的这个奇妙的页面.更好的是,大多数公式都是用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);
}
Run Code Online (Sandbox Code Playgroud)

然而,现实世界的测试再次表明,这并不能完成这项工作.问题是这个函数给出了误报,因为它没有考虑到由两个端点和半径形成的边界框.这导致我添加了另一个函数来将crosstrack距离约束到该边界框内:

/**
 * 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));
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,可以使用它来确定是否已在真实场景中访问过目标位置:

// 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;
    }
Run Code Online (Sandbox Code Playgroud)

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

我花了很长时间和很多测试来提出这个,所以我希望它可以帮助其他人:-)