Модель расширяемости в Kepler.gl построена вокруг принципов Redux-архитектуры и интеграции с визуальным стеком deck.gl. Расширения не являются отдельным изолированным API — они встраиваются в поток данных приложения через переопределение редьюсеров, добавление middleware, кастомных слоёв и модификацию состояния визуализации (visState, mapState, uiState).
Ключевая особенность заключается в том, что любое расширение фактически становится частью графа преобразований данных и рендеринга карты.
Kepler.gl предоставляет несколько уровней, на которых возможно вмешательство:
1. Redux-слой
2. Слой визуализации
3. Слой обработки данных
4. UI слой
Каждый уровень может использоваться независимо или в связке.
Основной механизм внедрения логики — композиция store через enhancers.
import keplerGlReducer from '@kepler.gl/reducers';
const customReducer = (state, action) => {
switch (action.type) {
case 'CUSTOM_SET_FLAG':
return {
...state,
customFlag: action.payload
};
default:
return state;
}
};
export const combinedReducer = keplerGlReducer.initialState({
uiState: {
activeSidePanel: null
}
});
Дальнейшее расширение происходит через
combineReducers:
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
keplerGl: combinedReducer,
custom: customReducer
});
Kepler.gl ожидает строгую структуру состояния. Любое расширение должно:
visStatedatasetsЭкшены используются для управления состоянием визуализации:
export const setCustomFilter = (value) => ({
type: 'CUSTOM_SET_FILTER',
value
});
Интеграция с Kepler.gl actions:
import KeplerGl from '@kepler.gl/components';
import { addDataToMap } from '@kepler.gl/actions';
dispatch(addDataToMap({
datasets: {
info: {
label: 'Custom Dataset',
id: 'custom_data'
},
data
}
}));
В расширениях часто создаются “обёртки” над стандартными экшенами для автоматической предобработки данных.
Наиболее мощный механизм расширений — добавление кастомных слоёв через deck.gl.
import { CompositeLayer } from '@deck.gl/core';
class HeatmapClusterLayer extends CompositeLayer {
renderLayers() {
return [
new ScatterplotLayer({
id: `${this.id}-scatter`,
data: this.props.data,
getPosition: d => d.coordinates,
getRadius: 100,
getFillColor: [255, 100, 0, 180]
})
];
}
}
HeatmapClusterLayer.layerName = 'HeatmapClusterLayer';
import KeplerGl from '@kepler.gl/components';
const customLayers = {
heatmap_cluster: HeatmapClusterLayer
};
<KeplerGl
id="map"
width={1200}
height={800}
mapboxApiAccessToken={TOKEN}
customLayers={customLayers}
/>
Kepler.gl Layer Manager:
layerNamevisState.layersvisState — центральная структура, управляющая слоями,
фильтрами и интеракциями.
const customVisStateReducer = (state, action) => {
switch (action.type) {
case 'SET_POINT_SIZE_MULTIPLIER':
return {
...state,
pointSizeMultiplier: action.value
};
default:
return state;
}
};
getRadius: d => d.size * state.visState.pointSizeMultiplier
Таким образом расширение может влиять на рендеринг без изменения core logic Kepler.gl.
UI Kepler.gl основан на наборе переиспользуемых React-компонентов.
import KeplerGl from '@kepler.gl/components';
const customPanels = {
sidePanel: CustomSidePanel
};
<KeplerGl
id="map"
width={800}
height={600}
mapboxApiAccessToken={TOKEN}
panels={customPanels}
/>
Расширения часто внедряют:
Пример:
const CustomControl = ({ dispatch }) => (
<button onCl ick={() => dispatch({ type: 'CUSTOM_TOGGLE' })}>
Toggle mode
</button>
);
Middleware используется для перехвата потоков экшенов:
const keplerMiddleware = store => next => action => {
if (action.type === 'LAYER_VISUAL_CHANNEL_CHANGE') {
console.log('Layer updated:', action);
}
return next(action);
};
Применение middleware позволяет:
Kepler.gl использует data processors для нормализации входных данных.
function customProcessor(data) {
return data.map(row => ({
...row,
timestamp: new Date(row.time).getTime()
}));
}
Интеграция:
dispatch(addDataToMap({
datasets: {
info: { id: 'processed' },
data: customProcessor(rawData)
}
}));
Расширения в Kepler.gl часто оформляются как плагины:
/plugins
/custom-layer
index.js
layer.js
reducer.js
ui.js
const plugins = [
{
name: 'custom-layer-plugin',
reducer: customReducer,
layer: HeatmapClusterLayer,
ui: CustomPanel
}
];
Такая структура позволяет масштабировать систему без изменения core пакетов.
Расширения взаимодействуют с несколькими фазами:
1. Инициализация
2. Обновление состояния
3. Рендеринг
4. Синхронизация
При одновременном использовании нескольких расширений возникают типовые проблемы:
layerIdРешение заключается в:
Комбинирование нескольких deck.gl слоёв внутри одного расширения позволяет строить сложные визуальные эффекты:
renderLayers() {
return [
new ScatterplotLayer(this.props),
new TextLayer({
...this.props,
getText: d => d.label
})
];
}
Такой подход часто используется для:
При работе с большими данными критично учитывать:
Особенно затратны:
Kepler.gl поддерживает реакцию на события карты:
onStateCha nge = (action, state) => {
if (action.type === 'UPDATE_MAP') {
syncExternalSystem(state.visState);
}
};
Это позволяет строить: