仔细查看计算纬度/经度点之间距离、方位和更多内容,其中包含几个公式和JavaScript示例,这可能会对您有所帮助。我知道它不是Java,但应该足够简单,可以将代码移植过来。尤其是给出了公式的详细说明。
编辑:
虽然在较短距离内使用线性插值似乎没问题,但实际上可能相当偏差,特别是当您靠近极地时。从您在汉堡的例子中可以看出,这将在数百米内就会有明显的影响。请参见此答案以获得良好的解释。
问题:经度1度之间的距离取决于纬度。
这是因为地球不是平面而是一个球体 - 实际上是一个椭球体。因此,在二维地图上的直线在地球上 不是 直线 - 反之亦然。
为了解决这个问题,我们可以采用以下方法:
- 从起始坐标(L1)指向结束坐标(L2)获取方位角
- 沿着大圆路径从起始坐标(L1)计算一个新的坐标,给定计算出的方位角和指定的距离
- 重复此过程,但使用新计算出的坐标作为起始坐标
我们可以创建一些简单的函数来完成这个技巧:
double radius = 6371;
double DegToRad(double deg) {
return (deg * Math.PI / 180);
}
double RadToDeg(double rad) {
return (rad * 180 / Math.PI);
}
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);
return (RadToDeg(bearing) + 360) % 360;
}
Location CalculateDestinationLocation(Location point, double bearing, double distance) {
distance = distance / radius; // convert to angular distance in radians
bearing = DegToRad(bearing);
double lat1 = DegToRad(point.latitude);
double lon1 = DegToRad(point.longitude);
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;
return new Location(RadToDeg(lat2), RadToDeg(lon2));
}
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 - a));
return (radius * c);
}
使用平均地球半径为6371公里。有关这个数字及其准确性的解释,请参见维基百科。
现在,可以通过给定的行程距离(以公里为单位)计算两点之间的新中间位置:
double bearing = CalculateBearing(startLocation, endLocation)
Location intermediaryLocation = CalculateDestinationLocation(startLocation, bearing, distanceTravelled)
假设速度为v(例如1.39)米/秒,现在可以使用一个简单的for循环来获取相隔1秒的点:
List<Location> locations = new ArrayList<Location>();
for (int i = 0; i < duration; i++)
作为额外的奖励,您甚至可以确定在任意两点之间旅行所需的时间:
double distanceBetweenPoints = CalculateDistanceBetweenLocations(startPoint, endPoint) * 1000; // multiply by 1000 to get meters instead of km
double timeRequired = distanceBetweenPoints / v;
使用这种方法比仅使用坐标差的简单线性插值在任何距离上都能得到更高的精度。虽然该方法并不完美,但通常会有0.3%或更低的误差,这是相当可接受的。如果您需要更好的解决方案,可以考虑使用Vincenty公式。