Обновление данных в реальном времени

Архитектура библиотеки изначально ориентирована на потоковую работу с геоданными, где наборы точек, траекторий и агрегированных слоёв могут изменяться без полного пересоздания визуализации. В основе лежит связка React + Redux, где вся сцена описывается через единое состояние приложения (keplerGl state), а любые обновления проходят через экшены, которые модифицируют слои, датасеты и параметры визуализации.


Архитектурная модель обновлений

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

Ключевые сущности:

  • datasets — наборы геоданных (GeoJSON, CSV, JSON)
  • visState — визуальное представление (слои, фильтры, взаимодействия)
  • mapState — камера, зум, позиция карты
  • uiState — интерфейсные параметры

Любое обновление данных проходит через Redux action:

  • addDataToMap — добавление или замена датасета
  • setFilter — обновление фильтров
  • updateMap — изменение состояния карты
  • layerConfigChange — изменение визуализации слоёв

Базовый механизм замены данных

Самый прямолинейный способ обновления — полная замена dataset через addDataToMap.

dispatch({
  type: 'ADD_DATA_TO_MAP',
  payload: {
    datasets: {
      info: {
        label: 'live-data',
        id: 'live_data'
      },
      data: newGeoJson
    },
    options: {
      centerMap: false,
      readOnly: false
    },
    config: {}
  }
});

В этом сценарии Kepler перерисовывает слои, пересчитывает агрегаты и обновляет фильтры.

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


Инкрементальное обновление данных

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

Подход:

  1. Хранение текущего dataset
  2. Получение новых точек
  3. Объединение или дифф
  4. Обновление только изменённого слоя
const upd atedData = {
  ...existingData,
  features: [
    ...existingData.features,
    ...newData.features
  ]
};

Далее обновление:

dispatch(addDataToMap({
  datasets: {
    info: {
      id: 'live_data',
      label: 'live-data'
    },
    data: upd atedData
  }
}));

Важно: такой подход подходит только для небольших потоков или батчей.


Потоковая архитектура через WebSocket

Реальные сценарии обновления данных в реальном времени чаще используют WebSocket.

Типичная схема:

  • сервер отправляет координаты объектов
  • клиент агрегирует их
  • Kepler обновляет слой
const socket = new WebSocket('wss://stream.example.com');

socket.onmess age = (event) => {
  const incoming = JSON.parse(event.data);

  store.dispatch(updateLiveDataset(incoming));
};

Далее Redux middleware преобразует поток в обновление Kepler:

function liveDataMiddleware(store) {
  return next => action => {
    if (action.type === 'LIVE_DATA_RECEIVED') {
      const state = store.getState();
      const current = state.keplerGl.map1.visState.datasets.live;

      const merged = mergeData(current, action.payload);

      store.dispatch(addDataToMap({
        datasets: {
          info: { id: 'live', label: 'live stream' },
          data: merged
        }
      }));
    }

    return next(action);
  };
}

Оптимизация частоты обновлений

Потоковые данные могут приходить с высокой частотой, что создаёт нагрузку на рендеринг.

Используются техники:

Debounce

Сглаживание частоты обновлений:

const debouncedUpdate = debounce((data) => {
  dispatch(addDataToMap(data));
}, 200);

Batch processing

Накопление событий:

let buffer = [];

socket.onmess age = (event) => {
  buffer.push(JSON.parse(event.data));

  if (buffer.length > 50) {
    dispatch(updateBatch(buffer));
    buffer = [];
  }
};

Throttle

Ограничение FPS обновлений:

const throttled = throttle((data) => {
  dispatch(addDataToMap(data));
}, 1000);

Обновление слоя без пересоздания dataset

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

Используется layerConfigChange:

dispatch({
  type: 'LAYER_CONFIG_CHANGE',
  payload: {
    layerId: 'points_layer',
    config: {
      colorField: 'speed',
      colorScale: 'quantile'
    }
  }
});

Это важно для real-time аналитики, где структура данных стабильна, но смысл визуализации меняется динамически.


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

Фильтры часто используются как реакция на поток данных.

dispatch({
  type: 'SET_FILTER',
  payload: {
    id: 'time_filter',
    value: [startTime, endTime]
  }
});

При потоковой обработке фильтр может автоматически сдвигаться:

function shiftTimeFilter(newTimestamp) {
  dispatch(setFilter({
    id: 'time_filter',
    value: [newTimestamp - 3600, newTimestamp]
  }));
}

Обновление данных через Trips и анимацию

Для треков объектов используется слой Trips.

При обновлении маршрутов важно сохранять временную структуру:

const tripsData = {
  type: 'FeatureCollection',
  features: updatedTrips
};

Обновление:

dispatch(addDataToMap({
  datasets: {
    info: { id: 'trips', label: 'vehicle movement' },
    data: tripsData
  }
}));

Анимация синхронизируется с временным фильтром, поэтому поток должен содержать корректные timestamp.


Частичная замена объектов (diff update)

В высоконагруженных системах используется дифф:

function applyDiff(oldData, diff) {
  const map = new Map();

  oldData.features.forEach(f => map.se t(f.id, f));

  diff.upserts.forEach(f => map.se t(f.id, f));
  diff.deletes.forEach(id => map.delete(id));

  return {
    ...oldData,
    features: Array.from(map.values())
  };
}

Такой подход снижает нагрузку на перерасчёт слоёв.


Синхронизация состояния карты

При обновлении данных важно не терять состояние камеры.

Kepler.gl разделяет:

  • данные (datasets)
  • визуализацию (layers)
  • положение карты (viewport)

Поэтому обновление данных не должно сбрасывать mapState:

dispatch(addDataToMap({
  datasets: newData,
  options: {
    centerMap: false
  }
}));

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

Основные узкие места:

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

Методы оптимизации:

  • уменьшение частоты dispatch
  • использование упрощённых геометрий
  • предагрегация на сервере
  • передача delta-обновлений вместо полного dataset

Серверная агрегация как часть real-time pipeline

Часто Kepler.gl используется не для сырого потока, а для уже агрегированных данных.

Сервер выполняет:

  • spatial aggregation
  • clustering
  • time bucketing
  • simplification (Douglas–Peucker)

Клиент получает уже подготовленные чанки:

{
  "type": "aggregated",
  "resolution": "5s",
  "features": [...]
}

Это значительно снижает нагрузку на браузер.


Согласование нескольких потоков данных

При нескольких источниках (например, GPS + события + телеметрия) используется нормализация:

  • единый timestamp
  • единый coordinate system (WGS84)
  • унифицированные feature id

Слияние происходит перед dispatch:

const unified = normalizeStreams([gps, telemetry, events]);
dispatch(addDataToMap(unified));

Обновление больших датасетов

Для массивных потоков (>100k объектов) применяется стратегия:

  • виртуализация данных
  • выборочная отрисовка
  • spatial indexing (R-tree)

Kepler.gl использует WebGL, но CPU-bound операции всё равно критичны.


Управление жизненным циклом потоков

Типичная схема:

  1. подключение WebSocket
  2. инициализация dataset
  3. потоковая агрегация
  4. периодические обновления Kepler state
  5. очистка устаревших данных
useEffect(() => {
  socket.connect();

  return () => {
    socket.disconnect();
  };
}, []);

Консистентность визуализации при потоках

При real-time обновлениях важно избегать:

  • мерцания слоёв
  • скачков камеры
  • пересчёта всей сцены

Для этого используется:

  • сохранение interactionConfig
  • фиксированный viewport
  • стабильные layer IDs

Стабильность идентификаторов является ключевым условием корректного обновления.


Итоговая модель поведения real-time системы

Поток данных → нормализация → батчинг → Redux action → Kepler state → WebGL рендер

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