Kepler.gl изначально проектировалась как высокоуровневая надстройка над стеком визуализации deck.gl и управления картографическим состоянием. Её архитектура построена таким образом, чтобы базовая функциональность оставалась компактной, а расширение происходило через предсказуемые точки интеграции: Redux-слой, кастомные компоненты React, расширяемые слои визуализации и работа с состоянием карты.
Внутренняя структура Kepler.gl разделена на несколько ключевых слоёв:
Расширение функциональности происходит не через модификацию ядра, а через подключение к этим слоям. Такой подход позволяет внедрять новые возможности без форка библиотеки.
Ключевой принцип: любая кастомизация должна быть выражена через состояние или слой рендера, а не через прямое изменение исходного кода.
Наиболее мощный механизм расширения — добавление пользовательских слоёв поверх deck.gl.
Kepler.gl поддерживает регистрацию новых типов слоёв через расширение
конфигурации layerClasses. Это позволяет внедрять
собственные WebGL-слои или адаптировать существующие.
Пример подключения кастомного слоя:
import KeplerGl from 'kepler.gl';
import { Layer } from '@deck.gl/core';
class CustomScatterLayer extends Layer {
initializeState() {
this.getAttributeManager().addInstanced({
positions: { size: 3, accessor: 'getPosition' }
});
}
draw({ uniforms }) {
// WebGL рендеринг логики слоя
}
}
Далее слой регистрируется в конфигурации:
const customLayers = {
custom_scatter: CustomScatterLayer
};
<KeplerGl
id="map"
width={800}
height={600}
layers={customLayers}
/>
Расширение через слои используется для:
В основе Kepler.gl лежит централизованное состояние. Оно разбито на:
visState — данные слоёв, фильтров и взаимодействийmapState — позиция камеры и параметры картыuiState — интерфейсРасширение через Redux позволяет внедрять бизнес-логику без изменения UI.
Подключение кастомного reducer:
import keplerGlReducer from 'kepler.gl/reducers';
const customizedReducer = keplerGlReducer.initialState({
uiState: {
readOnly: false
}
});
Дополнительные действия можно внедрять через middleware:
const customMiddleware = store => next => action => {
if (action.type === 'LAYER_CLICK') {
console.log('Layer clicked:', action);
}
return next(action);
};
Это позволяет:
Kepler.gl предоставляет набор стандартных actions, но архитектура Redux позволяет добавлять собственные действия, не конфликтующие с внутренними.
Пример расширения:
export const CUSTOM_FILTER_UPDATE = 'CUSTOM_FILTER_UPDATE';
export function updateCustomFilter(payload) {
return {
type: CUSTOM_FILTER_UPDATE,
payload
};
}
Reducer расширяется:
function customReducer(state, action) {
switch (action.type) {
case CUSTOM_FILTER_UPDATE:
return {
...state,
customFilter: action.payload
};
default:
return state;
}
}
Использование такого подхода особенно важно при:
UI Kepler.gl построен на React-компонентах и может быть переопределён через injection-механизмы.
Основные точки расширения:
Пример замены панели фильтров:
import {injectComponents} from 'kepler.gl/components';
const CustomFilterPanel = () => (
<div>
<h3>Custom Filters</h3>
</div>
);
const KeplerGlWithCustomUI = injectComponents([
[FilterPanelFactory, CustomFilterPanel]
])(KeplerGl);
Это позволяет:
visState — центральный объект, управляющий слоями и
визуализацией.
Его можно расширять программно:
dispatch(
addDataToMap({
datasets: {
info: { label: 'Custom Data' },
data: geojsonData
},
options: {
centerMap: true
}
})
);
Расширенные сценарии:
Kepler.gl поддерживает подключение различных форматов данных, но расширение функциональности часто связано с адаптацией нестандартных источников.
Пример адаптера:
async function fetchFromApi(url) {
const res = await fetch(url);
const json = await res.json();
return {
fields: Object.keys(json[0]),
rows: json.map(Object.values)
};
}
После адаптации данные подаются в Kepler.gl как стандартный dataset.
Использование:
Фильтрация в Kepler.gl реализована как часть visState,
но может быть расширена через пользовательские вычисления.
Пример кастомной логики фильтрации:
function customFilter(data, threshold) {
return data.filter(row => row.value > threshold);
}
Интеграция в pipeline:
Расширение фильтров применяется для:
Kepler.gl позволяет перехватывать события взаимодействия:
Через middleware или кастомные handlers можно расширить поведение:
map.onCl ick = (info) => {
dispatch({
type: 'FEATURE_SELECTED',
payload: info.object
});
};
Это используется для:
Хотя Kepler.gl не имеет классической plugin API, расширение реализуется через композицию:
Типовая схема плагина:
export function createKeplerPlugin() {
return {
reducer: customReducer,
components: customComponents,
layers: customLayers,
middleware: customMiddleware
};
}
Далее интеграция:
const store = createStore(
combineReducers({
keplerGl: keplerGlReducer,
custom: customReducer
}),
applyMiddleware(customMiddleware)
);
Одной из ключевых возможностей является синхронизация состояния карты с URL или внешними системами.
Пример сериализации:
const state = store.getState().keplerGl;
const encoded = btoa(JSON.stringify(state.visState));
Использование:
Kepler.gl часто используется как часть более крупных систем:
Расширение в таких сценариях включает:
Помимо стандартных GeoJSON и таблиц, расширение возможно через предварительную трансформацию данных.
Пример трансформации:
function normalizeData(raw) {
return raw.map(item => ({
lat: item.coordinates[0],
lng: item.coordinates[1],
value: item.metric
}));
}
Такая нормализация позволяет:
Расширение функциональности требует учёта производительности WebGL-рендеринга deck.gl.
Ключевые ограничения:
Оптимизационные практики:
Расширение Redux-слоя требует строгого разделения ответственности:
Пример селектора:
const selectCustomData = state => state.customModule.data;
Такой подход снижает риск конфликтов при обновлениях библиотеки и упрощает поддержку крупных систем на основе Kepler.gl.