Агрегация в контексте Mapbox GL JS — это преобразование большого количества точечных объектов в более компактные и интерпретируемые представления: кластеры, плотности, суммарные показатели или производные метрики. Такой подход критичен при работе с десятками тысяч и миллионами объектов, когда рендеринг каждого элемента отдельно приводит к деградации производительности и перегрузке визуального слоя.
Базовый механизм агрегации в Mapbox GL JS реализуется через кластеризацию источников типа GeoJSON.
map.addSource('points', {
type: 'geojson',
data: 'data.geojson',
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
Ключевые параметры:
Внутренне используется алгоритм, основанный на пространственном разбиении (аналогично Supercluster в экосистеме Mapbox), где точки объединяются в группы в зависимости от масштаба и расстояния.
После включения кластеризации источник автоматически генерирует дополнительные виртуальные сущности:
Пример слоя для кластеров:
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count'],
paint: {
'circle-radius': [
'step',
['get', 'point_count'],
15, 10,
25, 50,
35
],
'circle-color': '#51bbd6'
}
});
И слой для отображения количества объектов:
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'points',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 12
}
});
Mapbox GL JS позволяет расширять стандартную агрегацию с помощью
clusterProperties. Это механизм вычисления пользовательских
метрик внутри кластера.
map.addSource('points', {
type: 'geojson',
data: 'data.geojson',
cluster: true,
clusterProperties: {
sum: ['+', ['get', 'value']],
maxValue: ['max', ['get', 'value']]
}
});
Здесь формируются дополнительные поля:
valueЭти данные становятся доступны через свойства кластера и могут использоваться в стилях:
'circle-radius': [
'interpolate',
['linear'],
['get', 'sum'],
0, 10,
1000, 40
]
Heatmap-слой представляет собой непрямую агрегацию плотности точек. Вместо группировки объектов формируется непрерывное поле интенсивности.
map.addLayer({
id: 'heatmap',
type: 'heatmap',
source: 'points',
maxzoom: 15,
paint: {
'heatmap-weight': [
'interpolate',
['linear'],
['get', 'value'],
0, 0,
10, 1
],
'heatmap-intensity': 1,
'heatmap-radius': 20,
'heatmap-opacity': 0.8
}
});
Математически это можно рассматривать как взвешенную плотность распределения точек, где каждая точка вносит вклад в локальную область влияния.
При использовании векторных тайлов агрегация часто переносится на уровень генерации данных. Это снижает нагрузку на клиент.
Типичные сценарии:
Слой в Mapbox GL JS в этом случае получает уже агрегированные значения:
map.addSource('tiles', {
type: 'vector',
url: 'mapbox://tileset.id'
});
И стилизация работает напрямую с готовыми метриками:
'fill-color': [
'interpolate',
['linear'],
['get', 'density'],
0, '#f2f0f7',
100, '#54278f'
]
Дополнительный уровень агрегации реализуется на стороне клиента через анализ отображаемых объектов.
const features = map.queryRenderedFeatures({ layers: ['points-layer'] });
const aggregated = features.reduce((acc, f) => {
acc.total += f.properties.value || 0;
acc.count += 1;
return acc;
}, { total: 0, count: 0 });
Такой подход применяется для:
При сложных сценариях используется комбинированный подход:
Комбинация уровней позволяет балансировать между:
Ключевые факторы, влияющие на эффективность агрегации:
Особенно затратными становятся:
Агрегированные данные в Mapbox GL JS тесно связаны с системой выражений стилей:
step — дискретная сегментация значенийinterpolate — плавная интерполяцияmatch — категориальная агрегацияПример комбинированного подхода:
'circle-color': [
'step',
['get', 'point_count'],
'#00ff00',
100,
'#ffa500',
1000,
'#ff0000'
]
Такая схема позволяет визуально кодировать распределение плотности без дополнительных вычислений вне GPU-пайплайна рендерера.