Геопространственные координаты и расстояние в километрах
Это продолжение этот вопрос.
кажется, я застрял на этом. В принципе, мне нужно иметь возможность конвертировать туда и обратно ссылки на координаты либо в стандартной системе градусов, либо путем измерения расстояния к северу от южного полюса вдоль международной линии дат, а затем расстояния на Восток, начиная с этой точки На линии дат. Чтобы сделать это (а также некоторые более общие измерения расстояния), у меня есть один метод для определения расстояние между двумя точками lat/lon и другой метод, который принимает точку lat/lon, курс и расстояние и возвращает точку lat/lon в конце этого курса.
вот два статических метода, которые я определил:
/* Takes two lon/lat pairs and returns the distance between them in kilometers.
*/
public static double distance (double lat1, double lon1, double lat2, double lon2) {
double theta = toRadians(lon1-lon2);
lat1 = toRadians(lat1);
lon1 = toRadians(lon1);
lat2 = toRadians(lat2);
lon2 = toRadians(lon2);
double dist = sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(theta);
dist = toDegrees(acos(dist)) * 60 * 1.1515 * 1.609344 * 1000;
return dist;
}
/* endOfCourse takes a lat/lon pair, a heading (in degrees clockwise from north), and a distance (in kilometers), and returns
* the lat/lon pair that would be reached by traveling that distance in that direction from the given point.
*/
public static double[] endOfCourse (double lat1, double lon1, double tc, double dist) {
double pi = Math.PI;
lat1 = toRadians(lat1);
lon1 = toRadians(lon1);
tc = toRadians(tc);
double dist_radians = toRadians(dist / (60 * 1.1515 * 1.609344 * 1000));
double lat = asin(sin(lat1) * cos(dist_radians) + cos(lat1) * sin(dist_radians) * cos(tc));
double dlon = atan2(sin(tc) * sin(dist_radians) * cos(lat1), cos(dist_radians) - sin(lat1) * sin(lat));
double lon = ((lon1-dlon + pi) % (2*pi)) - pi;
double[] endPoint = new double[2];
endPoint[0] = lat; endPoint[1] = lon;
return endPoint;
}
и вот функция, которую я использую для ее тестирования:
public static void main(String args[]) throws java.io.IOException, java.io.FileNotFoundException {
double distNorth = distance(0.0, 0.0, 72.0, 0.0);
double distEast = distance(72.0, 0.0, 72.0, 31.5);
double lat1 = endOfCourse(0.0, 0.0, 0.0, distNorth)[0];
double lon1 = endOfCourse(lat1, 0.0, 90.0, distEast)[1];
System.out.println("end at: " + lat1 + " / " + lon1);
return;
}
значения "end at" должны быть appx. 72.0 / 31.5. Но вместо этого я получаю примерно 1.25 / 0.021.
Я предполагаю, что я, должно быть, пропустил что-то глупое, забыл где-то конвертировать единицы или что-то в этом роде... Любая помощь будет очень признательна!
обновление 1:
Я (правильно) написал функцию расстояния для возврата метров, но написал километры в комментариях по ошибке ... что, конечно, смутило меня, когда я вернулся к этому сегодня. Во всяком случае, теперь это исправлено, и я исправил ошибку факторинга в методе endOfCourse, и я также понял, что забыл преобразовать обратно в градусы из радианов в и этот метод тоже. В любом случае: хотя кажется, что теперь я получаю правильный номер широты (71.99...), номер долготы далеко (я получаю 3.54 вместо 11.5).
обновление 2: У меня была опечатка в тесте, как указано ниже. Теперь это зафиксировано в коде. Однако номер долготы по-прежнему неверен: теперь я получаю -11.34 вместо 11.5. Я думаю, что что-то не так с этими строками:
double dlon = atan2(sin(tc) * sin(dist_radians) * cos(lat1), cos(dist_radians) - sin(lat1) * sin(lat));
double lon = ((lon1-dlon + pi) % (2*pi)) - pi;
5 ответов
у вас серьезный случай с магическими числами в коде. Выражение:
(60 * 1.1515 * 1.609344 * 1000)
появляется дважды, но нет большого объяснения этому. С некоторой помощью: 1.609344-количество километров в миле; 60-количество минут в градусе; 1000-количество метров в километре; и 1.1515-количество статутных миль в морской миле (спасибо, Данм). Одна морская миля-длина одной минуты широты на экваторе.
I предположим, вы используете сферическую модель Земли, а не сфероидальную землю? Алгебра недостаточно сложна, чтобы быть сфероидальной.
первая формула-преобразование между двумя парами широты и долготы - нечетно. Вам нужно как delta-lat (Δλ), так и delta-lon (Δφ), чтобы отсортировать ответ. Далее, расстояние между парами:
(60° N, 30° W), (60° N, 60° W)
(60° N, 60° W), (60° N, 90° W)
должно быть то же самое, но я уверен, что ваш код дает разные ответы.
Итак, я думаю, вам нужно вернитесь к своим сферическим тригонометрическим справочным материалам и посмотрите, что вы делаете неправильно. (Мне потребуется некоторое время, чтобы найти мою книгу на эту тему - ее нужно будет распаковать из какой бы коробки она ни была.)
[...течение времени...распаковка закончена...]
дан сферический треугольник с углами A, B, C на вершинах и по бокам a, b, c напротив этих вершин (то есть в сторону a С B to C, etc.), Формула Косинуса:
cos a = cos b . cos c + sin b . sin c . cos A
применяя это к проблеме, мы можем назвать две точки, заданные B и C, и мы создаем правый сферический треугольник с прямым углом в A.
ASCII искусство в худшем виде:
+ C
/|
/ |
a / | b
/ |
/ |
/ |
B +------+ A
c
боковая c равно разнице в долгота; сторона b равно разнице в широте; угол A это 90°, так что cos A = 0. Поэтому, я считаю, уравнение для a - это:
cos a = cos Δλ . cos Δφ + sin Δλ . sin Δφ . cos 90°
a = arccos (cos Δλ . cos Δφ)
угол a в радианах затем преобразуется в расстояние путем умножения на радиус Земли. В качестве альтернативы, учитывая a в градусах (и долях градуса), то есть 60 морских миль до одного градуса, следовательно, 60 * 1.1515 статутные мили и 60 * 1.1515 * 1.609344 километры до одного градуса. Если вы не хотите расстояние в метрах, я не вижу необходимости в коэффициенте 1000.
пол Томблин указывает на авиационный формуляр v1.44 как источник уравнения-и действительно, он есть, вместе с более численно стабильной версией, когда разница в положении мала.
переходя к основной тригонометрии, мы также знаем, что:
cos (A - B) = cos A . cos B + sin A . sin B
применение это дважды в уравнении, которое я дал, вполне может закончиться формулой в Формуле авиации.
(моя ссылка: "Астрономия: принципы и практика, четвертое издание" by a e Roy and D Clarke (2003); моя копия-первое издание 1977 года, Adam Hilger, ISBN 0-85274-346-7.)
NB Проверьте (Google)"определить:" морская миля"; похоже, что морская миля теперь 1852 м (1,852 км) по определению. Множитель 1.1515 соответствует старому определению морской мили как приблизительно 6080 футов. Используя bc
со шкалой 10, я получаю:
(1852/(3*0.3048))/1760
1.1507794480
какой фактор работает для вас, зависит от того, какова ваша основа.
глядя на вторую задачу из первых принципов, у нас есть немного другая настройка, и нам нужно "другое" сферическое уравнение тригонометрии, Формула синуса:
sin A sin B sin C
----- = ----- = -----
sin a sin b sin c
адаптация предыдущей схеме:
+ C
/|
/ |
a / | b
| / |
|X/ |
|/ |
B +------+ A
c
вы даны исходные точки B, угол X = 90º - B, длина (угол) a и угол A = 90°. Что вы после b (дельта по широте) и c (дельта по долготе).
Итак, мы имеем:
sin a sin b
----- = ----
sin A sin B
или
sin a . sin B
sin b = -------------
sin A
или, поскольку A = 90°, sin A = 1, и sin B = sin (90° - X) = cos X:
sin b = sin a . cos X
это означает, что вы конвертируете расстояние путешествовал под углом a, возьмите синус этого, умножьте на косинус направления курса и возьмите дугу результата.
дано a, b (только что подсчитано) и A и B, мы можем применить формулу Косинуса получить c. Обратите внимание, что мы не можем просто повторно применить формулу синуса, чтобы получить c так как у нас нет значения C и, потому что мы играя со сферической тригонометрией, нет удобного правила, что C = 90° - B (сумма углов в сферическом треугольнике может быть больше 180°; рассмотрим равносторонний сферический треугольник со всеми углами, равными 90°, что вполне осуществимо).
проверить http://www.movable-type.co.uk/scripts/latlong.html
на этом сайте есть много разных формул и кода Javascript, которые должны вам помочь. Я успешно перевел его на C# и SQL Server UDF, и я использую их повсюду.
например, для Javascript для вычисления расстояния:
var R = 6371; // km
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2-lat1).toRadians();
var Δλ = (lon2-lon1).toRadians();
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
наслаждайтесь!
ваше преобразование между км и радианами неверно. Морская миля составляет 1/60 градуса, так что предположим, что 1.15... это ваше преобразование из миль в морские мили и 1.6... является ли ваше преобразование из км в статутные мили,
nm = km / (1.1515 * 1.609344);
deg = nm / 60;
rad = toRadians(deg);
другими словами, Я думаю, что вы ошибаетесь в 1000 раз.
относительно вашего обновленного вопроса: не следует
double lon1 = endOfCourse(lat1, 0.0, 90.0, distEast)[0];
быть
double lon1 = endOfCourse(lat1, 0.0, 90.0, distEast)[1];
Я понял большую проблему с этими формулами, помимо ошибок реализации, упомянутых в других ответах и обновлениях.
большая проблема заключалась в следующем: метод расстояния (для вычисления расстояния между двумя точками) вычислял расстояния большого круга. Который, конечно, имеет смысл-это кратчайший путь между двумя точками. , расстояние большого круга между двумя точками, которые лежат на одной параллели (линия широты) Не то же самое, что расстояние между этими двумя точками при движении прямо по линии широты, если вы не находитесь на экваторе.
Итак: функции работают правильно; однако альтернативная система координат, которую я предложил в исходном вопросе, требует, чтобы мы смотрели только на расстояние на север вдоль IDL, а затем на расстояние на восток вдоль параллели на результирующей широте. И вычисление расстояния вдоль определенной параллели сильно отличается от расчет расстояния по большому кругу!
в любом случае, вот оно.