Гаверсинус: расстояние на сфере
Урок выводит и реализует формулу гаверсинуса — стандартный способ найти расстояние между двумя точками на сфере по их широте и долготе.
Гаверсинус — формула для расстояния по дуге большого круга между двумя точками сферы, заданными широтой и долготой.
Возникает соблазн: раз у нас есть координаты двух городов, посчитаем расстояние по теореме Пифагора, как на плоскости. Это грубая ошибка. Во-первых, Земля круглая, и кратчайший путь идёт по дуге, а не по прямой сквозь планету. Во-вторых, градусы широты и долготы — не метры: один градус долготы у экватора это 111 км, а у полюса почти ноль. Евклидова формула на координатах в градусах даёт бессмыслицу.
Идея большого круга
Кратчайший путь между двумя точками на сфере лежит на большом круге — окружности, центр которой совпадает с центром сферы (экватор и меридианы — большие круги). Длина этой дуги и есть искомое расстояние. Формула гаверсинуса аккуратно вычисляет его, оставаясь точной даже для близких точек, где наивные формулы теряют разряды.
Формула
Пусть $\Delta\phi = \phi_2 - \phi_1$ — разница широт, $\Delta\lambda = \lambda_2 - \lambda_1$ — разница долгот (всё в радианах). Тогда:
$a = \sin^2\!\left(\frac{\Delta\phi}{2}\right) + \cos\phi_1\,\cos\phi_2\,\sin^2\!\left(\frac{\Delta\lambda}{2}\right)$
$d = 2R\,\arcsin\!\left(\sqrt{a}\right)$
Здесь $R$ — радиус Земли (берём средний $6371$ км). Величина $a$ — это «гаверсинус» центрального угла; вторая строка переводит угол в длину дуги.
Реализация
import math
def haversine(lat1, lon1, lat2, lon2):
R = 6371.0 # км
p1 = math.radians(lat1)
p2 = math.radians(lat2)
dphi = math.radians(lat2 - lat1)
dlam = math.radians(lon2 - lon1)
a = (math.sin(dphi / 2) ** 2
+ math.cos(p1) * math.cos(p2) * math.sin(dlam / 2) ** 2)
return 2 * R * math.asin(math.sqrt(a))
# Москва -> Санкт-Петербург
d = haversine(55.7558, 37.6173, 59.9343, 30.3351)
print(f"Москва - Санкт-Петербург: {d:.1f} км")
# Москва -> Нью-Йорк
d2 = haversine(55.7558, 37.6173, 40.7128, -74.0060)
print(f"Москва - Нью-Йорк: {d2:.0f} км")
Вывод:
Москва - Санкт-Петербург: 633.0 км Москва - Нью-Йорк: 7510 км
633 км между Москвой и Питером — близко к официальным 635 км по прямой; расхождение в пределах округления и того, что Земля не идеальный шар.
Как работает под капотом
Почему не просто $d = R \cdot \arccos(\dots)$ через скалярное произведение? Потому что для близких точек аргумент арккосинуса близок к единице, где функция «плоская», и из-за конечной точности float теряются значащие цифры — близкие города могут получить расстояние ноль. Гаверсинус использует синус половинного угла, который для малых углов ведёт себя устойчиво, поэтому формула точна на всём диапазоне. Для сверхточных задач берут формулу Винсенти, учитывающую сплюснутость эллипсоида, — она сложнее, но добавляет доли процента точности.
Частые ошибки
- Считать по Пифагору на градусах. Это даёт бессмысленные «градусные» числа и игнорирует кривизну.
- Забыть радианы.
math.sinждёт радианы; подача градусов даёт неверный ответ молча. - Перепутать порядок lat/lon. Функция ждёт (широта, долгота); из GeoJSON координаты придут наоборот.
Почему гаверсинус — рабочий стандарт
Расчёт расстояний — настолько базовая операция, что от его корректности зависит всё остальное: радиусные запросы, кластеризация точек, оценка стоимости доставки, ближайший сосед. И здесь гаверсинус занимает золотую середину между двумя крайностями. С одной стороны — наивная евклидова формула на градусах, которая просто неверна: она игнорирует кривизну Земли и сжатие меридианов к полюсам, давая ошибку в десятки процентов. С другой — формула Винсенти на эллипсоиде, точная до миллиметров, но громоздкая и иногда не сходящаяся для антиподальных точек. Гаверсинус даёт точность лучше процента (источник ошибки — лишь то, что Земля не идеальный шар) при простоте в несколько строк, и для подавляющего большинства задач — от логистики до спортивных трекеров — этого с запасом достаточно.
Стоит понимать и пределы. Гаверсинус считает кратчайший путь по поверхности шара (ортодромию) — это честное «расстояние по воздуху», но не «по дороге»: реальный маршрут всегда длиннее на коэффициент извилистости сети. Для оценки времени доставки гаверсинус берут как нижнюю границу или умножают на эмпирический коэффициент (обычно 1,2–1,4 для городов). Кроме того, формула возвращает только длину, но не направление; если нужен ещё и азимут (куда идти), считают отдельную формулу начального пеленга. Зная эти границы, вы применяете гаверсинус там, где он силён, и не удивляетесь расхождению с одометром.
Итог
- Кратчайший путь на сфере — дуга большого круга, а не прямая.
- Гаверсинус: $a = \sin^2(\frac{\Delta\phi}{2}) + \cos\phi_1\cos\phi_2\sin^2(\frac{\Delta\lambda}{2})$, $d = 2R\arcsin\sqrt{a}$.
- Формула устойчива для близких точек, в отличие от арккосинуса.
- Координаты переводят в радианы; порядок (широта, долгота) важен.