Кластеризация | MapGL | 2GIS Documentation

Marker clustering

If you have a large number of markers concentrated in one area, you can use marker clustering to combine them into a single object when the map is zoomed out to reduce the visual clutter.

To use marker clustering, you need to include the clustering plugin in your project. To do this, add the following line after the main MapGL script:

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

Or you can install the needed package using npm:

npm install @2gis/mapgl-clusterer

To create a cluster, first you need to initialize the Clusterer object:

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

Or, if you are using npm:

// Import either as an ES module...
import { Clusterer } from '@2gis/mapgl-clusterer';
// ...or as a CommonJS module
const { Clusterer } = require('@2gis/mapgl-clusterer');

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

The radius parameter is a distance in pixels. If the distance between markers is less than this value, the markers will be merged into a single object.

Once the cluster is initialized, the next step is to add markers to it. You do not need to create marker objects explicitly. Instead, you need to call the load() method and pass an array of marker options (InputMarker). For example, to create a cluster of three markers, you can use code similar to this:

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

Try zooming in and out the map to see the clustering in action.

<!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>

To change the set of markers in the cluster, you can call load() again and pass a new array of marker options.

If the cluster is no longer needed, you can delete it by calling the destroy() method:

clusterer.destroy();

To add an event handler to a cluster, use the on() method. See ClustererEventTable for the list of possible events.

For example, to receive a click event, use the following code:

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

The handler will receive a click on the cluster itself as well as a click on an individual marker. To distinguish one from the other, use the target property of the event argument (ClustererPointerEvent). If the user clicked on the cluster itself, target.type will contain the word "cluster", and target.data will contain the information about the markers inside the cluster (an array of InputMarker). If the user clicked on an individual marker, target.type will contain the word "marker", and target.data will contain the information about that marker (InputMarker).

clusterer.on('click', (event) => {
    alert(`${event.target.type} is clicked`);
});

Try clicking on an individual marker and on the cluster itself in the example below.

<!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>

Cluster markers can be customized just like regular markers. To customize a marker, specify the required options when you call the load() method (see InputMarker for more details):

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>

You can change the appearance of a cluster by specifying the clusterStyle parameter. You can set the icon, text size, text color, and other options. See ClusterStyle for more information.

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>

You can also set different styles depending on the number of markers in a cluster. For that, specify a function as the value for clusterStyle. That function can use its first argument (the number of markers) to return different styles.

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

Try zooming in and out to see the cluster style changes.

<!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>

Additionally, you can use individual marker data to apply a different cluster style. To do that, use the second argument of the function (see ClusterTarget).

For example, you can set a different cluster style if all markers inside the cluster have the same coordinates:

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

    // Determine if markers inside the cluster have the same 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 markers have the same coordinates, change the icon to red
    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>

You can add custom data to markers using the userData field. That data can be then used, for example, in event handlers.

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: '/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>

You can use custom HTML elements as markers and clusters. To do so, set the type parameter to "html", and specify the HTML element or the HTML markup as the html parameter.

Keep in mind that HTML markers are slower than the default (WebGL) markers. To avoid performance issues, try not to display more than 100 HTML markers on the screen at the same time. You can reduce the number of displayed markers, for example, by increasing the radius parameter of the cluster.

You can use both types of markers in the same cluster:

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);

To change the marker type of the cluster itself, use 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: 'bfd8bbca-8abf-11ea-b033-5fa57aae2de7',
        });
        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>