Кластеризация позволяет объединять большое количество близко расположенных точек в единые объекты, снижая нагрузку на карту и улучшая восприятие данных. Однако стандартное отображение кластеров редко соответствует требованиям реальных проектов. Для повышения информативности и визуальной привлекательности применяется стилизация кластеров.
В MapLibre GL JS кластеры представляют собой специальные объекты, создаваемые движком карты автоматически при включении кластеризации источника данных. Каждый кластер содержит набор служебных свойств:
cluster — признак того, что объект является
кластером;cluster_id — уникальный идентификатор кластера;point_count — количество точек внутри кластера;point_count_abbreviated — сокращённое представление
количества точек.Именно эти свойства используются для настройки внешнего вида кластеров.
Источник GeoJSON с включённой кластеризацией:
map.addSource('earthquakes', {
type: 'geojson',
data: 'earthquakes.geojson',
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
});
После создания источника обычно добавляются два слоя:
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#4A90E2',
'circle-radius': 20
}
});
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'earthquakes',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 12
}
});
В результате каждый кластер будет представлен синим кругом и числом внутри.
Одно из главных преимуществ MapLibre GL JS — поддержка выражений (Expressions), позволяющих динамически изменять внешний вид объектов на основе их свойств.
Количество объектов внутри кластера доступно через поле
point_count.
Пример изменения цвета в зависимости от размера кластера:
paint: {
'circle-color': [
'step',
['get', 'point_count'],
'#51bbd6',
100,
'#f1f075',
750,
'#f28cb1'
]
}
Логика работы:
| Количество точек | Цвет |
|---|---|
| до 100 | голубой |
| от 100 до 749 | жёлтый |
| от 750 | розовый |
Чем больше объектов внутри кластера, тем сильнее визуальное выделение.
Размер также можно связывать с количеством объединённых объектов.
paint: {
'circle-radius': [
'step',
['get', 'point_count'],
15,
100,
25,
750,
35
]
}
Теперь крупные кластеры будут не только менять цвет, но и становиться больше.
Подобный подход улучшает визуальное восприятие плотности данных.
На практике цвет и размер обычно изменяются одновременно.
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
'circle-color': [
'step',
['get', 'point_count'],
'#4FC3F7',
50,
'#29B6F6',
200,
'#039BE5',
1000,
'#01579B'
],
'circle-radius': [
'step',
['get', 'point_count'],
15,
50,
20,
200,
30,
1000,
45
]
}
});
Такая схема часто используется для отображения:
Функция step создаёт резкие переходы между значениями.
Для плавного изменения применяется interpolate.
paint: {
'circle-radius': [
'interpolate',
['linear'],
['get', 'point_count'],
0, 10,
100, 20,
500, 35,
1000, 50
]
}
Теперь радиус будет увеличиваться постепенно.
То же самое можно использовать для цвета.
paint: {
'circle-color': [
'interpolate',
['linear'],
['get', 'point_count'],
0, '#81D4FA',
500, '#29B6F6',
1000, '#01579B'
]
}
Результат выглядит значительно современнее по сравнению со ступенчатой схемой.
Для уменьшения визуальной перегрузки можно использовать прозрачность.
paint: {
'circle-opacity': 0.7
}
Или изменять прозрачность динамически:
paint: {
'circle-opacity': [
'interpolate',
['linear'],
['get', 'point_count'],
0, 0.5,
1000, 1
]
}
Крупные кластеры будут более заметными.
Контур помогает отделить кластер от фоновой карты.
paint: {
'circle-color': '#2196F3',
'circle-radius': 25,
'circle-stroke-width': 3,
'circle-stroke-color': '#FFFFFF'
}
Белая граница особенно полезна на спутниковых картах и насыщенных стилях.
Иногда требуется более выразительный дизайн.
Для этого создаются несколько слоёв кластеров.
Внешний слой:
map.addLayer({
id: 'cluster-shadow',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#90CAF9',
'circle-radius': 30,
'circle-opacity': 0.4
}
});
Внутренний слой:
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#1976D2',
'circle-radius': 20
}
});
В результате появляется эффект свечения или ореола.
Количество объектов обычно отображается через слой типа
symbol.
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'earthquakes',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 14,
'text-font': ['Open Sans Bold']
},
paint: {
'text-color': '#FFFFFF'
}
});
Настраиваемые параметры:
Размер подписи также может зависеть от количества объектов.
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': [
'interpolate',
['linear'],
['get', 'point_count'],
0, 10,
100, 14,
1000, 18
]
}
Большие кластеры автоматически получают более крупную подпись.
Вместо стандартного количества можно использовать выражения.
layout: {
'text-field': [
'case',
['<', ['get', 'point_count'], 100],
'Мало',
['<', ['get', 'point_count'], 500],
'Средне',
'Много'
]
}
Такой подход применяется в аналитических системах и информационных панелях.
Важно визуально различать кластеры и одиночные объекты.
Для этого используется фильтр:
filter: ['!', ['has', 'point_count']]
Пример слоя отдельных объектов:
map.addLayer({
id: 'unclustered-point',
type: 'circle',
source: 'earthquakes',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#11b4da',
'circle-radius': 6,
'circle-stroke-width': 1,
'circle-stroke-color': '#ffffff'
}
});
Чёткое разделение помогает пользователю понимать структуру данных на карте.
MapLibre GL JS поддерживает накопление пользовательских значений
внутри кластеров через параметр clusterProperties.
Например, можно суммировать количество продаж.
map.addSource('stores', {
type: 'geojson',
data: storesData,
cluster: true,
clusterProperties: {
totalSales: [
'+',
['get', 'sales']
]
}
});
После этого свойство totalSales становится доступным в
кластере.
Изменение цвета:
paint: {
'circle-color': [
'interpolate',
['linear'],
['get', 'totalSales'],
0, '#C8E6C9',
10000, '#4CAF50',
50000, '#1B5E20'
]
}
Теперь оформление зависит не от числа точек, а от суммарного объёма продаж.
Кластеры могут служить индикатором плотности.
Популярная схема:
paint: {
'circle-color': [
'step',
['get', 'point_count'],
'#00E676',
50,
'#FFEB3B',
200,
'#FF9800',
1000,
'#F44336'
]
}
Интерпретация:
Такое оформление напоминает тепловую карту, но требует значительно меньше вычислительных ресурсов.
Стили могут зависеть не только от данных, но и от текущего масштаба.
paint: {
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
0, 10,
10, 20,
15, 35
]
}
При приближении карты кластеры становятся крупнее.
Комбинация масштаба и количества объектов:
paint: {
'circle-radius': [
'interpolate',
['linear'],
['zoom'],
0, 10,
10, [
'interpolate',
['linear'],
['get', 'point_count'],
0, 15,
1000, 40
]
]
}
Подобная техника позволяет сохранять читаемость карты на любом уровне приближения.
MapLibre GL JS позволяет изменять свойства слоя динамически.
Например:
map.setPaintProperty(
'clusters',
'circle-color',
'#FF5722'
);
Или:
map.setPaintProperty(
'clusters',
'circle-radius',
40
);
Это может использоваться для:
Для небольших наборов данных:
Для городских сервисов:
Для аналитических панелей:
clusterProperties;Для карт с тёмной темой:
Грамотно настроенная стилизация превращает кластеры из простого механизма оптимизации в полноценный инструмент визуального анализа данных, позволяющий мгновенно оценивать плотность, распределение и значимость объектов на карте независимо от их количества.