Группировка маркеров

Группировка маркеров в картографических приложениях решает проблему визуального перегруза при отображении большого количества точек на ограниченном пространстве карты. В контексте HERE Technologies и HERE Maps API этот механизм реализуется через кластеризацию объектов, позволяющую объединять близко расположенные маркеры в единые агрегированные элементы на разных уровнях масштабирования.


Кластеризация в HERE Maps API строится на уровне слоя данных. Вместо добавления отдельных объектов на карту используется промежуточный слой, который анализирует пространственное распределение точек и динамически формирует группы.

Ключевые компоненты:

  • H.clustering.Provider — поставщик кластеризованных данных
  • H.clustering.DataPoint — базовая точка для кластеризации
  • H.map.layer.ObjectLayer — слой отображения объектов
  • H.map.Marker — визуальный маркер (кластер или одиночная точка)

Основная идея заключается в том, что карта не управляет маркерами напрямую, а получает уже агрегированные данные от провайдера кластеров.


Подключение и базовая инициализация

Перед использованием кластеризации необходимо создать карту и подключить модуль кластеризации.

const platform = new H.service.Platform({
  apikey: 'YOUR_API_KEY'
});

const defaultLayers = platform.createDefaultLayers();

const map = new H.Map(
  document.getElementById('map'),
  defaultLayers.vector.normal.map,
  {
    center: { lat: 52.5, lng: 13.4 },
    zoom: 6,
    pixelRatio: window.devicePixelRatio || 1
  }
);

const beh * avior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
const ui = H.ui.UI.createDefault(map, defaultLayers);

После создания карты подключается провайдер кластеров:

const dataPoints = [];

for (let i = 0; i < 1000; i++) {
  dataPoints.push(
    new H.clustering.DataPoint(
      Math.random() * 180 - 90,
      Math.random() * 360 - 180
    )
  );
}

Создание ClusterProvider

Центральный объект кластеризации — H.clustering.Provider.

const clusteredDataProvider = new H.clustering.Provider(dataPoints, {
  clusteringOptions: {
    eps: 32, // радиус кластеризации в пикселях
    minWeight: 2
  }
});

Параметры кластеризации

  • eps — радиус влияния точки в пикселях. Чем больше значение, тем крупнее кластеры
  • minWeight — минимальное количество точек для формирования кластера

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


Визуализация кластеров

Для отображения используется слой объектов:

const clusteringLayer = new H.map.layer.ObjectLayer(clusteredDataProvider);

map.addLayer(clusteringLayer);

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


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

Один из ключевых механизмов — настройка theme, определяющего визуальное представление кластеров.

const clusteredDataProvider = new H.clustering.Provider(dataPoints, {
  clusteringOptions: {
    eps: 64,
    minWeight: 2
  },
  theme: {
    getClusterPresentation: function (cluster) {
      const weight = cluster.getWeight();

      const svg = `
        <svg width="40" height="40">
          <circle cx="20" cy="20" r="18" fill="#1976d2" />
          <text x="20" y="25" text-anchor="middle" font-size="12" fill="#fff">
            ${weight}
          </text>
        </svg>
      `;

      const icon = new H.map.Icon(svg, {
        size: { w: 40, h: 40 }
      });

      const marker = new H.map.Marker(cluster.getPosition(), { icon });

      marker.setData(cluster);

      return marker;
    },

    getNoisePresentation: function (noisePoint) {
      const icon = new H.map.Icon('data:image/svg+xml;utf-8,<svg></svg>', {
        size: { w: 10, h: 10 }
      });

      return new H.map.Marker(noisePoint.getPosition(), { icon });
    }
  }
});

Разделение кластеров и одиночных точек

В кластерной модели существуют два типа сущностей:

Кластер

  • содержит несколько точек
  • имеет вес (getWeight())
  • может раскрываться при клике или зуме

Noise point (шумовая точка)

  • одиночный маркер
  • не входит в кластер
  • отображается отдельно

Обработка событий кластера

Кластеры поддерживают события, аналогично обычным маркерам.

map.addEventListener('tap', function (evt) {
  const target = evt.target;

  if (target instanceof H.map.Marker) {
    const data = target.getData();

    if (data.getWeight) {
      map.setCenter(data.getPosition());
      map.setZoom(map.getZoom() + 2);
    }
  }
});

Поведение обычно строится вокруг двух сценариев:

  • увеличение масштаба при клике на кластер
  • раскрытие отдельных точек при достижении высокого зума

Адаптация кластеров к масштабу карты

Кластеризация динамически пересчитывается при изменении zoom-level.

Основные принципы:

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

Это поведение управляется параметром eps, который фактически работает в пикселях экрана, а не в географических координатах.


Оптимизация производительности

При работе с большими наборами данных (10 000+ точек) важно учитывать:

1. Предварительная фильтрация данных

Удаление нерелевантных точек до передачи в провайдер снижает нагрузку.

2. Ограничение детализации кластеров

Слишком маленький eps приводит к росту числа объектов.

3. Использование ленивой загрузки

Данные могут подгружаться по географическим тайлам.


Поведение при больших наборах данных

При масштабировании до десятков тысяч точек кластеризация становится обязательной. Без неё карта теряет интерактивность.

Типичное поведение:

  • на уровне страны отображаются десятки кластеров
  • на уровне города — сотни
  • на уровне улиц — отдельные точки

Интеграция с пользовательскими данными

Кластеризация не ограничена географическими координатами. Каждый DataPoint может содержать произвольные метаданные.

new H.clustering.DataPoint(lat, lng, null, {
  id: 123,
  type: 'restaurant',
  rating: 4.8
});

Эти данные доступны при обработке событий:

const data = marker.getData();
const original = data.getData();

Расширенные сценарии кастомизации

Динамическая стилизация по типу данных

Кластеры могут менять внешний вид в зависимости от содержимого:

  • цвет по категории
  • размер по количеству объектов
  • иконка по типу сущностей

Иерархическая кластеризация

Для сложных систем применяется многоуровневая агрегация:

  • региональный уровень
  • городской уровень
  • локальный уровень

Взаимодействие с картой

Кластеры интегрируются с базовыми возможностями карты:

  • pan (перемещение)
  • zoom (масштабирование)
  • inertia scrolling
  • ограничение области отображения

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


Управление жизненным циклом данных

Обновление кластеров происходит через изменение источника данных:

clusteredDataProvider.setDataPoints(newDataPoints);

После этого карта автоматически пересчитывает группы и обновляет отображение без ручного вмешательства.


Особенности поведения при резких изменениях масштаба

При быстром изменении zoom:

  • кластер пересчитывается асинхронно
  • промежуточные состояния могут кратковременно отображать устаревшие группы
  • используется внутренняя буферизация отрисовки

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

Типичный pipeline кластеризации включает:

  1. загрузку геоданных
  2. преобразование в DataPoint
  3. создание Provider
  4. настройку theme
  5. добавление слоя на карту
  6. обработку взаимодействия пользователя

Эта модель обеспечивает стабильную работу даже при высокой плотности объектов и сложной визуализации.