Перейти к основному содержимому

Кластеризация маркеров

Если в какой-то области карты сосредоточено большое количество маркеров, они могут отвлекать внимание и закрывать собой другие объекты карты. Чтобы избавиться от визуального шума, можно объединять маркеры в один объект (кластер) при уменьшении масштаба карты.

Чтобы использовать кластеры, нужно подключить плагин Clusterer. Для этого нужно добавить следующую строку после подключения основного скрипта:

<script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>

Также можно установить плагин с помощью npm:

npm install @2gis/mapgl-clusterer

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

Чтобы создать кластер, сначала нужно инициализировать объект Clusterer:

const clusterer = new mapgl.Clusterer(map, {
radius: 60,
});

В случае использования npm:

// Импортируйте плагин как ES-модуль...
import { Clusterer } from '@2gis/mapgl-clusterer';
// ...или как модуль CommonJS
const { Clusterer } = require('@2gis/mapgl-clusterer');

const clusterer = new Clusterer(map, {
radius: 60,
});

Параметр radius определяет расстояние в пикселях. Если расстояние между маркерами меньше указанного значения, маркеры будут объединены в один объект.

Чтобы добавить маркеры в кластер, их не нужно создавать отдельно. Вместо этого нужно вызвать метод кластера load() и указать массив с настройками маркеров (нужное количество объектов InputMarker). Например, чтобы создать кластер из трёх маркеров, можно использовать следующий код:

const markers = [
{ coordinates: [55.27887, 25.21001] },
{ coordinates: [55.30771, 25.20314] },
{ coordinates: [55.35266, 25.24382] },
];
clusterer.load(markers);

На карте ниже показан пример кластера из трёх маркеров. Чем меньше масштаб карты, тем меньше отдельных маркеров будет видно на карте.

Чтобы изменить набор маркеров в существующем кластере, нужно вызвать метод load() повторно, указав новый массив с настройками маркеров.

Чтобы удалить кластер, нужно вызвать метод destroy(). При удалении кластера также удаляются все входящие в него маркеры.

clusterer.destroy();

События

Чтобы добавить обработчик событий для кластера, нужно вызвать метод on(). Полный список поддерживаемых событий можно найти в Справочнике API.

Например, подписаться на событие нажатия на кластер можно с помощью следующего кода:

clusterer.on('click', (event) => {
alert(`click`);
});

Функция-обработчик будет вызвана как при нажатии на кластер, так и при нажатии на отдельный маркер из этого кластера. Чтобы отличить нажатие на кластер от нажатия на маркер, можно использовать свойство target объекта события (ClustererPointerEvent):

  • Если пользователь нажал на кластер, target.type будет содержать строку "cluster", а в target.data будет указана информация о маркерах в этом кластере (массив объектов InputMarker).
  • Если пользователь нажал на отдельный маркер, target.type будет содержать строку "marker", а в target.data будет указана информация об этом маркере (объект InputMarker).
clusterer.on('click', (event) => {
alert(`${event.target.type} is clicked`);
});

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

Внешний вид маркера

Маркерам в составе кластера можно задать настройки внешнего вида так же, как и обычным маркерам. Настройки нужно указать в виде объекта InputMarker при вызове метода load().

const markers = [
{
coordinates: [55.27887, 25.21001],
icon: 'https://docs.2gis.com/img/mapgl/marker.svg',
hoverIcon: 'https://docs.2gis.com/img/mapgl/markerHover.svg',
size: [36, 36],
hoverSize: [46, 46],
},
{
coordinates: [55.30771, 25.20314],
icon: 'https://docs.2gis.com/img/mapgl/marker.svg',
hoverIcon: 'https://docs.2gis.com/img/mapgl/marker.svg',
size: [42, 42],
hoverSize: [48, 48],
},
{
coordinates: [55.35266, 25.24382],
icon: 'https://docs.2gis.com/img/mapgl/marker.svg',
hoverIcon: 'https://docs.2gis.com/img/mapgl/markerHover.svg',
size: [44, 44],
hoverSize: [50, 50],
},
];

clusterer.load(markers);

Внешний вид кластера

Чтобы изменить внешний вид кластера, нужно указать параметр clusterStyle. С помощью этого параметра можно задать иконку, цвет текста, размер текста и другие настройки (см. ClusterStyle).

const clusterer = new mapgl.Clusterer(map, {
clusterStyle: {
icon: 'https://docs.2gis.com/img/mapgl/cluster.svg',
hoverIcon: 'https://docs.2gis.com/img/mapgl/clusterHover.svg',
labelColor: '#ffffff',
labelFontSize: 16,
},
});

Также можно использовать разные стили в зависимости от количества маркеров в кластере. Для этого в качестве значения параметра clusterStyle нужно указать функцию, которая будет возвращать нужный стиль на основании первого аргумента (количества маркеров).

function clusterStyle(pointsCount) {
if (pointsCount < 3) {
return {
icon: 'https://docs.2gis.com/img/mapgl/cluster.svg',
hoverIcon: 'https://docs.2gis.com/img/mapgl/cluster.svg',
size: [25, 25],
hoverSize: [35, 35],
labelColor: '#ffffff',
labelFontSize: 12,
};
}
return {
icon: 'https://docs.2gis.com/img/mapgl/clusterHover.svg',
hoverIcon: 'https://docs.2gis.com/img/mapgl/clusterHover.svg',
size: [35, 35],
hoverSize: [45, 45],
labelColor: '#ffffff',
labelFontSize: 16,
};
}

const clusterer = new mapgl.Clusterer(map, {
clusterStyle,
});

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

Кроме количества маркеров, для переключения стилей можно использовать свойства маркеров. Для этого можно использовать второй аргумент функции - массив объектов ClusterTarget.

Например, можно изменить стиль, если у всех маркеров в кластере одинаковые координаты:

function clusterStyle(pointsCount, target) {
// Получаем информацию о маркерах
const points = target.data;

// Проверяем координаты маркеров
const divisible = points.some((point, index) => {
if (index > 0) {
const prevPoint = points[index - 1];
if (
point.coordinates[0] != prevPoint.coordinates[0] ||
point.coordinates[1] != prevPoint.coordinates[1]
) {
return true;
}
}
});

// Если у всех маркеров одинаковые координаты, меняем иконку кластера на красную
if (!divisible) {
return {
icon: 'https://docs-new.2gis.com/img/mapgl/clusterHover.svg',
hoverIcon: 'https://docs-new.2gis.com/img/mapgl/clusterHover.svg',
size: [25, 25],
hoverSize: [35, 35],
labelColor: '#ffffff',
};
}

return {
icon: 'https://docs-new.2gis.com/img/mapgl/cluster.svg',
hoverIcon: 'https://docs-new.2gis.com/img/mapgl/cluster.svg',
size: [25, 25],
hoverSize: [35, 35],
labelColor: '#ffffff',
};
}

const clusterer = new mapgl.Clusterer(map, {
clusterStyle,
});

Произвольные данные

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

const markers = [
{ coordinates: [55.27887, 25.21001], userData: 1 },
{ coordinates: [55.30771, 25.20314], userData: 2 },
{ coordinates: [55.35266, 25.24382], userData: 3 },
];

function clusterStyle(pointsCount, target) {
// Получаем информацию о маркерах
const points = target.data;

// Добавляем произвольные данные
target.userData = {
// Можно добавлять любые типы данных
foo: { bar: 'baz' },

// Собираем данные из маркеров в кластере
dataFromPoints: points.map((p) => p.userData).join(', '),
};

return {
icon: 'https://docs-new.2gis.com/img/mapgl/cluster.svg',
hoverIcon: 'https://docs-new.2gis.com/img/mapgl/cluster.svg',
size: [25, 25],
hoverSize: [35, 35],
labelColor: '#ffffff',
};
}

const clusterer = new mapgl.Clusterer(map, { clusterStyle });
clusterer.load(markers);

clusterer.on('click', (event) => {
if (event.target.type === 'cluster') {
alert(`Cluster user data: ${JSON.stringify(event.target.userData)}`);
} else {
alert(`Marker user data: ${event.target.data.userData}`);
}
});

HTML-маркеры

В качестве маркеров и кластеров можно использовать произвольные HTML-элементы. Для этого нужно указать параметр type со значением "html" и параметр html с нужной HTML-разметкой.

При этом следует иметь в виду, что HTML-маркеры работают медленнее, чем обычные WebGL-маркеры. Чтобы избежать проблем с производительностью, старайтесь не отображать на экране более 100 HTML-маркеров одновременно. Количество отображаемых маркеров можно уменьшить, если увеличить значение параметра radius у кластера.

Кластер может содержать оба типа маркеров:

const htmlMarker = document.createElement('div');
htmlMarker.classList.add('marker');
htmlMarker.innerText = 'HTML Marker 2';

const markers = [
{
type: 'html',
coordinates: [55.35266, 25.24382],
html: '<div class="marker">HTML Marker 1</div>',
},
{
type: 'html',
coordinates: [55.27887, 25.21001],
html: htmlMarker,
},
{
type: 'webgl',
coordinates: [55.55459, 25.156798],
},
{
type: 'webgl',
coordinates: [55.30771, 25.20314],
},
];

clusterer.load(markers);

Указать HTML-разметку для самого кластера можно при помощи параметра clusterStyle:

const clusterer = new mapgl.Clusterer(map, {
clusterStyle: (count) => {
if (count < 4) {
return {
type: 'html',
html: `<div class="cluster">HTML cluster (${count})</div>`,
};
}

return {
type: 'webgl',
labelText: `WebGL cluster (${count})`,
};
},
});

Полный пример