Преобразование единиц измерения

В Mapbox GL JS вся работа с геометрией и визуализацией опирается на несколько систем единиц измерения, которые постоянно преобразуются друг в друга: географические координаты, мировые координаты Web Mercator, экранные пиксели и условные единицы стиля. Корректное понимание этих преобразований определяет точность позиционирования объектов, адекватность масштабирования и стабильность кастомных визуализаций.

Географические координаты задаются в формате долгота/широта (lng/lat) и измеряются в градусах. Эти значения не являются линейными: один градус долготы в разных широтах соответствует разному физическому расстоянию, что делает невозможным прямое использование градусов в расчётах расстояний или размеров.


Web Mercator как промежуточная система измерений

Основная проекционная система Mapbox GL JS — Web Mercator (EPSG:3857). Она переводит сферические координаты в псевдоплоские мировые координаты, где единицы измерения становятся линейными в пределах локальных областей.

В этой системе:

  • ось X соответствует долготе
  • ось Y соответствует широте после нелинейного преобразования
  • единицы измерения приближены к метрам, но не являются ими строго

Проекция вводит ключевое свойство: масштабирование становится равномерным по осям, что позволяет применять стандартные операции линейной алгебры для рендеринга.


Масштаб карты и изменение единиц измерения

Система масштабирования в Mapbox GL JS основана на уровне zoom:

  • при увеличении zoom в 1 шаг масштаб увеличивается в 2 раза
  • размер пикселя на земле уменьшается экспоненциально

Связь между мировыми координатами и экранными единицами определяется формулой:

[ resolution = ]

где initial_resolution зависит от широты и начальной проекции.

На практике это означает, что одна и та же географическая длина в метрах будет занимать разное количество пикселей в зависимости от уровня увеличения.


Пиксельные единицы и устройство отображения

Экранные координаты в Mapbox GL JS выражаются в CSS-пикселях. Однако физическая плотность пикселей устройства влияет на итоговую визуализацию через devicePixelRatio.

Фактически:

  • CSS-пиксель — логическая единица
  • физический пиксель — аппаратная единица
  • Mapbox GL JS масштабирует рендеринг с учётом DPR для сохранения чёткости

Это приводит к необходимости учитывать двойное преобразование:

  1. географические координаты → мировые координаты
  2. мировые координаты → пиксели (с учётом DPR)

Преобразование lng/lat в экранные координаты

Mapbox GL JS предоставляет методы для конвертации координат в пиксели и обратно.

Проекция точки на экран

const point = map.project([lng, lat]);

Результат:

  • x — координата по горизонтали в пикселях
  • y — координата по вертикали в пикселях

Эти значения зависят от текущего центра карты, zoom и наклона.

Обратное преобразование

const lngLat = map.unproject([x, y]);

Операция инвертирует всю матрицу трансформации камеры карты, включая перспективные эффекты.


Геометрические преобразования через MercatorCoordinate

Для задач 3D и кастомных слоёв используется объект MercatorCoordinate, представляющий точку в нормализованном мировом пространстве.

const mc = mapboxgl.MercatorCoordinate.fromLngLat([lng, lat], altitude);

Особенности:

  • координаты x и y нормированы в диапазон [0, 1]
  • z соответствует высоте над поверхностью
  • позволяет работать в едином линейном пространстве WebGL

Это ключевой механизм согласования 2D карты и 3D сцены.


Перевод метров в пиксели

Физическое расстояние в метрах не имеет прямого отображения в пикселях, поэтому используется приближённое преобразование, зависящее от широты и масштаба:

[ metersPerPixel = ]

где C — константа окружности Земли в метрах, нормированная к тайловой системе.

В практической реализации чаще используется:

const metersPerPixel = (40075016.686 * Math.cos(lat * Math.PI / 180)) /
    Math.pow(2, zoom + 8);

Отсюда:

const pixels = meters / metersPerPixel;

Учет широты при масштабировании

Искажения Web Mercator усиливаются ближе к полюсам. Один и тот же объект:

  • на экваторе сохраняет линейные пропорции
  • на высоких широтах визуально растягивается

Поэтому любые расчёты расстояний должны учитывать cos(latitude), иначе возникает систематическая ошибка масштабирования.


Интерполяция и единицы в стиле

Mapbox Style Specification оперирует абстрактными единицами, которые динамически интерпретируются:

  • zoom-dependent значения
  • property-based values
  • expression-based вычисления

Пример масштабирования размера:

"circle-radius": [
  "interpolate",
  ["linear"],
  ["zoom"],
  5, 2,
  10, 8,
  15, 20
]

Здесь единицы радиуса интерпретируются как пиксели, но изменяются в зависимости от zoom, что фактически создаёт нелинейную шкалу преобразования единиц.


Линейные и экспоненциальные преобразования

В стиле используются два основных типа масштабирования:

Линейное

["interpolate", ["linear"], ...]

Изменение пропорционально входному значению.

Экспоненциальное

["interpolate", ["exponential", 1.5], ...]

Позволяет моделировать нелинейные физические процессы, например плотность объектов или визуальную иерархию.


Система трансформации камеры

Внутренний объект трансформации карты содержит:

  • center (lng/lat)
  • zoom
  • bearing
  • pitch

Эти параметры формируют матрицу преобразования:

[ M = P V T]

где:

  • T — перевод из географических координат
  • V — вращение и наклон камеры
  • P — проекция в экранное пространство

Все единицы измерений проходят через эту матрицу перед рендерингом.


Работа с расстояниями и геодезией

Mapbox GL JS не использует геодезические расчёты по умолчанию для рендеринга, поэтому расстояния:

  • в пикселях — точные в экранной системе
  • в метрах — приближённые через проекцию
  • в градусах — нелинейные

Для точных измерений применяется внешний расчёт или перевод через сферические формулы, но итоговая визуализация всегда возвращается в пиксельное пространство.


Высота и 3D-единицы

При использовании 3D объектов (extrusions, terrain):

  • высота задаётся в метрах
  • затем переводится в Mercator Z координату
  • визуально масштабируется вместе с zoom
const mc = mapboxgl.MercatorCoordinate.fromLngLat(
  [lng, lat],
  heightInMeters
);

Здесь высота становится частью единой системы координат сцены WebGL.


Преобразование тайловых единиц

Mapbox GL JS использует тайловую систему:

  • каждый zoom соответствует сетке тайлов 2^z × 2^z
  • координаты внутри тайла нормализованы [0..1]

Это создаёт дополнительный уровень преобразования:

lng/lat → world → tile → pixel

Каждый слой карты может оперировать на своём уровне абстракции, но финальный рендер всегда приводит данные к пиксельной сетке.


Практика согласования разных единиц

При комбинировании геометрии и интерфейса возникает необходимость постоянного преобразования:

  • маркеры используют пиксели
  • источники данных используют lng/lat
  • WebGL использует MercatorCoordinate
  • стили используют абстрактные пиксельные значения

Согласование этих систем требует постоянной нормализации через project/unproject и MercatorCoordinate API, иначе возникают расхождения между логической и визуальной позицией объектов.