В Mapbox GL JS кластеризация данных реализуется на уровне источника
GeoJSONSource через параметр cluster: true.
При этом визуализация кластеров и отдельных точек строится с
использованием нескольких слоёв, ключевым из которых является
circle layer.
Mapbox GL JS позволяет отображать большие наборы точечных данных за
счёт агрегации близких объектов в кластеры. Эти кластеры становятся
виртуальными объектами с полем point_count, которое
используется для стилизации через circle layer.
Базовая структура источника данных:
map.addSource('earthquakes', {
type: 'geojson',
data: '/data/earthquakes.geojson',
cluster: true,
clusterRadius: 50,
clusterMaxZoom: 14
});
Параметры кластеризации:
cluster: true — включает группировку точекclusterRadius — радиус в пикселях, определяющий
плотность кластеризацииclusterMaxZoom — максимальный zoom, на котором
выполняется кластеризацияCircle layer применяется для отображения кластерных объектов как кругов с изменяемыми параметрами радиуса, цвета и прозрачности.
Ключевой момент: кластерные объекты отличаются от обычных точек наличием свойства:
point_count — количество точек внутри кластераЭто позволяет разделять визуализацию на три логических слоя:
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#51bbd6',
'circle-radius': 18,
'circle-stroke-width': 2,
'circle-stroke-color': '#fff'
}
});
filter: ['has', 'point_count']
Означает, что слой применим только к объектам, содержащим
point_count, то есть кластерам.
Для некластеризованных точек используется обратный фильтр:
filter: ['!', ['has', 'point_count']]
Размер круга часто зависит от количества точек внутри кластера. Для
этого используется выражение step:
'circle-radius': [
'step',
['get', 'point_count'],
15,
10, 20,
50, 30,
100, 40
]
Логика:
Такой подход позволяет визуально выделять плотные области данных.
Circle layer часто дополняется цветовой шкалой:
'circle-color': [
'step',
['get', 'point_count'],
'#a6cee3',
20,
'#1f78b4',
50,
'#b2df8a',
100,
'#33a02c'
]
Цвет становится индикатором плотности данных, что уменьшает необходимость чтения подписей.
Circle layer сам по себе не отображает числовое значение, поэтому поверх него используется symbol layer:
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'earthquakes',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],
'text-size': 12
}
});
Ключевые элементы:
{point_count_abbreviated} — сокращённое отображение
числа (например, 1.2k)text-size — масштаб текста независимо от circle
radiusОтдельный circle layer используется для одиночных объектов:
map.addLayer({
id: 'unclustered-point',
type: 'circle',
source: 'earthquakes',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#ff5722',
'circle-radius': 6,
'circle-stroke-width': 1,
'circle-stroke-color': '#fff'
}
});
Такая схема обеспечивает визуальное разделение между агрегированными и исходными данными.
В Mapbox GL JS порядок добавления слоёв критичен:
Это гарантирует корректное перекрытие элементов.
Circle layer часто используется как интерактивная зона для раскрытия кластеров.
map.on('click', 'clusters', (e) => {
const features = map.queryRenderedFeatures(e.point, {
layers: ['clusters']
});
const clusterId = features[0].properties.cluster_id;
const source = map.getSource('earthquakes');
source.getClusterExpansionZoom(clusterId, (err, zoom) => {
if (err) return;
map.easeTo({
center: features[0].geometry.coordinates,
zoom: zoom
});
});
});
cluster_id идентифицирует конкретный кластерgetClusterExpansionZoom вычисляет zoom, на котором
кластер распадаетсяeaseTo выполняет плавное приближениеСостояния hover реализуются через изменение paint свойств:
map.on('mouseenter', 'clusters', () => {
map.setPaintProperty('clusters', 'circle-stroke-width', 3);
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'clusters', () => {
map.setPaintProperty('clusters', 'circle-stroke-width', 2);
map.getCanvas().style.cursor = '';
});
Circle layer поддерживает выражения Mapbox Style Specification.
Пример комбинированного управления прозрачностью:
'circle-opacity': [
'interpolate',
['linear'],
['zoom'],
5, 0.6,
10, 0.9
]
Или зависимость от количества точек:
'circle-opacity': [
'interpolate',
['linear'],
['get', 'point_count'],
0, 0.4,
100, 1
]
Circle layer сам по себе лёгкий, но основная нагрузка возникает на этапе генерации кластеров в источнике.
Ключевые оптимизации:
clusterRadius для уменьшения пересчёта
кластеровclusterMaxZoom для отключения
кластеризации на высоких zoomТипичная архитектура слоёв:
clusters — circle layer для кластеровcluster-count — symbol layer с числомunclustered-point — circle layer для одиночных
точекТакая структура позволяет отделить:
Circle layer поддерживает полную data-driven стилизацию:
'circle-color': [
'case',
['>', ['get', 'point_count'], 50],
'#e31a1c',
'#1f78b4'
]
Такой подход позволяет формировать сложные визуальные кодировки без дополнительных слоёв.
Circle layer изменяет визуальное восприятие в зависимости от масштаба:
Эта динамика напрямую связана с параметром
clusterMaxZoom.
Circle layer может комбинироваться с фильтрами по атрибутам GeoJSON:
filter: [
'all',
['has', 'point_count'],
['>', ['get', 'point_count'], 20]
]
Это позволяет отображать только “значимые” кластеры, игнорируя мелкие группы.
Circle layer для кластеров используется в задачах:
В каждом случае circle layer остаётся базовым примитивом визуализации кластеров благодаря простоте и высокой производительности.