根据步行速度在2个GPS位置之间插值

问题:


鉴于两个位置:

L 1 =(纬度1 ,经度1 ,时间戳1L 2 =(纬度2 ,经度2 ,时间戳2

以及可配置但恒定的移动速度:

v =每秒1.39米 (例如)。

我们如何在这两个位置之间进行插值以估算用户从L 1L 2的位置


我一直在寻找这个问题的解决方案,到目前为止我发现,对于小距离(远离极点),可以使用线性插值。 所以,我在维基百科上查找了线性插值 ,发现了这个:

// Imprecise method which does not guarantee v = v1 when t = 1, // due to floating-point arithmetic error. float lerp(float v0, float v1, float t) { return v0 + t*(v1-v0); } 

所以我正在考虑使用这个lerp函数来插入L 1L 2之间的纬度和经度。 这很容易。 我如何计算t ? 我猜我必须计算一些时间增量,但我如何计算运动速度?


编辑:

我正在测试各种收集GPS位置的方法。 为此,我正在整个步行中记录航路点位置。 我需要使用移动速度在这些航点之间进行插值,以估计我沿着步行的位置。 然后我可以将我的结果与估算值进行比较,看看他们的表现如何。

例:

地图

我猜我必须计算一些时间增量,但我如何计算运动速度?

在线性插值中,在您的情况下,使用从开始时间t1到结束时间t2运行的迭代变量t,使用预定义步骤在两个时间点之间进行迭代。 假设步长= 1秒,这对您的应用程序非常有用。

 long t1 = location1.getTimeStamp(); // in milliseconds; long t2 = location2.getTimeStamp(); double deltaLat = location2.latitude - location1.latitude; doule deltaLon = location2.longitude- location1.longtude; // remove this line if you don't have measured speed: double deltaSpeed = location2.speed - location1.speed; long step = 1 * 1000; // 1 second in millis for (long t = t1; t1 < t2; t+= step) { // t0_1 shall run from 0.0 to (nearly) 1.0 in that loop double t0_1 = (t - t1) / (t2 - t1); double latInter = lat1 + deltaLat * t0_1; double lonInter = lon1 + deltaLon * t0_1; // remove the line below if you dont have speed double speedInter = speed1 + deltaSpeed * t0_1; Location interPolLocation = new Location(latInter, lonInter, speedInter); // add interPolLocation to list or plot. } 

仔细查看纬度/经度点之间的计算距离,方位等

它包含几个可能对您有帮助的公式和JavaScript示例。 我知道它不是Java,但它应该足够简单以移植代码。 特别是给出了配方的详细描述。

编辑:

虽然对于较短的距离使用线性插值似乎没问题,但实际上它可能非常偏离,特别是当你靠近极点时。 从你在汉堡的例子来看,这已经有了几百米的显着效果。 请参阅此答案以获得更好的解释。

问题:经度1度之间的距离根据您的纬度而变化很大。

这是因为地球不是平坦的,而是一个球体 – 实际上是一个椭圆体。 因此,二维地图上的直线不是地球上的直线 – 反之亦然。

要解决此问题,可以使用以下方法:

  1. 获取从起始坐标(L1)到结束坐标(L2)的方位
  2. 在给定计算的方位和指定距离的情况下,沿着大圆路径从起始坐标(L1)计算新坐标
  3. 重复此过程,但使用新计算的坐标作为起始坐标

我们可以创建一些简单的函数来为我们提供帮助:

 double radius = 6371; // earth's mean radius in km // Helper function to convert degrees to radians double DegToRad(double deg) { return (deg * Math.PI / 180); } // Helper function to convert radians to degrees double RadToDeg(double rad) { return (rad * 180 / Math.PI); } // Calculate the (initial) bearing between two points, in degrees double CalculateBearing(Location startPoint, Location endPoint) { double lat1 = DegToRad(startPoint.latitude); double lat2 = DegToRad(endPoint.latitude); double deltaLon = DegToRad(endPoint.longitude - startPoint.longitude); double y = Math.sin(deltaLon) * Math.cos(lat2); double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(deltaLon); double bearing = Math.atan2(y, x); // since atan2 returns a value between -180 and +180, we need to convert it to 0 - 360 degrees return (RadToDeg(bearing) + 360) % 360; } // Calculate the destination point from given point having travelled the given distance (in km), on the given initial bearing (bearing may vary before destination is reached) Location CalculateDestinationLocation(Location point, double bearing, double distance) { distance = distance / radius; // convert to angular distance in radians bearing = DegToRad(bearing); // convert bearing in degrees to radians double lat1 = DegToRad(point.latitude); double lon1 = DegToRad(point.logintude); double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distance) + Math.cos(lat1) * Math.sin(distance) * Math.cos(bearing)); double lon2 = lon1 + Math.atan2(Math.sin(bearing) * Math.sin(distance) * Math.cos(lat1), Math.cos(distance) - Math.sin(lat1) * Math.sin(lat2)); lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180 - + 180 degrees return new Location(RadToDeg(lat2), RadToDeg(lon2)); } // Calculate the distance between two points in km double CalculateDistanceBetweenLocations(Location startPoint, Location endPoint) { double lat1 = DegToRad(startPoint.latitude); double lon1 = DegToRad(startPoint.longitude); double lat2 = DegToRad(endPoint.latitude); double lon2 = DegToRad(endPoint.longitude); double deltaLat = lat2 - lat1; double deltaLon = lon2 - lon1; double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - 1)); return (radius * c); } 

这使用平均地球半径6371千米。 有关此数字及其准确性的说明,请参阅维基百科 。

在给定行进距离(以km为单位)的情况下,现在可以计算两点之间的新中间位置:

 double bearing = CalculateBearing(startLocation, endLocation); Location intermediaryLocation = CalculateDestinationLocation(startLocation, bearing, distanceTravelled); 

假设速度为v(例如1.39)米/秒,现在可以使用一个简单的for循环来获得相隔1秒的点:

 List locations = new ArrayList(); // assuming duration in full seconds for (int i = 0; i < duration; i++){ double bearing = CalculateBearing(startLocation, endLocation); double distanceInKm = v / 1000; Location intermediaryLocation = CalculateDestinationLocation(startLocation, bearing, distanceInKm); // add intermediary location to list locations.add(intermediaryLocation); // set intermediary location as new starting location startLocation = intermediaryLocation; } 

作为额外的奖励,您甚至可以确定在任意两点之间旅行所需的时间:

 double distanceBetweenPoints = CalculateDistanceBetweenLocations(startPoint, endPoint) * 1000; // multiply by 1000 to get meters instead of km double timeRequired = distanceBetweenPoints / v; 

与仅使用坐标的增量的简单线性插值相比,这将导致任何距离的精度更高。 虽然这种方法并不完美,但误差通常为0.3%或更低,这是完全可以接受的。 如果您需要更好的解决方案,您可能需要查看Vincenty公式。

还有一些其他插值策略比线性插值更好,包括运动插值,它将锚点的初始和最终速度作为输入。 作为示例,参见最近的论文(Long JA(2015)运动数据的运动插值.Inter J Geogr Inf Sci 8816:1-15.doi:10.1080 / 13658816.2015.1081909)的这种比较:

来自Long JA(2015)运动数据的运动插值。 Int J Geogr Inf Sci 8816:1-15。 doi:10.1080 / 13658816.2015.1081909

运动插值有R和Python实现。 编写Java版本应该很容易。

是的。 线性插值。

 L1 = (1, 2, 3) L2 = (4, 5, 6) desired_number_of_interpolation_points = 9 interpolation_points = [] lat_step = (L2[0] - L1[0]) / (desired_number_of_interpolation_points + 1) lon_step = (L2[1] - L1[1]) / (desired_number_of_interpolation_points + 1) time_step = (L2[2] - L1[2]) / (desired_number_of_interpolation_points + 1) for i in range(1, desired_number_of_interpolation_points + 1) interpolation_points.append((lat_step * i, lon_step * i, time_step * i)) 

如果你首先将你的纬度/长度转换为n向量( https://en.wikipedia.org/wiki/N-vector ),这样的计算实际上非常简单。 转换后你可以使用标准插值,你也可以避免长距离,极点或日期线的任何问题。

如果您在维基百科页面上查看“外部链接”,则会有一个页面( http://www.navlab.net/nvector/ )解决了10个问题,该页面上的问题6(插值位置)应该相同作为你的问题。 正如您所看到的,该解决方案对于任何距离都是精确的,并且可以在任何地球位置工作,例如两极。