CameraForBounds

MapLibre GL JS реализует камеру карты как абстракцию над трансформацией мира в экранное пространство. Одним из ключевых механизмов позиционирования является вычисление параметров камеры по географическим границам — прямоугольнику, заданному координатами юго-западного и северо-восточного углов. Внутренняя логика, условно обозначаемая как CameraForBounds, отвечает за преобразование географического bounding box в набор параметров камеры: центр, zoom, bearing и pitch.


Геометрическая модель границ

Границы карты задаются в виде:

  • southWest: [lng, lat]
  • northEast: [lng, lat]

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

В основе вычислений лежит проекция Web Mercator projection, которая переводит сферические координаты в плоскость. Это позволяет корректно работать с расстояниями и масштабированием на разных широтах.


Принцип работы CameraForBounds

CameraForBounds формирует камеру на основе следующих шагов:

  1. Перевод географических координат в мировые координаты проекции
  2. Вычисление минимального ограничивающего прямоугольника в пикселях
  3. Определение центра bounding box
  4. Подбор zoom уровня, при котором весь прямоугольник помещается в viewport
  5. Учет padding, rotation и pitch

Результатом становится объект камеры:

  • center
  • zoom
  • bearing
  • pitch

Преобразование координат

Каждая точка bounds проходит трансформацию:

worldPoint = project(lng, lat)

Где projection основана на Web Mercator:

  • долгота линейно масштабируется
  • широта преобразуется через логарифмическую функцию

Это позволяет учитывать искажения карты при приближении к полюсам.


Вычисление центра

Центр bounds вычисляется как среднее в мировом пространстве:

centerWorld = (minWorld + maxWorld) / 2

После чего выполняется обратное преобразование:

centerLngLat = unproject(centerWorld)

Центр камеры становится опорной точкой всей трансформации.


Подбор zoom для bounds

Ключевая задача CameraForBounds — подобрать такой zoom, при котором прямоугольник полностью помещается в viewport.

Используется принцип:

  • вычисляется размер bounds в пикселях на нулевом zoom
  • сравнивается с размером canvas
  • zoom уменьшается до момента, когда bounds помещается

Формально:

  • widthRatio = viewportWidth / boundsWidth
  • heightRatio = viewportHeight / boundsHeight
  • zoom = baseZoom + log2(min(widthRatio, heightRatio))

Учет padding

Padding позволяет смещать визуальное центрирование внутрь viewport, оставляя поля:

  • padding.left
  • padding.right
  • padding.top
  • padding.bottom

Фактически viewport уменьшается до:

effectiveWidth = width - left - right
effectiveHeight = height - top - bottom

CameraForBounds выполняет расчёты уже на этом уменьшенном пространстве, что влияет на итоговый zoom и center.


Bearing и pitch при fit bounds

При вычислении камеры из bounds значения:

  • bearing (поворот)
  • pitch (наклон)

обычно сохраняются из текущего состояния камеры, если явно не переопределены. Это позволяет сохранить визуальный стиль сцены при переходе между регионами.


Ограничения zoom

После вычисления базового zoom применяется clamp:

zoom = Math.max(minZoom, Math.min(maxZoom, zoom))

Это предотвращает:

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

Интеграция с fitBounds API

CameraForBounds является основой метода:

map.fitBounds([
  [minLng, minLat],
  [maxLng, maxLat]
], {
  padding: 40,
  duration: 1000
});

Алгоритм:

  1. bounds передаются в CameraForBounds
  2. вычисляется cameraOptions
  3. выполняется transition
  4. применяется easing функция

Влияние проекции на вычисления

Web Mercator вносит нелинейность по оси Y. Это означает:

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

CameraForBounds компенсирует это через работу в projected space, а не в lat/lng напрямую.


Учет rotation (bearing)

При ненулевом bearing bounding box перестает быть осесимметричным в экранных координатах.

Алгоритм:

  • bounds преобразуется в 4 угла
  • каждый угол вращается вокруг центра
  • строится новый axis-aligned bounding box в screen space
  • zoom подбирается по этому новому прямоугольнику

Стабилизация вычислений

При частых обновлениях bounds (анимации, потоковые данные) применяются:

  • округление zoom до 1/100 долей
  • защита от микродвижений центра
  • дедупликация одинаковых camera state

Это предотвращает дрожание карты.


Связь с transform pipeline

CameraForBounds является частью цепочки:

  • LngLat → World coordinates
  • World → Screen coordinates
  • Screen → Camera state

Внутренний transform state хранит:

  • scale
  • translation
  • rotation matrix

Поведение при экстремальных bounds

При bounds, пересекающих анти-меридиан:

  • longitude нормализуется в диапазоне [-180, 180]
  • bounds разделяется на два сегмента
  • выбирается минимальный охватывающий вариант

Использование в кластеризации и геоаналитике

CameraForBounds часто применяется в сценариях:

  • отображение всех объектов слоя
  • автофокус на кластеры
  • построение аналитических дашбордов
  • динамическое позиционирование результатов поиска

Взаимодействие с анимацией камеры

При transition:

  • interpolates center linearly
  • zoom interpolates logarithmically
  • bearing interpolates circularly (shortest path)
  • pitch interpolates linearly

CameraForBounds формирует стартовое и конечное состояние, между которыми строится анимация.


Влияние размеров viewport

Любое изменение:

  • resize окна
  • изменение layout контейнера
  • появление sidebar

немедленно изменяет результат CameraForBounds, поскольку перерасчет выполняется в пиксельной системе координат.


Практика вычисления bounds-based камеры

Типовой pipeline:

const bounds = [
  [minLng, minLat],
  [maxLng, maxLat]
];

map.fitBounds(bounds, {
  padding: 80,
  maxZoom: 14
});

Внутри это эквивалентно:

  • CameraForBounds(bounds)
  • transform evaluation
  • state transition

Итоговая модель поведения

CameraForBounds можно рассматривать как функцию:

f(bounds, viewport, padding, rotation, constraints)
→ camera(center, zoom, bearing, pitch)

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