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

Clustering

The marker clustering helps you to manage multiple markers at different zoom levels. When a user views the map at a high zoom level, the individual markers show on the map. When the user zooms out, the markers gather together into clusters, to make viewing the map easier. If you have a lot of markers on the map, it’s better to use a clustering to organize them better visually.

To add marker clustering functionality to the Map you need to include the clustering plugin JS file after MapGL API file using unpkg CDN:

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

Or using NPM:

npm install @2gis/mapgl-clusterer

First you need to instantiate the Clusterer.

If you install the clustering plugin using NPM, you can import the Clusterer to your project as shown below:

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

If you use unpkg CDN, you need to instantiate the Clusterer via the global object mapgl:

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

The first argument of the Clusterer constructor is an instance of the Map, the second - ClustererOptions.

The clusterer is ready to go. The next step will be markers loading. To add markers to the clusterer you need to use the load method. As an argument this method takes an array of InputMarker markers.

In the example below the markers will be added to the clusterer with default styles:

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

clusterer.load(markers);

The markers passed to the clusterer will display on the map:

<!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 reload the markers in the clusterer you need to call the load method again, but with a new array of markers. New markers will display on the map immediately:

const newMarkers = [
    { coordinates: [55.1234, 25.4321] },
    ...
];

clusterer.load(newMarkers);

If the clusterer is no longer needed you can destroy it by using the destroy method:

clusterer.destroy();

The clusterer can emit events described in the ClustererEventTable. You can add handlers for them.

For example, on click event:

clusterer.on('click', (event) => {
    // do something
});

As an argument the handler accepts a ClustererPointerEvent. A clusterer event target can be either a MarkerTarget or a ClusterTarget. Event target data respresent a InputMarker in case of marker event or an array of InputMarker markers contained in a cluster, on which this event has been emitted.

In the example below the modal dialog box appear with event target type value when marker or cluster is clicked:

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>

Your InputMarker can be customized by modifying icons (icon or hoverIcon) and icons position relative to the marker coordinates (anchor or hoverAnchor), also by resizing icons (size or hoverSize).

In the example below the markers have modified icons and their size for normal and hover states:

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],
    },
    {
        // The icon of this marker will grow in size on hover.
        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],
    },
    ...
];

Each marker can have a label which can be customized according to InputMarkerLabelOptions interface.

markers.push({
    coordinates: [55.35266, 25.24382],
    icon: '/img/mapgl/marker.svg',
    hoverIcon: '/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',
    },
});

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 also pass custom properties to markers. Such properties can be used in the clusterer events handling, for example.

A ClusterStyle is set in the ClustererOptions. In terms of structure, a cluster is a set of an icon and a text (label) indicating the number of markers in this cluster. For cluster icon customizing the same mechanism is used as in marker.

To modify label appearance you can set: labelColor - text color; labelFontSize - text font size; labelHaloRadius - letters background radius of the text; labelHaloColor - letters background color of the text; labelLetterSpacing - space between letters of the text; labelAnchor - text position binding to a point of the map.

To set the uniform style for all clusters you need to pass a ClusterStyle object to the options of the clusterer when its instantiation.

For example, the following is the style that has modified normal and hover icons, text color and text font size. This style applies for all clusters:

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>

Also you can set different styles for clusters based on the number of markers in them. For that you need to pass a function as a style, which returns a ClusterStyle object.

In the example below the function returns different styles for a cluster containing from 3 markers and less:

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>

By default, clusterer uses WebGL markers, but if it's more convenient, you can use HTML markers.

What kind of marker will be created is determined by type option.

You can specify content for HTML marker via simple string or HTMLElement:

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

Also you can create HTML clusters through clusterStyle function the same way:

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

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

Important notice: HTML markers are less performant than WebGL markers, so it's a good practice to keep not more than 100 HTML marker/cluster instances within your viewport. Usually, to reduce number of marker/cluster instances you should increase radius option value of the clusterer.

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