Доступ к deck.gl слоям

Внутри Kepler.gl визуализация строится поверх движка deck.gl. Сам Kepler.gl не рендерит слои напрямую — он формирует декларативное описание слоёв, которое затем преобразуется в экземпляры deck.gl Layer.

Ключевой принцип: все слои Kepler.gl существуют как часть состояния (Redux store), но фактически исполняются deck.gl. Это создаёт возможность перехвата, модификации и расширения визуализации на уровне низкоуровневых API.


Где живут deck.gl слои внутри Kepler.gl

Kepler.gl хранит конфигурацию визуализации в нескольких ключевых сегментах состояния:

  • visState.layers — декларации слоёв
  • visState.layerData — данные, привязанные к слоям
  • mapState — параметры камеры и координатной системы
  • interactionConfig — настройки интерактивности

Каждый слой в visState.layers — это не deck.gl объект, а описательный JSON, который позже трансформируется в экземпляр слоя.

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

{
  id: "layer_1",
  type: "point",
  config: {
    dataId: "trip_data",
    columns: {
      lat: "pickup_lat",
      lng: "pickup_lng"
    },
    color: [255, 0, 0],
    isVisible: true
  },
  visualChannels: {
    colorField: null,
    sizeField: null
  }
}

На этапе рендеринга Kepler.gl преобразует это в соответствующий deck.gl слой (например, ScatterplotLayer, ArcLayer, HexagonLayer и т.д.).


Момент создания deck.gl слоёв

Преобразование происходит в момент построения экземпляра KeplerGl компонента. Внутри происходит вызов фабрики слоёв:

  • берётся visState.layers
  • каждый слой проходит через слой-конструктор
  • создаётся массив deckLayers

Эти слои затем передаются в:

new Deck({
  layers: deckLayers
})

Таким образом, доступ к deck.gl слоям возможен только после того, как Kepler.gl завершил сборку сцены.


Получение доступа к экземпляру deck.gl

Прямой доступ к deck.gl instance — ключевой способ взаимодействия.

Через ref компонента KeplerGl

import KeplerGl from 'kepler.gl';

const mapRef = useRef(null);

<KeplerGl
  id="map"
  width={800}
  height={600}
  ref={mapRef}
/>

Однако сам ref не всегда предоставляет deck instance напрямую. Более надёжный путь — через store и middleware.


Доступ через Redux store

Kepler.gl построен поверх Redux, поэтому слой deck.gl можно извлекать из состояния приложения.

const state = store.getState();

const layers = state.keplerGl.map.visState.layers;

Но это всё ещё декларативное представление. Для получения deck.gl объектов требуется промежуточный слой — selectors.


Использование selectors для получения реальных слоёв

Kepler.gl предоставляет селекторы, которые позволяют получить уже трансформированные объекты:

import { visStateSelector } from 'kepler.gl/reducers';

const visState = visStateSelector(store.getState().keplerGl.map);

const deckLayers = visState.layers;

В некоторых конфигурациях visState.layers уже содержит экземпляры deck.gl Layer, а не только конфигурации.


Перехват deck.gl слоёв через middleware

Расширенный способ доступа — Redux middleware.

const layerInterceptor = store => next => action => {
  const result = next(action);

  if (action.type === 'LAYER_CONFIG_CHANGE') {
    const state = store.getState();
    const layers = state.keplerGl.map.visState.layers;

    console.log(layers);
  }

  return result;
};

Это позволяет отслеживать момент пересборки deck.gl слоёв.


Типы deck.gl слоёв внутри Kepler.gl

Kepler.gl использует набор предопределённых слоёв deck.gl:

  • ScatterplotLayer — точечные данные
  • LineLayer / ArcLayer — связи между точками
  • PolygonLayer — полигоны и границы
  • HexagonLayer — агрегация плотности
  • GridLayer — сеточная агрегация
  • GeoJsonLayer — произвольная геометрия

Каждый из них создаётся динамически на основе layer.type.


Маппинг Kepler.gl слоя в deck.gl слой

Внутренний процесс можно представить так:

KeplerLayer (config)
  -> LayerManager
    -> LayerFactory
      -> DeckGL Layer instance

Пример трансформации:

new ScatterplotLayer({
  id: layer.id,
  data: layer.data,
  getPosition: d => [d.lng, d.lat],
  getRadius: layer.config.radius,
  getFillColor: layer.config.color
})

Доступ к props deck.gl слоя

После создания слоя можно извлечь его параметры:

deckLayers.forEach(layer => {
  console.log(layer.id);
  console.log(layer.props);
});

Особенно важны:

  • data — входной массив
  • updateTriggers — механизм обновления
  • accessor functions — функции извлечения данных

Расширение слоёв через кастомные deck.gl классы

Kepler.gl позволяет подменять стандартные слои.

import { CompositeLayer } from '@deck.gl/core';

class CustomLayer extends CompositeLayer {
  renderLayers() {
    return [
      new ScatterplotLayer({
        id: `${this.props.id}-scatter`,
        data: this.props.data,
        getPosition: d => d.position,
        getFillColor: [0, 200, 255]
      })
    ];
  }
}

Далее слой регистрируется в Kepler.gl через расширение layerManager.


Доступ к слоям через instance deck.gl

Если доступен deck instance:

const deck = mapRef.current.deck;

const layers = deck.props.layers;

Это наиболее прямой способ получить фактические render-ready объекты.


Обновление deck.gl слоёв через Kepler.gl actions

Любое изменение слоёв должно проходить через actions:

import { addLayer, updateLayer } from 'kepler.gl/actions';

dispatch(updateLayer({
  ...layer,
  config: {
    ...layer.config,
    color: [0, 255, 0]
  }
}));

После dispatch происходит:

  1. обновление Redux state
  2. пересборка visState
  3. пересоздание deck.gl layers
  4. повторный render

Связь визуального состояния и deck.gl

Важно различать:

  • visState.layers — логическая модель
  • deck.gl layers — вычисленный runtime слой

Любое прямое вмешательство в deck.gl без синхронизации с visState приводит к рассинхронизации UI и состояния.


Контроль жизненного цикла слоёв

Deck.gl слои в Kepler.gl пересоздаются при:

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

Каждое пересоздание вызывает повторную генерацию массива layers, даже если конфигурация изменилась частично.


Оптимизация доступа к слоям

При работе с большим количеством слоёв важно учитывать:

  • избегать частого чтения store.getState() в рендере
  • использовать мемоизированные селекторы
  • минимизировать side-effects при чтении deck instance

Особенно критично это при работе с потоковыми данными и динамическими обновлениями.


Практика анализа слоёв в runtime

Для диагностики можно использовать логирование deck слоёв:

const inspectLayers = () => {
  const state = store.getState();
  const layers = state.keplerGl.map.visState.layers;

  layers.forEach(l => {
    console.log({
      id: l.id,
      type: l.type,
      visible: l.config.isVisible
    });
  });
};

Это позволяет сопоставить UI состояние с фактической визуализацией.


Взаимодействие с событиями deck.gl

deck.gl генерирует события, которые могут быть перехвачены:

new Deck({
  layers,
  onClick: info => {
    console.log(info.object);
  },
  onHover: info => {
    console.log(info.x, info.y);
  }
});

Kepler.gl проксирует эти события через собственную систему interactionConfig, связывая их с Redux actions.


Управление слоями на низком уровне

Прямой доступ к deck.gl слоям даёт возможность:

  • динамически заменять data source
  • изменять accessor функции
  • переключать визуальные каналы без пересборки Kepler.gl состояния
  • внедрять кастомные WebGL-слои

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