Кластеризация маркеров | MapGL | 2GIS Documentation
MapGL JS API
Личный кабинет

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

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

Чтобы использовать кластеры, нужно подключить плагин 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);

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

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="Default markers in cluster example" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.323, 25.235],
                zoom: 10.5,
                key: 'Your API access key',
            });

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

            const clusterer = new mapgl.Clusterer(map);
            clusterer.load(markers);
        </script>
    </body>
</html>

Чтобы изменить набор маркеров в существующем кластере, нужно вызвать метод 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`);
});

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

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="Cluster event handling example" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.323, 25.235],
                zoom: 10.5,
                key: 'Your API access key',
            });

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

            const clusterer = new mapgl.Clusterer(map);
            clusterer.load(markers);
            clusterer.on('click', (event) => {
                alert(`${event.target.type} is clicked`);
            });
        </script>
    </body>
</html>

Маркерам в составе кластера можно задать настройки внешнего вида так же, как и обычным маркерам. Настройки нужно указать в виде объекта 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);
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="Custom markers in cluster example" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.31395, 25.22171],
                zoom: 11,
                key: 'Your API access key',
            });

            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],
                    label: {
                        relativeAnchor: [0, 0.5],
                        offset: [20, 0],
                        color: '#00f',
                        haloColor: '#ffffff',
                        haloRadius: 1,
                        fontSize: 12,
                        text: 'Dubai Airport'
                    }
                },
            ];

            const clusterer = new mapgl.Clusterer(map);
            clusterer.load(markers);
        </script>
    </body>
</html>

Чтобы изменить внешний вид кластера, нужно указать параметр 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,
    },
});
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="Cluster object style example" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.323, 25.235],
                zoom: 10.5,
                key: 'Your API access key',
            });

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

            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,
                },
            });
            clusterer.load(markers);
        </script>
    </body>
</html>

Также можно использовать разные стили в зависимости от количества маркеров в кластере. Для этого в качестве значения параметра 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,
});

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

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="Cluster function style example" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.323, 25.235],
                zoom: 10.5,
                key: 'Your API access key',
            });

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

            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,
            });
            clusterer.load(markers);
        </script>
    </body>
</html>

Кроме количества маркеров, для переключения стилей можно использовать свойства маркеров. Для этого можно использовать второй аргумент функции - массив объектов 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: '/img/mapgl/clusterHover.svg',
            hoverIcon: '/img/mapgl/clusterHover.svg',
            size: [25, 25],
            hoverSize: [35, 35],
            labelColor: '#ffffff',
        };
    }

    return {
        icon: '/img/mapgl/cluster.svg',
        hoverIcon: '/img/mapgl/cluster.svg',
        size: [25, 25],
        hoverSize: [35, 35],
        labelColor: '#ffffff',
    };
}

const clusterer = new mapgl.Clusterer(map, {
    clusterStyle,
});
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="Clusters customization with data example" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.323, 25.235],
                zoom: 10.5,
                key: 'Your API access key',
            });

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

            function clusterStyle(pointsCount, target) {
                const points = target.data;

                // determine that points have different coordinates
                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 all points have the same coordinates - make it red
                if (!divisible) {
                    return {
                        icon: 'https://docs.2gis.com/img/mapgl/clusterHover.svg',
                        hoverIcon: 'https://docs.2gis.com/img/mapgl/clusterHover.svg',
                        size: [25, 25],
                        hoverSize: [35, 35],
                        labelColor: '#ffffff',
                    };
                }

                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',
                };
            }

            const clusterer = new mapgl.Clusterer(map, {
                clusterStyle,
            });
            clusterer.load(markers);
        </script>
    </body>
</html>

Для маркеров можно указать произвольные данные при помощи поля 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: '/img/mapgl/cluster.svg',
        hoverIcon: '/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}`);
    }
});
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="UserData example" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.323, 25.235],
                zoom: 9.5,
                key: 'Your API access key',
            });

            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) {
                // Get data from markers in the cluster
                const points = target.data;

                // Add custom data to cluster
                target.userData = {
                    // Any data what you want
                    foo: { bar: 'baz' },

                    // Data from markers
                    dataFromPoints: points.map((p) => p.userData).join(', '),
                };

                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',
                };
            }

            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}`);
                }
            });
        </script>
    </body>
</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})`,
        };
    },
});
<?doctype html>
<html>
<head>
    <script src="https://mapgl.2gis.com/api/js/v1"></script>
    <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
    <style>
        html,
        body,
        #container {
            margin: 0;
            width: 500px;
            height: 300px;
            overflow: hidden;
        }

        .marker, .cluster {
            font-family: sans-serif;
            font-size: 10px;
            padding: 2px;
            border-radius: 2px;
            border: solid 1px black;
            cursor: pointer;
        }

        .marker {
            background: #00ffff;
        }

        .cluster {
            background: #ffff00;
        }
    </style>
</head>
<body>
    <div id="container"></div>
    <script>
        const map = new mapgl.Map('container', {
            center: [55.323, 25.235],
            zoom: 10.5,
            key: 'Your API access key',
        });
        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],
        }];


        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})`
                }
            }
        });
        clusterer.load(markers);
    </script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>2GIS Map API</title>
        <meta name="description" content="Full example for cluster" />
        <style>
            html,
            body,
            #container {
                margin: 0;
                width: 500px;
                height: 300px;
                overflow: hidden;
            }

            .marker,
            .cluster {
                font-family: sans-serif;
                font-size: 10px;
                padding: 2px;
                border-radius: 2px;
                border: solid 1px black;
                cursor: pointer;
            }

            .marker {
                background: #00ffff;
            }

            .cluster {
                background: #ffff00;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^2/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.323, 25.235],
                zoom: 10.5,
                key: 'Your API access key',
            });

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

            const markers = [
                {
                    type: 'html',
                    coordinates: [55.27887, 25.21001],
                    html: '<div class="marker">HTML marker №1</div>',
                },
                {
                    type: 'html',
                    coordinates: [55.30771, 25.20314],
                    html: htmlMarker,
                },
                {
                    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],
                    label: {
                        relativeAnchor: [0, 0.5],
                        offset: [20, 0],
                        color: '#00f',
                        haloColor: '#ffffff',
                        haloRadius: 1,
                        fontSize: 12,
                        text: 'Dubai Airport',
                    },
                },
            ];

            function clusterStyle(pointsCount) {
                if (pointsCount < 3) {
                    return {
                        type: 'html',
                        html: `<div class="cluster">HTML cluster (${pointsCount})</div>`,
                    };
                }

                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, {
                radius: 60,
                clusterStyle,
            });

            clusterer.load(markers);
            clusterer.on('click', (event) => {
                alert(`${event.target.type} is clicked`);
            });
        </script>
    </body>
</html>