Управление камерой и viewport

Архитектура отображения карты в Kepler.gl основана на концепции контролируемого состояния viewport, которое определяет текущее положение камеры, масштаб, наклон и поворот сцены. Вся геопространственная визуализация, включая слои, анимации и взаимодействия, опирается на синхронизацию состояния карты с Redux-хранилищем и механизмами deck.gl.


Модель viewport: структура и ключевые параметры

Viewport представляет собой объект состояния, описывающий камеру в трёхмерном пространстве карты.

Основные поля:

  • latitude — широта центра карты
  • longitude — долгота центра карты
  • zoom — уровень приближения
  • bearing — поворот карты относительно севера (в градусах)
  • pitch — наклон камеры (3D перспектива)
  • altitude — виртуальная высота камеры
  • width / height — размеры контейнера карты

Каждое изменение этих параметров приводит к перерасчёту матрицы проекции и перерисовке всех слоёв через WebGL.


Состояние карты в Kepler.gl

Viewport является частью более широкого состояния карты, которое включает:

  • mapState — управление камерой и viewport
  • visState — данные слоёв, фильтры, интерактивные настройки
  • uiState — интерфейсные параметры (панели, модальные окна, режимы отображения)

Viewport хранится внутри mapState и обновляется через действия Redux.

Пример структуры:

{
  mapState: {
    latitude: 55.751244,
    longitude: 37.618423,
    zoom: 10,
    bearing: 0,
    pitch: 0,
    dragRotate: true
  }
}

Контролируемый и неконтролируемый viewport

Kepler.gl может работать в двух режимах управления камерой:

Контролируемый режим

В этом режиме внешний код полностью управляет состоянием viewport. Любое движение карты вызывает событие обновления состояния, которое должно быть синхронизировано обратно.

Ключевой механизм — обработчик onViewStateChange.

<KeplerGl
  mapboxApiAccessToken={token}
  id="map"
  onViewStateCha nge={({ viewState }) => {
    dispatch(updateMap(viewState));
  }}
/>

Характеристики:

  • Полный контроль состояния извне
  • Возможность синхронизации с URL, сервером или другими компонентами
  • Требует аккуратного управления производительностью

Неконтролируемый режим

В этом режиме Kepler.gl самостоятельно управляет viewport внутри себя через Redux store.

<KeplerGl
  mapboxApiAccessToken={token}
  id="map"
/>

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

  • Упрощённая интеграция
  • Меньше кода управления состоянием
  • Ограниченная внешняя синхронизация

Обновление viewport через действия

Kepler.gl использует набор Redux-экшенов для изменения камеры.

updateMap

Базовое действие для изменения viewport:

dispatch(
  updateMap({
    latitude: 40.7128,
    longitude: -74.0060,
    zoom: 12
  })
);

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

  • Частичное обновление состояния
  • Не требует полного объекта viewport
  • Подходит для динамических переходов

fitBounds

Механизм автоматического масштабирования карты под заданный bounding box.

dispatch(
  fitBounds({
    bounds: [
      [-74.2, 40.7],
      [-73.9, 40.8]
    ],
    padding: 40
  })
);

Поведение:

  • Рассчитывает оптимальный zoom
  • Центрирует карту
  • Учитывает отступы

Взаимодействие viewport с deck.gl

Kepler.gl построен поверх deck.gl, который использует ViewState как основной источник данных для камеры.

Viewport в Kepler.gl транслируется в параметры:

  • initialViewState
  • viewState
  • onViewStateChange

deck.gl ожидает обновления состояния при каждом изменении пользовательского взаимодействия (панорамирование, zoom, rotation).


Плавные переходы камеры

Для создания анимации перемещения камеры используется интерполяция состояния viewport.

Основные механизмы:

  • изменение zoom с easing
  • плавный переход между координатами
  • анимация поворота (bearing)
  • анимация наклона (pitch)

Пример логики перехода:

dispatch(
  updateMap({
    latitude: 48.8566,
    longitude: 2.3522,
    zoom: 11,
    transitionDuration: 1000
  })
);

Параметр transitionDuration определяет длительность анимации.


Управление камерой через события

Viewport обновляется в ответ на пользовательские действия:

  • drag (перемещение карты)
  • scroll (изменение zoom)
  • pinch (мультитач масштабирование)
  • rotation gesture (поворот)

Каждое действие генерирует новый viewState, который проходит через обработчик:

onViewStateChange({ viewState }) => {
  dispatch(updateMap(viewState));
}

Таким образом формируется непрерывный цикл:

ввод пользователя → новый viewport → Redux → рендер → отображение


Ограничения и фиксация viewport

Для контроля области отображения применяются ограничения:

  • minZoom / maxZoom
  • maxBounds
  • ограничение pitch
  • блокировка вращения

Пример конфигурации:

{
  mapState: {
    minZoom: 2,
    maxZoom: 15,
    dragRotate: false
  }
}

Эти параметры предотвращают выход камеры за допустимые пределы.


Синхронизация viewport с внешним состоянием

Viewport часто синхронизируется с внешними источниками:

  • URL-параметрами
  • состоянием приложения (Redux вне Kepler.gl)
  • серверными сохранениями
  • пользовательскими сценариями

Пример синхронизации с URL:

const viewState = {
  latitude,
  longitude,
  zoom
};

history.replace(`#map=${JSON.stringify(viewState)}`);

При восстановлении состояния карта возвращается к сохранённой позиции.


Несколько карт и независимые viewport

Kepler.gl поддерживает несколько экземпляров карт в одном приложении. Каждый экземпляр имеет собственный id, который изолирует состояние:

<KeplerGl id="mapA" />
<KeplerGl id="mapB" />

Каждый viewport хранится отдельно:

state = {
  keplerGl: {
    mapA: { mapState: {...} },
    mapB: { mapState: {...} }
  }
}

Это позволяет:

  • сравнивать разные регионы
  • синхронизировать или разделять камеры
  • строить мультикарточные интерфейсы

Производительность обновлений viewport

Частые изменения камеры могут вызывать нагрузку на GPU и Redux.

Оптимизационные подходы:

  • throttling событий onViewStateChange
  • избегание глубоких копий состояния
  • использование мемоизации селекторов
  • отключение лишних анимаций при drag

Особенно критично при:

  • большом количестве слоёв
  • 3D визуализациях
  • потоковых данных

Камера в 3D-режиме

При включённом pitch и bearing viewport становится полноценной 3D-камерой.

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

  • изменение pitch влияет на перспективу
  • rotation изменяет ориентацию сцены
  • altitude управляет виртуальной дистанцией

3D-режим усиливает зависимость от GPU и требует более аккуратной работы с частотой обновлений.


Программное управление перемещением камеры

Камера может управляться без пользовательского ввода:

dispatch(
  updateMap({
    latitude: 35.6895,
    longitude: 139.6917,
    zoom: 12,
    bearing: 45,
    pitch: 30
  })
);

Это используется для:

  • сценарных презентаций данных
  • автоматической навигации по точкам
  • пошаговой визуализации

Связь viewport и слоёв визуализации

Viewport влияет на:

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

При увеличении zoom происходит:

  • разбиение кластеров
  • загрузка более детализированных данных
  • изменение уровня агрегации

Взаимодействие viewport и UI-элементов

UI Kepler.gl может изменять viewport через:

  • кнопки zoom in/out
  • reset view
  • переключение пресетов карты

Каждое UI-действие вызывает Redux-экшен, который модифицирует mapState и инициирует перерисовку сцены.


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

Viewport в Kepler.gl формирует связующее звено между:

  • пользовательским вводом
  • состоянием приложения
  • WebGL-рендерингом
  • геоданными и слоями

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