Эффективность визуализации в Kepler.gl напрямую зависит от качества исходных данных. Даже при наличии мощных инструментов агрегации, фильтрации и стилизации некорректные, неполные или плохо структурированные наборы данных приводят к ошибкам отображения, снижению производительности и искажению аналитических выводов.
Предобработка данных представляет собой комплекс операций по подготовке набора данных к загрузке в Kepler.gl. Основные задачи данного этапа:
Большинство операций выполняется до передачи данных в компонент Kepler.gl, обычно средствами JavaScript или серверной логики.
Kepler.gl работает преимущественно с табличными данными. Каждая строка представляет отдельный объект наблюдения.
Пример набора данных:
| id | city | latitude | longitude | population |
|---|---|---|---|---|
| 1 | Almaty | 43.2389 | 76.8897 | 1977000 |
| 2 | Astana | 51.1694 | 71.4491 | 1350000 |
В JavaScript такие данные часто представлены массивом объектов:
const cities = [
{
id: 1,
city: "Almaty",
latitude: 43.2389,
longitude: 76.8897,
population: 1977000
},
{
id: 2,
city: "Astana",
latitude: 51.1694,
longitude: 71.4491,
population: 1350000
}
];
Перед загрузкой желательно убедиться, что:
Одной из самых распространенных проблем являются отсутствующие значения.
Пример:
const rawData = [
{
city: "Almaty",
latitude: 43.2389,
longitude: 76.8897
},
{
city: "Unknown",
latitude: null,
longitude: 70.1234
}
];
Если слой требует координаты, запись без широты становится бесполезной.
Фильтрация выполняется следующим образом:
const cleanedData = rawData.filter(
item =>
item.latitude !== null &&
item.longitude !== null
);
Более универсальный вариант:
const cleanedData = rawData.filter(item =>
Number.isFinite(item.latitude) &&
Number.isFinite(item.longitude)
);
Такой подход позволяет исключить:
null;undefined;NaN;Повторяющиеся записи искажают тепловые карты, кластеры и статистические показатели.
Исходный набор:
const data = [
{ id: 1, city: "Almaty" },
{ id: 1, city: "Almaty" },
{ id: 2, city: "Astana" }
];
Удаление по идентификатору:
const uniqueData = [
...new Map(
data.map(item => [item.id, item])
).values()
];
Результат:
[
{ id: 1, city: "Almaty" },
{ id: 2, city: "Astana" }
]
При отсутствии уникального идентификатора возможно использование комбинации полей.
const unique = [
...new Map(
data.map(item => [
`${item.city}_${item.latitude}_${item.longitude}`,
item
])
).values()
];
Kepler.gl ожидает географические координаты в формате WGS84:
Проверка координат:
function isValidCoordinate(lat, lng) {
return (
lat >= -90 &&
lat <= 90 &&
lng >= -180 &&
lng <= 180
);
}
Фильтрация:
const validPoints = data.filter(point =>
isValidCoordinate(
point.latitude,
point.longitude
)
);
Некоторые системы хранят координаты в обратном порядке.
Некорректно:
{
latitude: 76.8897,
longitude: 43.2389
}
Корректно:
{
latitude: 43.2389,
longitude: 76.8897
}
Ошибки порядка координат являются одной из наиболее частых причин отображения объектов в океане или на другом континенте.
Координаты часто поступают как строки.
Пример:
{
latitude: "43.2389",
longitude: "76.8897"
}
Для корректной работы слоев значения должны быть числами.
Преобразование:
const normalized = data.map(item => ({
...item,
latitude: parseFloat(item.latitude),
longitude: parseFloat(item.longitude)
}));
Дополнительная проверка:
const normalized = data
.map(item => ({
...item,
latitude: Number(item.latitude),
longitude: Number(item.longitude)
}))
.filter(item =>
Number.isFinite(item.latitude) &&
Number.isFinite(item.longitude)
);
Kepler.gl поддерживает временную анимацию через Time Playback.
Для этого даты должны иметь корректный формат.
Исходные данные:
{
event: "Delivery",
date: "2025-05-15 14:30"
}
Преобразование в ISO-формат:
const normalized = data.map(item => ({
...item,
timestamp: new Date(item.date).toISOString()
}));
Результат:
{
timestamp: "2025-05-15T14:30:00.000Z"
}
Преимущества ISO-формата:
Во многих API используется Unix Timestamp.
Пример:
{
createdAt: 1747303200
}
Конвертация:
const converted = data.map(item => ({
...item,
createdAt: new Date(
item.createdAt * 1000
).toISOString()
}));
Если timestamp уже в миллисекундах:
new Date(item.createdAt).toISOString();
Даже небольшие различия в написании приводят к ошибкам группировки.
Проблемный набор:
[
{ city: "Almaty" },
{ city: " almaty " },
{ city: "ALMATY" }
]
Нормализация:
const normalized = data.map(item => ({
...item,
city: item.city.trim().toLowerCase()
}));
Результат:
[
{ city: "almaty" },
{ city: "almaty" },
{ city: "almaty" }
]
Для отображения можно дополнительно использовать форматирование:
function capitalize(value) {
return value.charAt(0).toUpperCase() +
value.slice(1);
}
Не всегда необходимо отображать каждую запись.
Исходные данные:
[
{ city: "Almaty", sales: 100 },
{ city: "Almaty", sales: 150 },
{ city: "Astana", sales: 90 }
]
Группировка:
const grouped = data.reduce(
(acc, item) => {
acc[item.city] =
(acc[item.city] || 0) +
item.sales;
return acc;
},
{}
);
Результат:
{
Almaty: 250,
Astana: 90
}
Подготовка для Kepler.gl:
const aggregated = Object.entries(grouped)
.map(([city, sales]) => ({
city,
sales
}));
Преимущества агрегации:
При работе с миллионами записей производительность браузера может заметно снижаться.
Выборка части данных:
const sample = data.slice(0, 100000);
Случайная выборка:
const sample = data.filter(
() => Math.random() < 0.1
);
Выборка 10% записей:
const sample = data.filter(
(_, index) => index % 10 === 0
);
Такой подход часто используется на этапе предварительного анализа.
Kepler.gl способен работать с готовыми геометриями.
Формирование объекта Point:
const geoData = data.map(item => ({
...item,
geometry: {
type: "Point",
coordinates: [
item.longitude,
item.latitude
]
}
}));
Полученный объект соответствует стандарту GeoJSON.
Пример:
{
type: "Point",
coordinates: [76.8897, 43.2389]
}
Важно помнить, что GeoJSON использует порядок:
[longitude, latitude]
а не наоборот.
Для отображения маршрутов требуется геометрия типа LineString.
Исходные точки:
const route = [
[76.8897, 43.2389],
[77.0400, 43.2700],
[77.2000, 43.3200]
];
Создание объекта:
const line = {
type: "LineString",
coordinates: route
};
Использование:
const routes = [
{
routeId: 1,
geometry: line
}
];
Полигоны применяются для отображения районов, административных границ и зон покрытия.
Пример:
const polygon = {
type: "Polygon",
coordinates: [[
[76.8, 43.2],
[77.0, 43.2],
[77.0, 43.4],
[76.8, 43.4],
[76.8, 43.2]
]]
};
Первую и последнюю точку необходимо замыкать.
Некорректно:
[
[76.8, 43.2],
[77.0, 43.2],
[77.0, 43.4]
]
Корректно:
[
[76.8, 43.2],
[77.0, 43.2],
[77.0, 43.4],
[76.8, 43.4],
[76.8, 43.2]
]
Перед загрузкой часто создаются дополнительные признаки.
Исходные данные:
{
revenue: 12000,
orders: 300
}
Вычисление среднего чека:
const prepared = data.map(item => ({
...item,
averageOrderValue:
item.revenue / item.orders
}));
Создание категории:
const prepared = data.map(item => ({
...item,
category:
item.revenue > 10000
? "high"
: "low"
}));
Такие поля затем используются для:
Нередко один набор данных используется одновременно несколькими слоями.
Исходный объект:
{
id: 1,
latitude: 43.2389,
longitude: 76.8897,
revenue: 12000
}
Подготовка различных представлений:
const pointsLayerData = data.map(item => ({
latitude: item.latitude,
longitude: item.longitude,
revenue: item.revenue
}));
const heatmapLayerData = data.map(item => ({
latitude: item.latitude,
longitude: item.longitude,
weight: item.revenue
}));
Разделение данных заранее позволяет избежать лишних вычислений внутри интерфейса.
Типичный процесс подготовки набора данных перед передачей в Kepler.gl выглядит следующим образом:
const preparedData = rawData
.filter(item =>
Number.isFinite(item.latitude) &&
Number.isFinite(item.longitude)
)
.map(item => ({
...item,
latitude: Number(item.latitude),
longitude: Number(item.longitude),
timestamp: new Date(
item.timestamp
).toISOString(),
city: item.city.trim()
}))
.filter(item =>
item.city.length > 0
);
Такой конвейер обеспечивает: