Геопространственные координаты и расстояние в километрах

Это продолжение этого вопроса.

Кажется, я зациклился на этом. По сути, мне нужно иметь возможность конвертировать туда и обратно, чтобы ссылаться на координаты либо в стандартной системе градусов, либо путем измерения расстояния к северу от южного полюса вдоль международной линии дат, а затем расстояния на восток, начиная с этой точки на дату. линия. Для этого (а также для некоторых более общих задач измерения расстояния) у меня есть один метод определения расстояния между двумя точками широты / долготы и другой метод, который берет точку широты / долготы, направление и расстояние и возвращает точка широты / долготы в конце этого курса.

Вот два статических метода, которые я определил:

/* 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;
}

Значения «конца в» должны быть прибл. 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;

person DanM    schedule 23.12.2008    source источник
comment
В качестве справочного материала вы можете посетить movable-type.co. uk / scripts / latlong-vincenty.html и movable-type.co.uk/scripts/latlong-vincenty-direct.html (реализации JavaScript включены.   -  person Brendan Cashman    schedule 23.12.2008
comment
В вашей первой функции было бы лучше использовать две переменные: 'double angle = ... 1st expression; double dist = ... 2-е выражение изменено на опорный угол; '. Фактически, первое значение - это вовсе не расстояние. Просто педантичная возня.   -  person Jonathan Leffler    schedule 24.12.2008


Ответы (5)


У вас серьезный случай с магическими числами в коде. Выражение:

 (60 * 1.1515 * 1.609344 * 1000)

появляется дважды, но этому не так много объяснений. С некоторой справкой: 1,609344 - количество километров в миле; 60 - количество минут в градусе; 1000 - количество метров в километре; и 1,1515 - количество статутных миль в морской миле (спасибо, DanM). Одна морская миля - это длина одной минуты широты на экваторе.

Я предполагаю, что вы используете сферическую модель Земли, а не сфероидальную Землю? Алгебра недостаточно сложна, чтобы быть сфероидальной.

Первая формула - преобразование между двумя парами широты и долготы - нечетная. Вам нужны как дельта-широта (Δλ), так и дельта-долгота (Δφ), чтобы разобраться в ответе. Далее расстояние между парами:

(60° N, 30° W), (60° N, 60° W)
(60° N, 60° W), (60° N, 90° W)

должно быть таким же, но я уверен, что ваш код дает разные ответы.

Итак, я думаю, вам нужно вернуться к справочным материалам по сферической тригонометрии и посмотреть, что вы делаете не так. (Мне потребовалось бы время, чтобы найти свою книгу по этой теме - ее нужно было бы распаковывать из той коробки, в которой она находится.)

[... время идет ... распаковка завершена ...]

Дан сферический треугольник с углами A, B, C в вершинах и сторонах a, b , c напротив этих вершин (то есть сторона a находится от B до C и т. д. .), формула косинуса:

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

Применение этого дважды в приведенном мною уравнении вполне может привести к формуле из авиационного формуляра.

(Моя ссылка: "Астрономия: принципы и практика, четвертое издание" А.Е. Роем и Д. Кларком (2003 г.); моя копия - первое издание 1977 г., Адам Хильгер, ISBN 0-85274-346-7.)


NB Проверьте (Google) 'define: "морская миля"'; Судя по определению, морская миля составляет 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

Or

        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, мы можем применить формулу косинуса, чтобы получить < em> c. Обратите внимание, что мы не можем просто повторно применить формулу синуса, чтобы получить c, поскольку у нас нет значения C и, поскольку мы играем со сферической тригонометрией, там Нет удобного правила, что C = 90 ° - B (сумма углов в сферическом треугольнике может быть больше 180 °; рассмотрим равносторонний сферический треугольник со всеми углами, равными 90 °, что вполне возможно).


person Jonathan Leffler    schedule 23.12.2008
comment
1,1515 - это перевод статутных миль в морские мили. - person DanM; 23.12.2008

Посетите http://www.movable-type.co.uk/scripts/latlong.html

На этом сайте есть много разных формул и кода Javascript, которые должны вам помочь. Я успешно перевел его как на C #, так и на UDF SQL Server, и я использую их повсюду.

Например, для 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; 

Наслаждаться!

person user19371    schedule 23.12.2008

Ваше преобразование между км и радианами неверно. Морская миля составляет 1/60 градуса, поэтому предположим, что 1,15 ... это ваше преобразование из миль в морские мили, а 1,6 ... это преобразование из км в уставные мили,

   nm = km /  (1.1515 * 1.609344);
   deg = nm / 60;
   rad = toRadians(deg);

Другими словами, я думаю, что вы ошиблись в 1000 раз.

person Paul Tomblin    schedule 23.12.2008
comment
Ага. Я не имею ни малейшего представления, о чем я думал с помощью метода Distance; возвращает расстояние в метрах, а не в километрах. - person DanM; 23.12.2008

Что касается вашего обновленного вопроса: не следует

double lon1 = endOfCourse(lat1, 0.0, 90.0, distEast)[0];

be

double lon1 = endOfCourse(lat1, 0.0, 90.0, distEast)[1];
person Paul Tomblin    schedule 23.12.2008

Я выяснил большую проблему с этими формулами, помимо ошибок реализации, упомянутых в других ответах и ​​обновлениях.

Большая проблема заключалась в следующем: метод Distance (для вычисления расстояния между двумя точками) вычислял расстояния по большому кругу. Что, конечно, имеет смысл - это кратчайший путь между двумя точками. Однако расстояние по дуге большого круга между двумя точками, лежащими на одной параллели (линии широты), НЕ совпадает с расстоянием между этими двумя точками при движении непосредственно по линии широты, если только вы не на экваторе.

Итак: функции РАБОТАЮТ правильно; однако альтернативная система координат, которую я предложил в исходном вопросе, требует, чтобы мы смотрели только на расстояние на север вдоль IDL, а затем на расстояние на восток вдоль параллели на полученной широте. И вычисление расстояния по определенной параллели сильно отличается от вычисления расстояния по большому кругу!

В любом случае, вот оно.

person DanM    schedule 26.12.2008