Динамическое управление фильтрами

Фильтры в Kepler.gl являются частью состояния визуализации и управляются через слой visState, который хранит всю логику взаимодействия с данными: от набора датасетов до конфигурации слоёв и фильтрации. Динамическое управление фильтрами предполагает изменение параметров фильтрации в реальном времени через действия (actions), внешние события интерфейса или программную логику приложения без пересборки карты.

Система фильтров интегрирована в Redux-подобное состояние Kepler.gl. Основной контейнер — visState, внутри которого хранится массив filters. Каждый элемент этого массива представляет собой объект фильтра с набором параметров:

  • тип фильтра (timeRange, range, select и др.)
  • идентификаторы полей (dataId, name)
  • текущие значения диапазона или выборки
  • конфигурация UI (step, speed, enlarged, plotType)

Фильтр всегда связан с конкретным датасетом через dataId. Это позволяет одновременно применять разные фильтры к разным источникам данных.

Базовые принципы динамического обновления

Динамическое управление фильтрами в Kepler.gl опирается на поток обновлений состояния. Любое изменение фильтра реализуется через dispatch action:

  • добавление фильтра
  • обновление параметров фильтра
  • удаление фильтра
  • полная синхронизация состояния фильтров

Ключевой момент заключается в том, что фильтр не изменяется напрямую как объект JavaScript. Вместо этого создаётся новое состояние, которое проходит через reducer visState.

Создание фильтра через действие

Добавление фильтра происходит через действие, формирующее объект фильтра и передающее его в состояние:

dispatch(addFilter({
  dataId: 'dataset_1',
  name: 'timestamp',
  type: 'timeRange',
  value: [startTimestamp, endTimestamp]
}));

После обработки reducer автоматически интегрирует новый фильтр в массив filters. Этот подход обеспечивает предсказуемость состояния и совместимость с архитектурой Redux.

Обновление фильтра в реальном времени

Основной механизм динамического управления связан с действием обновления фильтра. Оно используется при изменении значений слайдеров, внешних API или событий пользовательского интерфейса.

dispatch(updateFilter({
  id: 'filter_1',
  value: [newMin, newMax]
}));

При каждом вызове происходит перерасчёт данных слоёв, зависящих от фильтра. Kepler.gl оптимизирует вычисления, применяя фильтрацию на уровне GPU или WebGL-слоя, если это возможно.

Программная синхронизация фильтров

В сложных приложениях фильтры часто синхронизируются с внешним состоянием: URL-параметрами, состоянием React или серверными событиями. Это позволяет создавать реактивные карты, изменяющиеся без перезагрузки.

Типичный подход заключается в подписке на изменения состояния:

store.subscribe(() => {
  const state = store.getState();
  const filters = state.keplerGl.map.visState.filters;
});

На основе этих данных можно формировать внешние контроллеры или сохранять конфигурацию.

Управление временными фильтрами

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

Структура такого фильтра включает:

  • timeInterval
  • domain
  • value
  • speed
  • animationWindow

Изменение временного диапазона в реальном времени приводит к перерасчёту отображаемых объектов:

dispatch(updateFilter({
  id: 'time_filter',
  value: [t0, t1],
  speed: 1
}));

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

Связь фильтров с слоями

Каждый слой (layer) в Kepler.gl может ссылаться на один или несколько фильтров. Связь осуществляется через filterId или косвенно через dataId. Это означает, что изменение одного фильтра может влиять на несколько визуальных слоёв одновременно.

Пример поведения:

  • фильтр времени ограничивает точки на ScatterplotLayer
  • числовой диапазон влияет на HexagonLayer
  • категориальный фильтр изменяет GeoJsonLayer

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

Сложные сценарии обновления состояния

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

Это реализуется через промежуточный слой логики:

function syncFilters(newValue) {
  dispatch(updateFilter({
    id: 'filter_a',
    value: newValue
  }));

  dispatch(updateFilter({
    id: 'filter_b',
    value: computeDependentRange(newValue)
  }));
}

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

Интеграция с React-компонентами

Kepler.gl часто используется внутри React-приложений, где фильтры управляются через props и callbacks. Компоненты могут выступать как контроллеры состояния:

  • слайдеры диапазона
  • чекбоксы категорий
  • таймлайны

Каждое изменение UI-компонента вызывает dispatch действия, обновляющего visState.

const onCha nge = (value) => {
  dispatch(updateFilter({ id: 'filter_1', value }));
};

Это обеспечивает односторонний поток данных и предотвращает рассинхронизацию интерфейса и карты.

Производительность при динамическом обновлении

При частых обновлениях фильтров возникает нагрузка на перерасчёт слоёв. Kepler.gl решает эту проблему через:

  • мемоизацию промежуточных результатов
  • использование WebGL для фильтрации
  • батчинг Redux-обновлений
  • отложенные вычисления (debounce/throttle)

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

Управление множественными фильтрами

Система поддерживает несколько фильтров одновременно. Их взаимодействие строится по принципу логического пересечения:

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

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

filters: [
  { id: 'f1', type: 'range', value: [0, 100] },
  { id: 'f2', type: 'select', value: ['A', 'B'] },
  { id: 'f3', type: 'timeRange', value: [t0, t1] }
]

Внешнее управление через API состояния

В некоторых архитектурах Kepler.gl используется как «визуальный движок», управляемый извне. В этом случае фильтры полностью контролируются приложением через глобальный store.

Изменения могут приходить:

  • от WebSocket событий
  • от REST API
  • от пользовательских скриптов
  • от аналитических движков

Каждое событие преобразуется в updateFilter action и синхронизирует карту с внешним источником данных.

Особенности сериализации фильтров

Состояние фильтров может быть сохранено и восстановлено. Это важно для сценариев, где требуется воспроизводимость визуализации.

Сериализуемые поля включают:

  • type
  • value
  • dataId
  • name

При восстановлении состояния Kepler.gl полностью реконструирует фильтры и их влияние на слои без дополнительной логики со стороны приложения.