Если я нахожусь здесь и направляюсь туда, и я прошел через это много, где я?
Мне нужна помощь в написании следующего метода:
def get_new_location(current_location, target_location, distance_travelled):
...
...
return new_location
где все локации (широта, лонг)
Я понимаю, что существуют разные модели Земли (WGS-84, GRS-80, ...), которые учитывают тот факт, что Земля является эллипсоидом. Для моих целей этот уровень точности не нужен, если предположить, что идеальная сфера достаточно хороша.
ОБНОВИТЬ
Я хорошо настраиваю свой вопрос, принимая во внимание некоторые ответы.
benjismith
утверждает, что на мой вопрос невозможно ответить, потому что между точками на земном шаре существует более одного кратчайшего пути. У него много поддержки в виде голосов, поэтому я думаю, что есть кое-что, чего я не понимаю, потому что я не согласен.
Средняя точка между любыми двумя точками на сфере - это круговая дуга.
Я признаю, что это правда, когда две точки находятся в полной противоположности. Под этим я подразумеваю, что обе точки, оставаясь на поверхности сферы, не могли быть дальше друг от друга. В этом случае существует бесконечное число эквидистантных путей, соединяющих обе точки. Это, однако, крайний случай, а не правило. Во всех остальных случаях, в подавляющем большинстве случаев, существует единственный кратчайший путь.
Чтобы проиллюстрировать: если бы вы держали строку, которая проходила через две точки, и тянули ее крепко, разве не было бы только одного возможного пути, по которому эта строка могла бы располагаться (кроме уже описанного случая ребра)?
Теперь, прежде чем задать вопрос, получить расстояние между двумя точками и курсом не было проблемой.
Я думаю, что я должен был спросить, является ли следующее:
def get_new_location(current_location, target_location, percent_traveled):
new_location.lon = (1-percent_traveled)*current_location.lon+percent_traveled*target_location.lon
new_location.lat = (1-percent_traveled)*current_location.lat+percent_traveled*target_location.lat
return new_location
Если бы я следовал по этому пути, я бы пошел по большому кругу, по прямой линии... или я был бы совершенно не в себе? (Я знаю эти термины сейчас из-за ответа Дрю Холла.)
5 ответов
Как сказал БенджиСмит, потенциально существует несколько путей, которые соединяют любые A & B на земном шаре, но два самых популярных (безусловно!) - это пути "большого круга" и "прямой линии".
Большой круг дает кратчайшее расстояние (путем построения плоскости из двух точек и центра Земли и следуя дуге окружности в этой плоскости).
Прямая линия поддерживает постоянный курс, торгуя на некотором расстоянии (может быть экстремальным в высоких широтах) для простоты использования. То есть в лодке или самолете вы просто указываете на желаемый курс и идете, пока не достигнете своего пункта назначения (тогда как при большом круге курс постоянно изменяется). В средних широтах штраф за расстояние не слишком строг.
Имейте в виду, что при работе с антиподальными точками (оба типа точек имеют противоположные стороны на сфере), оба типа путей имеют разрывы с полюсами и неоднозначности.
Чтобы построить большой круг, вам нужно преобразовать точки в трехмерные декартовы координаты (я опущу этот шаг, но он является тривиальным для сферической земли и найден итеративно для модели сплюснутой земли в виде WGS-84).
Пусть a будет единичным вектором, указывающим на начальную точку от центра Земли.
Пусть b будет единичным вектором, указывающим на конечную точку от центра земли.
Пусть r будет радиусом Земли.
Пусть d будет (заданным) пройденным расстоянием.
Построить единичный вектор, нормальный к плоскости ГХ, взяв перекрестное произведение единичных векторов a и b. То есть пусть n = axb.
(Заданное) пройденное расстояние - это длина дуги, образованной путем смещения вектора *r***a** вокруг n на некоторый угол тета. Вспоминая, что длина окружности полного большого круга равна 2 * pi * r, мы находим theta = d/r.
Таким образом, декартова точка, соответствующая новому местоположению, определяется вращением *r***a** вокруг n на тета- радианы. Преобразуйте эту декартову точку в лат / лонг, и все готово.
Я не буду выводить здесь математику прямой линии, но скажу, что у проекции карты Меркатора есть свойство прямой линии прямой линии. Вы можете легко построить прямую линию с помощью формулы проекции меркатора, но вам придется определить некоторую погрешность, чтобы разбить путь на короткие прямые отрезки.
Удачи!
По поводу вашего обновленного вопроса:
То, что вы делаете, это линейная интерполяция координат широты / долготы. Это верный путь, но это не большой круг или прямолинейная линия. Фактически, поскольку меридианы сходятся по мере увеличения широты (по крайней мере, в северном полушарии), плавная интерполяция в широтном смысле может привести к странно ускоряющемуся пути на земле.
Если бы вы описывали интерполяцию в декартовых координатах, вы бы, по крайней мере, двигались в правильной плоскости, но путь прорезал бы поверхность Земли (то есть это был бы аккорд на большом круге, а не дуга).
Вот пример кода, который должен помочь. Алгоритм работает для всех случаев и всегда следует по кратчайшему пути большого круга между двумя точками. Математика по сути идентична ответу Дрю Холла, но использует процент_путешествия и игнорирует радиус Земли.
Для простоты в этом коде предполагается, что широта и долгота хранятся в радианах.
def get_new_location(current_location, target_location, percent_traveled):
# convert locations into cartiesian co-ordinates
current_vector = location_to_vector(current_location)
target_vector = location_to_vector(target_location)
# compute the angle between current_vector and target_vector
complete_angle = acos(vector_dot_product(current_vector, target_vector))
# determine the current partial angle, based on percent_traveled
partial_angle = percent_traveled*complete_angle
# compute a temporary vector to simplify calculation
temporary_vector = vector_cross_product(current_vector, target_vector)
temporary_vector = vector_cross_product(current_vector, temporary_vector)
# calculate new_vector
scalar_one = cos(partial_angle)
scalar_two = -sin(partial_angle)/sin(complete_angle)
vector_one = vector_multiply_by_scalar(scalar_one, current_vector)
vector_two = vector_multiply_by_scalar(scalar_two, temporary_vector)
new_vector = vector_sum(vector_one, vector_two)
# convert new_vector back into latitude & longitude and return
new_location = vector_to_location(new_vector)
return new_location
функция для перевода из широты и долготы в декартовые кооридинаты: def location_to_vector(location)
vector.x = cos(location.lat)*sin(location.lon)
vector.y = sin(location.lat)
vector.z = cos(location.lat)*cos(location.lon)
return vector
функция для преобразования из декартовых кооридинатов в широту и долготу: def vector_to_location(vector)
location.lat = asin(vector.y)
if (vector.z == 0):
if (vector.x < 0):
location.lon = -pi/2
else:
location.lon = pi/2
else:
if (vector.z < 0):
if (vector.x < 0):
location.lon = atan(vector.x/vector.z) - pi
else:
location.lon = pi - atan(-vector.x/vector.z)
else:
if (vector.x < 0):
location.lon = -atan(-vector.x/vector.z)
else:
location.lon = atan(vector.x/vector.z)
return location
функция для вычисления скалярного произведения двух векторов: def vector_dot_product(A, B):
dot_product = A.x*B.x + A.y*B.y + A.z*B.z
return dot_product
функция для вычисления перекрестного произведения двух векторов: def vector_cross_product(A, B):
cross_product.x = A.y*B.z - A.z*B.y
cross_product.y = A.z*B.x - A.x*B.z
cross_product.z = A.x*B.y - A.y*B.x
return cross_product
функция для умножения вектора на скаляр: def vector_multiply_by_scalar(scalar, vector)
scaled_vector.x = scalar*vector.x
scaled_vector.y = scalar*vector.y
scaled_vector.z = scalar*vector.z
return scaled_vector
Функция для вычисления суммы двух векторов: def vector_sum(A, B)
sum.x = A.x + B.x
sum.y = A.y + B.y
sum.z = A.z + B.z
return sum
Ваш обновленный пример кода не всегда будет следовать по правильному пути.
Для быстрого примера рассмотрим следующие две точки на экваторе в середине Тихого океана:
- current_location: lat = 0, lon = -179
- target_location: lat = 0, lon = 179
Эти две точки расположены очень близко друг к другу (только на два градуса долготы друг от друга), но когда процент_путешествия равен 0,5, то новое_расположение будет равно: широта = 0, лон = 0, то есть точка на противоположной стороне земного шара.
редактировать: становится хуже
Рассмотрим следующие две точки в северном полушарии:
- current_location: lat = 80, lon = 0
- target_location: lat = 80, lon = 180
Траектория большого круга между этими двумя точками проходит непосредственно над северным полюсом, но new_location будет двигаться вокруг земного шара, оставаясь параллельным экватору.
Вот онлайн калькулятор, чтобы найти большое расстояние круга от долготы и широты. Все расчеты выполняются в клиентском JavaScript, поэтому вы можете просмотреть исходный код страницы, чтобы увидеть код. Кроме того, вот страница, выводящая уравнения, используемые в калькуляторе.