Архитектура состояния

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

Состояние Kepler.gl делится на несколько ключевых срезов, каждый из которых отвечает за отдельный слой системы визуализации.

visState — центральный слой визуализации

visState является наиболее насыщенной частью состояния. Он управляет данными и тем, как они отображаются.

Внутри visState находятся:

  • datasets — загруженные наборы данных, уже приведённые к внутреннему формату
  • layers — визуальные слои (points, arcs, hexagon, cluster и др.)
  • filters — фильтры, применяемые к данным в реальном времени
  • interactionConfig — настройки взаимодействий (hover, click, brush)
  • animationConfig — параметры временной анимации

Каждый dataset хранится в нормализованной форме. Это важно: Kepler.gl не работает с «сырыми таблицами» напрямую, а преобразует их в структуру, удобную для быстрого рендера через deck.gl.

Нормализация данных

Перед добавлением в состояние данные проходят трансформацию:

  • выделение полей
  • определение типов (число, строка, дата)
  • индексация для ускорения фильтрации
  • построение геометрических представлений (для H3, GeoJSON и т.д.)

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


mapState — состояние камеры и визуального представления

mapState отвечает за то, как именно пользователь видит карту.

В него входят:

  • latitude / longitude — центр карты
  • zoom — масштаб
  • pitch — наклон
  • bearing — поворот
  • dragRotate — разрешение вращения
  • width / height — размеры контейнера

mapState напрямую связан с viewState deck.gl. Любое изменение этого среза приводит к перерасчёту матрицы камеры и перерисовке слоёв.

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


uiState — пользовательский интерфейс

uiState управляет состоянием интерфейсных компонентов, не связанных напрямую с геометрией карты.

Сюда входят:

  • открыта ли боковая панель
  • активная вкладка (layers, filters, interactions)
  • режимы отображения (например, split map)
  • уведомления и всплывающие панели
  • состояние модальных окон

uiState изолирован от visState, что предотвращает смешивание визуальной логики и интерфейсной структуры.


interactionState — временные пользовательские события

Отдельный подслой отвечает за временные взаимодействия:

  • hover над объектами
  • selection (выделение)
  • clickedObjects
  • tooltip state

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


Redux как основа управления состоянием

Kepler.gl использует архитектуру, основанную на Redux reducer-цепочке, где каждый срез состояния управляется своим редьюсером.

keplerGlReducer

Центральная функция:

  • объединяет visState, mapState, uiState
  • обрабатывает actions
  • обеспечивает изоляцию экземпляров карты

Каждый экземпляр Kepler.gl (например, несколько карт на странице) может иметь собственное пространство состояния, что достигается через namespacing reducer’ов.


Иммутабельность и производительность

Состояние строго иммутабельно. Любое изменение приводит к созданию нового объекта:

  • фильтрация создаёт новый visState
  • добавление слоя не мутирует существующий массив layers
  • изменение камеры создаёт новый mapState

Для оптимизации используются:

  • мемоизация селекторов
  • кэширование вычисленных данных
  • частичное обновление слоёв

Поток данных и actions

Все изменения проходят через action-based систему.

Типы действий

  • ADD_DATA_TO_MAP — добавление данных
  • ADD_LAYER — создание слоя
  • UPDATE_LAYER — изменение параметров слоя
  • ADD_FILTER / UPDATE_FILTER — фильтрация данных
  • INTERACTION_CHANGE — изменения взаимодействий
  • MAP_STATE_CHANGE — движение камеры

Каждое действие проходит через middleware слой, где может быть:

  • нормализация данных
  • вычисление derived state
  • синхронизация с внешними источниками

Derived state и вычисляемые структуры

Kepler.gl активно использует концепцию производного состояния.

Примеры:

  • отфильтрованные данные слоя
  • агрегации (hexbin, grid)
  • временные срезы анимации
  • геометрические преобразования

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


Связь состояния с deck.gl

Kepler.gl является обёрткой над deck.gl, поэтому значительная часть состояния трансформируется в props для deck.gl layers.

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

  1. visState обновляется
  2. выполняется селектор getLayersToRender
  3. данные преобразуются в deck.gl layers
  4. viewState берётся из mapState
  5. результат передаётся в DeckGL компонент

Таким образом, Redux состояние выступает промежуточным слоем между UI и WebGL рендерингом.


Сериализация и восстановление состояния

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

Состояние преобразуется в JSON, включающий:

  • datasets
  • layers configuration
  • filters
  • mapState
  • uiState

При восстановлении выполняется обратная процедура:

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

Это позволяет сохранять полноценные проекты карт и переносить их между средами.


Мульти-инстанс архитектура

Kepler.gl поддерживает несколько независимых экземпляров на одной странице.

Это достигается через:

  • namespace reducer’ов
  • уникальные идентификаторы инстансов
  • изолированные селекторы состояния

Каждый инстанс имеет собственные:

  • visState
  • mapState
  • uiState

При этом глобальный store может содержать сразу несколько карт, не конфликтующих между собой.


Middleware слой и расширяемость

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

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

Расширяемость достигается через возможность внедрения собственных middleware между dispatch и reducer.


Связь состояния с обработкой больших данных

Архитектура состояния учитывает работу с большими объёмами данных:

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

Это позволяет системе сохранять интерактивность даже при миллионах точек.


Роль селекторов

Селекторы являются критическим элементом архитектуры:

  • извлекают данные из visState
  • формируют конфигурации слоёв
  • вычисляют итоговое состояние рендера

Они изолируют React-компоненты от внутренней структуры Redux, обеспечивая стабильный API для UI слоя.


Согласование состояния между слоями

Особенность Kepler.gl заключается в тесной синхронизации между:

  • visState (данные и визуализация)
  • mapState (камера)
  • interactionState (взаимодействия)

Например, изменение фильтра в visState может привести к:

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

При этом mapState остаётся неизменным, что сохраняет стабильность пользовательского восприятия.


Управление сложностью состояния

Для борьбы со сложностью применяется несколько принципов:

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

Эта дисциплина позволяет удерживать масштабируемость даже при росте количества слоёв, фильтров и датасетов.