Кластеризация в Mapbox GL JS реализуется на уровне источника данных GeoJSON и представляет собой механизм агрегации множества точечных объектов в единые кластерные сущности. Каждый кластер становится виртуальным объектом с собственными свойствами, которые вычисляются на основе исходных точек. Управление поведением кластеризации осуществляется через параметры источника и выражения агрегации.
Основой работы кластеров является конфигурация источника данных:
map.addSource('points', {
type: 'geojson',
data: geojsonData,
cluster: true,
clusterRadius: 50,
clusterMaxZoom: 14
});
Активирует кластеризацию. При значении true Mapbox GL JS
перестаёт отображать все точки напрямую и вместо этого группирует их в
кластеры на основе пространственной близости.
Определяет радиус кластеризации в пикселях. Этот параметр влияет на плотность кластеров:
Задает максимальный zoom-уровень, на котором продолжается кластеризация. После этого уровня кластеры “распадаются” на отдельные точки.
Одним из ключевых механизмов является clusterProperties,
позволяющий создавать пользовательские агрегаты на основе свойств точек
внутри кластера.
map.addSource('points', {
type: 'geojson',
data: geojsonData,
cluster: true,
clusterRadius: 50,
clusterProperties: {
sumValue: ['+', ['get', 'value']],
maxValue: ['max', ['get', 'value']],
minValue: ['min', ['get', 'value']]
}
});
Каждое свойство кластера вычисляется через выражение, которое агрегирует значения всех входящих в кластер точек. Внутри используются expression syntax Mapbox GL JS.
Основные типы агрегаций:
sumValue: ['+', ['get', 'value']]
Суммирует значения свойства value всех точек.
maxValue: ['max', ['get', 'value']],
minValue: ['min', ['get', 'value']]
Можно создавать несколько категорий внутри кластера:
clusterProperties: {
categoryA: [
'+',
['case', ['==', ['get', 'type'], 'A'], 1, 0]
],
categoryB: [
'+',
['case', ['==', ['get', 'type'], 'B'], 1, 0]
]
}
Каждый кластер автоматически получает стандартное свойство:
point_count — количество точек внутри кластераТакже доступно:
point_count_abbreviated — сокращённое отображение
(например, 1.2k)Это значение используется для отображения размеров и текста кластера.
Свойства кластеров применяются в слоях через data-driven styling.
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'points',
filter: ['has', 'point_count'],
paint: {
'circle-radius': [
'step',
['get', 'point_count'],
20,
100,
30,
750,
40
],
'circle-color': [
'step',
['get', 'point_count'],
'#51bbd6',
100,
'#f1f075',
750,
'#f28cb1'
]
}
});
Здесь используется выражение step, которое позволяет
менять визуализацию в зависимости от размера кластера.
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'points',
filter: ['has', 'point_count'],
layout: {
'text-field': ['get', 'point_count_abbreviated'],
'text-size': 12
}
});
Можно также использовать пользовательские агрегаты:
'text-field': [
'concat',
'sum: ',
['to-string', ['get', 'sumValue']]
]
Mapbox GL JS предоставляет методы для работы с кластерами на уровне источника.
Позволяет определить zoom, на котором кластер “раскрывается”:
map.getSource('points').getClusterExpansionZoom(clusterId, (err, zoom) => {
map.easeTo({ center: coordinates, zoom });
});
Это используется для интерактивного приближения к кластеру.
Возвращает точки, входящие в кластер:
map.getSource('points').getClusterLeaves(
clusterId,
limit,
offset,
(err, features) => {
console.log(features);
}
);
Параметры:
limit — количество возвращаемых объектовoffset — смещение для пагинацииПозволяет получить дочерние кластеры или точки:
map.getSource('points').getClusterChildren(clusterId, (err, features) => {
console.log(features);
});
Expression system Mapbox GL JS поддерживает набор операторов, применимых в clusterProperties.
['+', value]
['*', value]
['-', value]
['/', value]
['case',
condition, result,
fallback
]
['get', 'propertyName']
clusterProperties: {
weightedSum: [
'+',
['*', ['get', 'weight'], ['get', 'value']]
]
}
После обработки GeoJSON каждый кластер приобретает структуру:
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [lng, lat]
},
"properties": {
"cluster": true,
"cluster_id": 123,
"point_count": 42,
"sumValue": 1200,
"maxValue": 98
}
}
Эта структура позволяет использовать кластеры как полноценные объекты в слоях и интерактивных обработчиках событий.
Кластеры обрабатываются через события слоя:
map.on('click', 'clusters', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['clusters']
});
const clusterId = features[0].properties.cluster_id;
});
Дальнейшая логика обычно включает:
clusterProperties позволяет строить аналитические кластеры:
Пример комбинированной аналитики:
clusterProperties: {
totalSales: ['+', ['get', 'sales']],
avgApprox: [
'/',
['+', ['get', 'sales']],
['get', 'point_count']
],
highPriorityCount: [
'+',
['case', ['>', ['get', 'priority'], 5], 1, 0]
]
}
Кластеризация выполняется на стороне клиента WebGL-движка Mapbox GL JS, что накладывает ограничения:
Кластеры динамически перестраиваются при изменении масштаба:
Это делает кластеризацию зависимой от текущего состояния камеры карты и позволяет адаптивно отображать плотность данных.