Обработка большого количества маркеров на карте приводит к снижению производительности и ухудшению визуального восприятия: десятки или сотни объектов перекрывают друг друга, затрудняя навигацию и анализ данных. Решением выступает кластеризация — группировка близко расположенных маркеров в единые визуальные элементы, которые динамически изменяются при масштабировании карты.
В контексте Google Maps JavaScript API кластеризация чаще всего реализуется через сторонние библиотеки, одной из наиболее известных среди которых является MarkerClustererPlus.
При отображении большого числа точек возникают следующие ограничения:
google.maps.MarkerДаже 200–300 маркеров могут создавать заметную нагрузку на слабых
устройствах, особенно при активных событиях zoom_changed и
drag.
Кластеризация основана на пространственном объединении маркеров в зависимости от текущего масштаба карты:
Алгоритмически используется разбиение по сетке (grid-based clustering) или деревья пространственного поиска (quad-tree / k-d tree), однако MarkerClustererPlus использует упрощённый grid-based подход, оптимизированный для браузера.
Перед использованием кластеризации необходимо подключить сам API:
<script
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"
async
defer
></script>
Инициализация карты:
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 48.0, lng: 66.0 },
zoom: 5,
});
}
Подключение библиотеки:
<script src="https://unpkg.com/@googlemaps/markerclustererplus/dist/index.min.js"></script>
После подключения становится доступен объект
MarkerClusterer.
Создание маркеров и их объединение в кластер:
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 48.0, lng: 66.0 },
zoom: 5,
});
const locations = [
{ lat: 48.0, lng: 66.0 },
{ lat: 49.0, lng: 67.0 },
{ lat: 47.5, lng: 65.5 },
];
const markers = locations.map((position) => {
return new google.maps.Marker({
position,
map,
});
});
const clusterer = new MarkerClusterer(map, markers, {
imagePath:
"https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m",
});
}
MarkerClustererPlus поддерживает набор конфигураций, влияющих на поведение группировки:
gridSize — размер сетки кластеризацииmaxZoom — уровень зума, при котором кластер перестаёт
формироватьсяminimumClusterSize — минимальное количество маркеров в
кластереstyles — кастомные стили отображения кластеровПример настройки:
const clusterer = new MarkerClusterer(map, markers, {
gridSize: 60,
maxZoom: 15,
minimumClusterSize: 2,
imagePath:
"https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m",
});
Визуальное оформление кластеров критично для UX. Можно задавать разные стили в зависимости от размера группы:
const styles = [
{
url: "cluster-small.png",
width: 40,
height: 40,
textColor: "#ffffff",
textSize: 12,
},
{
url: "cluster-medium.png",
width: 50,
height: 50,
textColor: "#ffffff",
textSize: 13,
},
{
url: "cluster-large.png",
width: 60,
height: 60,
textColor: "#ffffff",
textSize: 14,
},
];
const clusterer = new MarkerClusterer(map, markers, {
styles,
});
Стили применяются в зависимости от количества маркеров в группе.
Кластеры поддерживают события взаимодействия:
google.maps.event.addListener(clusterer, "clusterclick", (cluster) => {
const bounds = cluster.getBounds();
map.fitBounds(bounds);
});
Такой подход позволяет реализовать поведение «drill-down» — при клике карта приближается к области кластера.
При изменении данных требуется пересоздание кластеров:
function updateMarkers(newLocations) {
const newMarkers = newLocations.map((pos) => {
return new google.maps.Marker({ position: pos });
});
clusterer.clearMarkers();
clusterer.addMarkers(newMarkers);
}
Ключевой момент — очистка старого состояния перед добавлением нового набора маркеров.
При применении фильтров (например, по категории или радиусу) используется пересборка:
function filterMarkers(conditionFn, allMarkers) {
const filtered = allMarkers.filter((m) =>
conditionFn(m.getPosition())
);
clusterer.clearMarkers();
clusterer.addMarkers(filtered);
}
При работе с тысячами объектов необходимо учитывать:
DocumentFragment при генерации
данныхДополнительно полезно:
maxZoomsetMap(null) для
маркеровПри объёмах 10 000+ точек становится критичным перенос части логики на сервер:
На клиенте остаётся только визуализация уже агрегированных данных.
Ключевой механизм работы кластеризации связан с событиями карты:
zoom_changedbounds_changedКаждое изменение пересчитывает распределение маркеров по сетке кластеров. MarkerClustererPlus автоматически подписывается на эти события и пересобирает структуру без ручного вмешательства.
Несмотря на удобство, библиотека имеет ряд ограничений:
В современных проектах иногда используется альтернативный
@googlemaps/markerclusterer, однако MarkerClustererPlus
остаётся распространённым в легаси-системах.
Кластеры могут включать нестандартные маркеры:
const marker = new google.maps.Marker({
position,
icon: {
url: "custom-pin.svg",
scaledSize: new google.maps.Size(30, 30),
},
});
Clusterer работает независимо от типа маркера, пока он наследует
google.maps.Marker.
При сложных интерфейсах важно корректно управлять состоянием:
function destroyCluster() {
clusterer.clearMarkers();
clusterer.setMap(null);
}
В реальных приложениях кластеризация часто комбинируется с:
В таких случаях MarkerClustererPlus выступает только как визуальный слой агрегации поверх потоковых данных.