Я ожидал, что это будет просто, но я получаю некоторые странные результаты.Я был бы признателен, если бы кто-то мог указать, что я делаю неправильно.
У меня есть 3 точки (A, B, C), определенные на поверхности Земли (предполагается, что сфера) с [lat, long]
координаты для каждой точки.Мне нужно вычислить угол между двумя Великими Дугами, образованными AC и AB.
У меня уже есть функция, которая вычисляет Расстояние по Большому Кругу (GCD), поэтому я решил решить эту проблему, получив GCD дляAC, AB и CA, сводя их к единичной сфере, а затем применяя сферический закон косинусов, чтобы получить угол BAC.
Это сработало и дало мне разумные углы.Однако затем я попытался поставить все три точки на одном и том же Большом круге, и началась странная вещь.Если B и C были в пределах 1 градуса, результаты были разумными, но когда я начал перемещать B и C дальше друг от друга вдоль того же Великого круга, угол начал расти!
Например:
A = 49, 1
B = 49, 10 => Angle: 0.0378
C = 49, 10.1
A = 49, 1
B = 49, 10 => Angle: 0.2270
C = 49, 10.6
A = 49, 1
B = 49, 10 => Angle: 3.7988
C = 49, 20
A = 49, 1
B = 49, 10 => Angle: 99.1027
C = 49, 200
Это какая-то ошибка точности, или моя формула неверна?
Вот код (getDistance()
, как известно, работает):
public static BigDecimal getAngle(
final BigDecimal commonLat, final BigDecimal commonLong,
final BigDecimal p1Lat, final BigDecimal p1Long,
final BigDecimal p2Lat, final BigDecimal p2Long) {
// Convert real distances to unit sphere distances
//
double a = getDistance(p1Lat, p1Long, commonLat, commonLong).doubleValue() / RADIUS_EARTH;
double b = getDistance(p2Lat, p2Long, commonLat, commonLong).doubleValue() / RADIUS_EARTH;
double c = getDistance(p1Lat, p1Long, p2Lat, p2Long).doubleValue() / RADIUS_EARTH;
// Use the Spherical law of cosines to get at the angle between a and b
//
double numerator = Math.cos(c) - Math.cos(a) * Math.cos(b);
double denominator = Math.sin(a) * Math.sin(b);
double theta = Math.acos(numerator / denominator);
// Back to degrees
//
double angleInDegrees = Math.toDegrees(theta);
return new BigDecimal(angleInDegrees);
}
К сожалению для меня,мое приложение часто будет иметь точки почти на одной линии, поэтому важна точность в этой ситуации.Что здесь не так?
РЕДАКТИРОВАТЬ: В соответствии с запросом, вот код для getDistance()
:
public static BigDecimal getDistance(final BigDecimal endLat, final BigDecimal endLong,
final BigDecimal startLat, final BigDecimal startLong) {
final double latDiff = Math.toRadians(endLat.doubleValue() - startLat.doubleValue());
final double longDiff = Math.toRadians(endLong.doubleValue() - startLong.doubleValue());
final double lat1 = Math.toRadians(startLat.doubleValue());
final double lat2 = Math.toRadians(endLat.doubleValue());
double a =
Math.sin(latDiff / 2) * Math.sin(latDiff / 2) +
Math.sin(longDiff / 2) * Math.sin(longDiff / 2) * Math.cos(lat1) * Math.cos(lat2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double d = RADIUS_EARTH * c;
return new BigDecimal(d);
}
Объявление RADIUS_EARTH
не имеет значения, b / c мы умножаем наэто при расчете расстояния, а затем поделить на него при расчете угла, чтобы оно было исключено.