Восстановление состояния карты

Kepler.gl основана на концепции единого состояния (single source of truth), где вся конфигурация карты хранится в структурированном состоянии Redux. Восстановление карты сводится к корректной сериализации и последующей десериализации трёх ключевых частей: visState, mapState, mapStyle. Эти сегменты полностью определяют внешний вид, данные и поведение визуализации.


Структура состояния Kepler.gl

Состояние Kepler.gl обычно состоит из следующих блоков:

  • visState — описание данных, слоёв, фильтров и взаимодействий
  • mapState — положение карты, масштаб, центр, ориентация
  • mapStyle — стиль карты, темы, базовые слои и кастомизация

Дополнительно могут присутствовать:

  • uiState — параметры интерфейса
  • datasets — загруженные источники данных
  • interactionConfig — настройки интерактивности слоёв

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


Базовый принцип восстановления

Восстановление карты строится на обратной операции к сохранению:

  1. Получение сериализованного состояния
  2. Валидация структуры
  3. Передача состояния в store Kepler.gl
  4. Рендеринг интерфейса на основе восстановленных данных

Ключевой момент заключается в том, что Kepler.gl не хранит “проекцию карты”, а пересобирает её из состояния.


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

Состояние обычно сериализуется в JSON:

const savedState = {
  version: 'v1',
  keplerGl: {
    map: {
      visState: {...},
      mapState: {...},
      mapStyle: {...}
    }
  }
};

При сохранении важно учитывать:

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

Восстановление через Redux store

Kepler.gl интегрируется с Redux через редьюсер keplerGlReducer. Восстановление состояния выполняется через инициализацию store:

import { createStore, combineReducers } from 'redux';
import keplerGlReducer from 'kepler.gl/reducers';

const store = createStore(
  combineReducers({
    keplerGl: keplerGlReducer
  }),
  persistedState
);

Где persistedState — ранее сохранённый JSON.

Ключевым моментом является совпадение структуры состояния с ожидаемой схемой reducer’а.


Восстановление через actions Kepler.gl

Помимо прямой инициализации store, используется диспатчинг действий.

Загрузка данных

import { addDataToMap } from 'kepler.gl/actions';

store.dispatch(
  addDataToMap({
    datasets: savedDatasets,
    options: {
      centerMap: true,
      readOnly: false
    },
    config: savedConfig
  })
);

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

  • слои визуализации
  • фильтры
  • конфигурацию карты

Восстановление mapState

mapState отвечает за положение камеры:

mapState: {
  latitude: 55.75,
  longitude: 37.61,
  zoom: 10,
  bearing: 0,
  pitch: 0
}

При восстановлении важно, чтобы эти параметры применялись после инициализации карты, иначе возможен конфликт с дефолтным состоянием.


Восстановление visState

visState является наиболее сложной частью восстановления.

Она включает:

  • layers (слои визуализации)
  • filters (фильтры данных)
  • interactionConfig
  • animationConfig

Пример структуры слоя:

layers: [
  {
    id: 'point-layer',
    type: 'point',
    config: {
      dataId: 'dataset_1',
      columns: {
        lat: 'lat',
        lng: 'lng'
      }
    }
  }
]

При восстановлении важно соблюдать:

  • идентичность dataId
  • корректность ссылок на поля данных
  • совместимость типов слоёв с текущей версией Kepler.gl

Восстановление mapStyle

mapStyle отвечает за визуальную основу карты:

mapStyle: {
  styleType: 'dark',
  visibleLayerGroups: {
    label: true,
    road: true,
    border: false
  }
}

При восстановлении могут возникать проблемы при:

  • изменении доступных стилевых тем
  • несовместимости кастомных tile-источников
  • устаревших конфигурациях Mapbox styles

Версионирование состояния

Kepler.gl не гарантирует обратную совместимость между версиями состояния. Поэтому вводится слой версионирования:

{
  version: 'v2',
  keplerGl: {...}
}

При восстановлении выполняется проверка версии:

  • совпадающая версия → прямое восстановление
  • устаревшая версия → миграция
  • неизвестная версия → fallback к базовому состоянию

Миграция обычно реализуется вручную через функции трансформации JSON.


Восстановление из URL

Одним из ключевых сценариев является восстановление через URL-параметры.

Состояние кодируется в Base64:

const encoded = encodeURIComponent(
  btoa(JSON.stringify(state))
);

При загрузке:

const state = JSON.parse(
  atob(decodeURIComponent(encoded))
);

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

  • ограничение длины URL
  • необходимость сжатия (LZ-string часто используется)
  • безопасность передачи данных

Восстановление через localStorage

Простейший механизм хранения:

localStorage.setItem('kepler-state', JSON.stringify(state));

Загрузка:

const state = JSON.parse(
  localStorage.getItem('kepler-state')
);

Особенности подхода:

  • отсутствие серверной синхронизации
  • ограничение объёма хранилища
  • риск устаревания структуры

Гидратация данных (rehydration)

Процесс восстановления часто называют гидратацией состояния.

Он включает этапы:

  1. загрузка сериализованных данных
  2. проверка целостности
  3. нормализация датасетов
  4. применение конфигурации
  5. синхронизация с UI

Особое внимание уделяется датасетам, так как они могут содержать большие объёмы данных и требуют асинхронной обработки.


Типичные проблемы восстановления

Несовпадение dataId

Слои теряют связь с данными при изменении идентификаторов.

Несоответствие схемы данных

Изменение структуры таблиц приводит к ошибкам отображения слоёв.

Версионные конфликты

Старые состояния могут не поддерживать новые типы слоёв или фильтров.

Потеря кастомных стилей

Mapbox styles могут стать недоступными при изменении ключей доступа.


Синхронизация состояния между сессиями

Для устойчивого восстановления используется комбинация:

  • Redux store persistence
  • URL encoding
  • серверное хранение
  • локальное кэширование

Приоритет обычно задаётся следующим образом:

  1. серверное состояние
  2. URL состояние
  3. localStorage
  4. дефолтная конфигурация

Асинхронное восстановление

При больших наборах данных восстановление требует асинхронного подхода:

store.dispatch(
  addDataToMap({
    datasets: await loadDatasets(),
    options: {
      centerMap: false
    }
  })
);

Асинхронность позволяет:

  • избежать блокировки UI
  • постепенно загружать слои
  • управлять памятью при больших объёмах данных

Контроль целостности состояния

Перед применением состояния выполняются проверки:

  • наличие обязательных полей visState, mapState, mapStyle
  • валидность JSON
  • соответствие типов слоёв
  • проверка ссылок на данные

При обнаружении ошибок применяется частичное восстановление, где корректные части состояния применяются, а повреждённые заменяются дефолтными значениями.


Обновление состояния после восстановления

После загрузки состояния часто требуется синхронизация с текущей версией приложения:

  • пересчёт фильтров
  • обновление bounding box
  • перерасчёт агрегированных данных
  • пересборка визуальных слоёв

Этот этап обеспечивает корректное отображение даже при изменении логики рендера между версиями Kepler.gl