Clustering | 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@^1/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@^1/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@^1/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],
    },
    ...
];

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@^1/dist/clustering.js"></script>
        <div id="container"></div>
        <script>
            const map = new mapgl.Map('container', {
                center: [55.31395, 25.22171],
                zoom: 12,
                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],
                },
            ];

            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@^1/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@^1/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>
<!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;
            }
        </style>
    </head>
    <body>
        <script src="https://mapgl.2gis.com/api/js/v1"></script>
        <script src="https://unpkg.com/@2gis/mapgl-clusterer@^1/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],
                    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],
                },
            ];

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

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