Настройка кластеров

При отображении большого количества маркеров на карте возникает несколько проблем:

  • снижается производительность браузера;
  • карта становится перегруженной визуально;
  • маркеры накладываются друг на друга;
  • пользователю сложно определить плотность объектов в определённой области.

Кластеризация решает эти проблемы путём объединения близко расположенных маркеров в единый объект — кластер. Вместо десятков или сотен отдельных точек отображается один элемент с числом, указывающим количество объектов внутри группы.

При изменении масштаба карты кластеры автоматически пересчитываются. Чем больше масштаб, тем сильнее происходит детализация, а отдельные маркеры постепенно становятся видимыми.


Библиотека MarkerClusterer

Для работы с кластерами в Google Maps JavaScript API используется отдельная библиотека:

<script
  src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js">
</script>

Современная реализация основана на пакете:

@googlemaps/markerclusterer

Основной класс:

MarkerClusterer

Он принимает карту и массив маркеров, после чего автоматически группирует их по алгоритму кластеризации.


Подключение Google Maps API

Базовое подключение карты:

<!DOCTYPE html>
<html>
<head>
  <title>Marker Clustering</title>
  <style>
    #map {
      width: 100%;
      height: 600px;
    }
  </style>
</head>
<body>

<div id="map"></div>

<script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY"></script>

<script src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js"></script>

<script src="app.js"></script>

</body>
</html>

Создание карты

Начальная настройка карты:

const map = new google.maps.Map(
  document.getElementById("map"),
  {
    center: {
      lat: 55.7558,
      lng: 37.6176
    },
    zoom: 5
  }
);

После создания карты можно приступать к добавлению маркеров.


Формирование массива маркеров

Кластеризатор работает не с координатами, а с объектами Marker.

Пример массива данных:

const locations = [
  { lat: 55.7558, lng: 37.6176 },
  { lat: 59.9343, lng: 30.3351 },
  { lat: 56.8389, lng: 60.6057 },
  { lat: 54.7104, lng: 20.4522 }
];

Создание маркеров:

const markers = locations.map(position => {
  return new google.maps.Marker({
    position
  });
});

Обратите внимание: маркеры пока не привязаны к карте через свойство map. Их отображением будет управлять кластеризатор.


Создание простого кластера

Минимальная конфигурация:

new markerClusterer.MarkerClusterer({
  map,
  markers
});

Или:

const markerCluster = new markerClusterer.MarkerClusterer({
  map: map,
  markers: markers
});

После выполнения кода маркеры автоматически группируются.


Полный пример кластеризации

const map = new google.maps.Map(
  document.getElementById("map"),
  {
    center: {
      lat: 55.7558,
      lng: 37.6176
    },
    zoom: 4
  }
);

const locations = [
  { lat: 55.7558, lng: 37.6176 },
  { lat: 55.7512, lng: 37.6184 },
  { lat: 55.7544, lng: 37.6200 },
  { lat: 59.9343, lng: 30.3351 },
  { lat: 59.9370, lng: 30.3300 },
  { lat: 59.9300, lng: 30.3400 }
];

const markers = locations.map(location => {
  return new google.maps.Marker({
    position: location
  });
});

const clusterer =
  new markerClusterer.MarkerClusterer({
    map,
    markers
  });

Результат:

  • близкие маркеры объединяются;
  • внутри кластера отображается количество точек;
  • при увеличении масштаба кластеры распадаются на отдельные маркеры.

Динамическое создание большого количества маркеров

Обычно данные поступают из базы данных или API.

Генерация случайных координат:

const markers = [];

for (let i = 0; i < 1000; i++) {

  const lat =
    55 + Math.random() * 10;

  const lng =
    35 + Math.random() * 15;

  markers.push(
    new google.maps.Marker({
      position: { lat, lng }
    })
  );
}

Создание кластера:

new markerClusterer.MarkerClusterer({
  map,
  markers
});

Даже при нескольких тысячах объектов карта остаётся удобной для взаимодействия.


Настройка алгоритма кластеризации

Библиотека позволяет выбирать алгоритмы формирования групп.

Импорт алгоритма:

const algorithm =
  new markerClusterer.SuperClusterAlgorithm();

Использование:

new markerClusterer.MarkerClusterer({
  map,
  markers,
  algorithm
});

Алгоритм SuperClusterAlgorithm основан на пространственном индексировании и обеспечивает высокую производительность при работе с десятками тысяч маркеров.


Настройка радиуса кластеризации

Радиус определяет, насколько близко должны находиться маркеры для объединения.

Пример:

const algorithm =
  new markerClusterer.SuperClusterAlgorithm({
    radius: 80
  });

Подключение:

new markerClusterer.MarkerClusterer({
  map,
  markers,
  algorithm
});

Типичные значения:

Радиус Поведение
40 Мелкие кластеры
60 Стандартное объединение
80 Крупные группы
100+ Агрессивная кластеризация

Маленький радиус увеличивает детализацию, большой — уменьшает количество отображаемых объектов.


Ограничение уровня масштабирования

Иногда необходимо отключить кластеризацию после достижения определённого масштаба.

Пример:

const algorithm =
  new markerClusterer.SuperClusterAlgorithm({
    maxZoom: 15
  });

Использование:

new markerClusterer.MarkerClusterer({
  map,
  markers,
  algorithm
});

Поведение:

  • до масштаба 15 работают кластеры;
  • после масштаба 15 отображаются отдельные маркеры.

Добавление маркеров после создания кластера

Создание кластеризатора:

const clusterer =
  new markerClusterer.MarkerClusterer({
    map,
    markers: []
  });

Создание нового маркера:

const marker =
  new google.maps.Marker({
    position: {
      lat: 55.75,
      lng: 37.61
    }
  });

Добавление:

clusterer.addMarker(marker);

Добавление нескольких объектов:

clusterer.addMarkers([
  marker1,
  marker2,
  marker3
]);

Кластеры будут автоматически пересчитаны.


Удаление маркеров

Удаление одного объекта:

clusterer.removeMarker(marker);

Удаление нескольких:

clusterer.removeMarkers([
  marker1,
  marker2
]);

Полная очистка:

clusterer.clearMarkers();

После удаления структура кластеров пересчитывается автоматически.


Получение списка маркеров

Иногда требуется доступ к текущему набору объектов.

const markers =
  clusterer.getMarkers();

console.log(markers);

Это может использоваться для анализа данных или построения статистики.


Кастомизация внешнего вида кластеров

По умолчанию используется стандартный стиль Google Maps.

Для создания собственного оформления применяется объект renderer.

Пример:

const renderer = {
  render({ count, position }) {

    return new google.maps.Marker({
      position,
      label: {
        text: String(count),
        color: "#ffffff",
        fontSize: "14px"
      },
      icon: {
        path: google.maps.SymbolPath.CIRCLE,
        fillColor: "#1976D2",
        fillOpacity: 1,
        strokeColor: "#ffffff",
        strokeWeight: 2,
        scale: 25
      }
    });
  }
};

Подключение:

new markerClusterer.MarkerClusterer({
  map,
  markers,
  renderer
});

В результате кластеры получают собственный дизайн.


Изменение цвета в зависимости от количества объектов

Количество маркеров внутри кластера можно использовать для выбора цвета.

const renderer = {
  render({ count, position }) {

    let color = "#4CAF50";

    if (count > 50) {
      color = "#FF9800";
    }

    if (count > 100) {
      color = "#F44336";
    }

    return new google.maps.Marker({
      position,
      label: {
        text: String(count),
        color: "#fff"
      },
      icon: {
        path: google.maps.SymbolPath.CIRCLE,
        fillColor: color,
        fillOpacity: 1,
        strokeWeight: 2,
        scale: 28
      }
    });
  }
};

Такой подход позволяет визуально оценивать плотность объектов на карте.


Обработка клика по кластеру

Событие клика помогает реализовать собственную логику взаимодействия.

Пример:

const clusterer =
  new markerClusterer.MarkerClusterer({
    map,
    markers,
  });

Обработчик:

google.maps.event.addListener(
  map,
  "click",
  () => {
    console.log("Клик по карте");
  }
);

Для кластеров чаще применяется кастомный рендерер и собственные обработчики внутри создаваемых маркеров.

Например:

const clusterMarker =
  new google.maps.Marker({
    position
  });

clusterMarker.addListener(
  "click",
  () => {
    console.log(count);
  }
);

Автоматическое масштабирование при выборе кластера

Стандартное поведение большинства кластеризаторов заключается в увеличении масштаба карты после выбора группы объектов.

При клике происходит:

  1. вычисление границ кластера;
  2. увеличение масштаба;
  3. повторный расчёт кластеров;
  4. отображение более детализированной структуры.

Такой механизм обеспечивает удобную навигацию по большим наборам данных.


Работа с десятками тысяч маркеров

Для крупных проектов рекомендуется:

  • использовать SuperClusterAlgorithm;
  • загружать данные порциями;
  • минимизировать количество обработчиков событий;
  • использовать лёгкие SVG-иконки;
  • избегать сложного DOM внутри маркеров;
  • обновлять данные только при необходимости.

Пример конфигурации:

const clusterer =
  new markerClusterer.MarkerClusterer({
    map,
    markers,
    algorithm:
      new markerClusterer.SuperClusterAlgorithm({
        radius: 60,
        maxZoom: 16
      })
  });

Такая схема позволяет эффективно отображать десятки и даже сотни тысяч географических объектов.


Типичные ошибки при настройке кластеров

Подключена только Google Maps API

Ошибка:

MarkerClusterer is not defined

Причина:

<script src="https://maps.googleapis.com/maps/api/js"></script>

Отсутствует библиотека кластеризации.

Необходимо подключить:

<script
src="https://unpkg.com/@googlemaps/markerclusterer/dist/index.min.js">
</script>

Маркеры одновременно добавлены на карту

Нежелательный вариант:

new google.maps.Marker({
  position,
  map
});

и одновременно:

new MarkerClusterer({
  map,
  markers
});

В таком случае объект отображается дважды.

Правильнее:

new google.maps.Marker({
  position
});

Управление отображением передаётся кластеризатору.


Слишком большой радиус

Пример:

radius: 200

Последствия:

  • огромные кластеры;
  • потеря детализации;
  • неудобная навигация.

Обычно достаточно диапазона:

40–80

Частое пересоздание кластеризатора

Нежелательно:

setInterval(() => {

  new MarkerClusterer({
    map,
    markers
  });

}, 1000);

Это приводит к росту потребления памяти и ухудшению производительности.

Предпочтительно обновлять существующий экземпляр через методы:

addMarker()
removeMarker()
clearMarkers()

Практические сценарии использования

Кластеризация активно применяется в системах, где требуется отображать большое количество географических объектов:

  • каталоги недвижимости;
  • транспортные сервисы;
  • системы мониторинга транспорта;
  • службы доставки;
  • карты магазинов и филиалов;
  • туристические сервисы;
  • геоинформационные системы;
  • карты событий;
  • логистические платформы;
  • аналитические панели с геоданными.

Во всех этих случаях использование кластеров позволяет сохранить высокую производительность интерфейса и обеспечить удобную работу с большими массивами пространственных данных.