Проекция Меркатора: формула и код

Урок реализует проекцию Меркатора на чистом Python: из географических координат получаем плоские метры, которые ложатся на квадратную карту.

Прямая проекция Меркатора задаётся формулами $x = R\,\lambda$ и $y = R\,\ln\!\left(\tan\!\left(\frac{\pi}{4} + \frac{\phi}{2}\right)\right)$, где $\phi, \lambda$ — широта и долгота в радианах.

В прошлом уроке мы поняли, почему Меркатор искажает площади. Теперь реализуем саму проекцию. Идея проста: долгота переходит в горизонтальную координату линейно, а широта — нелинейно, через логарифм тангенса, и именно этот логарифм растягивает высокие широты.

Разбор формулы

Горизонтальная координата: $x = R\,\lambda$. Долгота просто масштабируется радиусом — меридианы на карте Меркатора равноотстоящие вертикальные линии.

Вертикальная координата: $y = R\,\ln\!\left(\tan\!\left(\frac{\pi}{4} + \frac{\phi}{2}\right)\right)$. Когда $\phi \to 90°$, аргумент тангенса стремится к $90°$, тангенс — к бесконечности, логарифм — тоже. Поэтому $y$ неограниченно растёт у полюсов, и карту приходится обрезать примерно на $\pm 85°$ — отсюда «квадратная» карта веба.

Реализация

import math

def mercator(lat_deg, lon_deg):
    R = 6378137.0  # радиус WGS84 в метрах
    lat = math.radians(lat_deg)
    lon = math.radians(lon_deg)
    x = R * lon
    y = R * math.log(math.tan(math.pi / 4 + lat / 2))
    return x, y

# Москва
x, y = mercator(55.7558, 37.6173)
print(f"x = {x:.1f} м")
print(f"y = {y:.1f} м")

# Точка на экваторе на той же долготе
x0, y0 = mercator(0.0, 37.6173)
print(f"y на экваторе = {abs(y0):.1f} м")

Вывод:

x = 4187538.7 м
y = 7509955.1 м
y на экваторе = 0.0 м

На экваторе $y = 0$ — это «нулевая линия» карты, как и должно быть: $\ln(\tan 45°) = \ln 1 = 0$. Для Москвы $y$ превышает $x$ — широта 56° уже заметно «вытянута» логарифмом.

Как работает под капотом

Существует красивое тождество: $\ln\!\left(\tan\!\left(\frac{\pi}{4} + \frac{\phi}{2}\right)\right) = \operatorname{arctanh}(\sin\phi)$. Оба варианта дают одно и то же, и в библиотеках встречается любой. Обратная проекция (из метров обратно в широту) использует функцию Гудермана и нужна, когда по клику на карте надо понять, какая это широта-долгота. Web Mercator (EPSG:3857) — это ровно эта формула с фиксированным $R = 6378137$, плюс упрощение «считаем Землю шаром, а не эллипсоидом» ради скорости тайлов.

Частые ошибки

  • Забыть перевести градусы в радианы. math.tan и math.log работают с радианами; подача градусов даёт мусор.
  • Подать широту $\pm 90°$. Логарифм уйдёт в бесконечность или выдаст ошибку — полюса в Меркаторе недостижимы, обрезайте до $\pm 85°$.
  • Перепутать порядок аргументов. Здесь функция принимает (широта, долгота); в GeoJSON порядок обратный.

Зачем уметь проецировать руками

Может показаться, что писать проекцию самому незачем — есть pyproj. Но понимание формулы окупается в практических ситуациях. Когда точки на вашей веб-карте «уезжают» от настоящего положения, причина почти всегда в путанице систем координат, и, зная, что Web Mercator берёт градусы и выдаёт метры в диапазоне примерно $\pm 20$ миллионов, вы сразу отличите «координаты в градусах подали как метры» от «перепутан порядок широты и долготы». Когда нужно вычислить, какой пиксель тайла соответствует точке, вы применяете ровно эту формулу плюс перевод метров в номера тайлов. А когда отлаживаете чужой геокод, способность в уме прикинуть, что Москва в Web Mercator это примерно $(4{,}19$ млн, $7{,}51$ млн$)$ метров, экономит часы.

Полезно осознавать и предел применимости. Меркатор хорош для навигации и тайлов, но катастрофичен для любой задачи про площади: считать по нему «сколько леса вырубили» — значит завысить вырубку в высоких широтах в разы. Поэтому реальный конвейер почти всегда двухсистемный: данные хранят и отдают в градусах WGS84, на экран показывают в Web Mercator, а измеряют — перепроецировав в локальную метровую зону. Меркатор в этой схеме отвечает строго за «показать», и попытка измерять прямо на нём — классическая ошибка новичка, которую теперь вы распознаете.

Итог

  • Меркатор: $x = R\lambda$ линейно, $y = R\ln(\tan(\frac{\pi}{4}+\frac{\phi}{2}))$ нелинейно.
  • На экваторе $y = 0$; у полюсов $y \to \infty$, поэтому карту обрезают на $\pm 85°$.
  • Перед тригонометрией градусы переводят в радианы.
  • Web Mercator (EPSG:3857) — эта формула с шаровым радиусом WGS84.
Проверьте себя
1. Чему равна координата $y$ в проекции Меркатора на экваторе?
AРадиусу Земли
BНулю
CБесконечности
DМинус радиусу
2. Почему карта Меркатора обрезается примерно на ±85°?
AТак удобнее печатать
BУ полюсов y уходит в бесконечность (логарифм тангенса)
CТам нет суши
DИз-за ограничения цвета
3. Что обязательно сделать перед вызовом math.tan и math.log в формуле?
AОкруглить координаты
BПеревести градусы в радианы
CПоменять местами lat и lon
DУмножить на 1000