Step-2: Point Clustering

Step-2: Point Clustering

Introduction

This step-2 shows you how to make point clustering to display a number of markers on Viamap.

Getting Started

In order to show data on the Viamap we need to add a source. Sources objects do supply this data to be shown on the map.

Adding a source

Following method adds a source to the Map:

addSource(id, source)

NameDescription
id(string)The ID of the source to add. Must not conflict with existing sources.
source(Object)The source object.

The type of source is specified by the “type” property, and must be one of vector, raster, raster-dem, geojson, image, video. For example the following code adds a source to the map with the ID busstops:

map.addSource("busstops", { 
    type: "geojson", 
    data: "https://vektordemo.viamap.net/starterkit/data/busstops.json", 
}); 

Enabling Point Clustering

When a source is added to the map, setting the cluster option to true enables the point clustering as shown below:

map.addSource("busstops", {
    type: "geojson",
    data: "https://vektordemo.viamap.net/starterkit/data/busstops.json",
    cluster: true,
    clusterMaxZoom: 18,
    clusterRadius: 50
});

If the data is a collection of point features, setting the cluster option to true clusters the points by radius into groups.

Adding Layers for Circle, Cluster Count and Unclustered Points

Adding a source won’t immediately make data appear on the map because sources don’t contain styling details like color or width. Layers refer to a source and give it a visual representation. The following method adds a layer:

addLayer (layer, beforeId?)

A layer defines styling for data from a specified source with the following parameters:

NameDescription
layer ( Object ) The style layer to add.
beforeId(string?) The ID of an existing layer to insert the new layer before. If this argument is omitted, the layer will be appended to the end of the layers array.

The type of layer is specified by the "type" property, and must be one of background, fill, line, symbol, raster, circle, fill-extrusion, heatmap, hillshade.
Except for layers of the background type, each layer needs to refer to a source. Layers take the data that they get from a source, optionally filter features, and then define how those features are styled.

Below is the code that adds circle, cluster count and unclustered layers on Viamap:

map.addLayer({
    id: "clusters",
    type: "circle",
    source: "busstops",
    filter: ["has", "point_count"],
    paint: {
        "circle-color": [
            "step",
            ["get", "point_count"],
            "#51bbd6",
            200,
            "#f1f075",
            600,
            "#f28cb1"
        ],
        "circle-radius": [
            "step",
            ["get", "point_count"],
            16,
            100,
            24,
            750,
            32
        ]
    }
});
map.addLayer({
    id: "cluster-count",
    type: "symbol",
    source: "busstops",
    filter: ["has", "point_count"],
    layout: {
        "text-field": "{point_count_abbreviated}",
        "text-font": ["Droid Sans Regular"],
        "text-size": 12
    }
});

map.addLayer({
    id: "unclustered-point",
    type: "circle",
    source: "busstops",
    filter: ["!", ["has", "point_count"]],
    paint: {
        "circle-color": "#11b4da",
        "circle-radius": 8,
        "circle-stroke-width": 1,
        "circle-stroke-color": "#fff"
    }
});

The number on a cluster indicates how many markers it contains. Notice that as you zoom into any of the cluster locations the number on the cluster decreases and you begin to see the individual markers on the map. Zooming out of the map consolidates the markers into clusters again. 

Final Code:

Here is the final code that shows you how to make point clustering to display a number of markers on Viamap

A working example can be seen here.

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
        <title>Viamap vektordemo</title>

        <!-- add viamap bootstrap code -->
        <script src="viamapstrap.js"></script>
    </head>

    <body style="margin:0; padding:0;">
        <nav id="menu"></nav>
        <div id='map' style="position:absolute; top:0; bottom:0; width:100%;"></div>
        <script>
            // the position for the marker
            var lnglat = [9.912028, 57.043528],
                zoom = 15.6;

            vms.initmap({
                    container: 'map',
                    hash: false,
                    // 'center': lnglat,
                    // 'zoom': zoom
                })
                .then(function(map) {
                    // add some controls
                    map.addControl(new mapboxgl.NavigationControl(), 'top-left');
                    debugger;


                    // the following is a slight rewrite of this: https://www.mapbox.com/mapbox-gl-js/example/cluster/
                    map.addSource("busstops", {
                        type: "geojson",
                        data: "https://vektordemo.viamap.net/starterkit/data/busstops.json",
                        cluster: true,
                        clusterMaxZoom: 18, // Max zoom to cluster points on
                        clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
                    });

                    map.addLayer({
                        id: "clusters",
                        type: "circle",
                        source: "busstops",
                        filter: ["has", "point_count"],
                        paint: {
                            "circle-color": [
                                "step",
                                ["get", "point_count"],
                                "#51bbd6",
                                200,
                                "#f1f075",
                                600,
                                "#f28cb1"
                            ],
                            "circle-radius": [
                                "step",
                                ["get", "point_count"],
                                16,
                                100,
                                24,
                                750,
                                32
                            ]
                        }
                    });



                    // inspect a cluster on click
                    map.on('click', 'clusters', function(e) {
                        var features = map.queryRenderedFeatures(e.point, {
                            layers: ['clusters']
                        });
                        var clusterId = features[0].properties.cluster_id;
                        map.getSource('busstops').getClusterExpansionZoom(clusterId, function(err, zoom) {
                            if (err)
                                return;

                            map.easeTo({
                                center: features[0].geometry.coordinates,
                                zoom: zoom
                            });
                        });
                    });

                    // Create a popup, but don't add it to the map yet.
                    var popup = new mapboxgl.Popup({
                        closeButton: false,
                        closeOnClick: false
                    });


                    map.on('mouseenter', 'unclustered-point', function(e) {
                        // Change the cursor style as a UI indicator.
                        map.getCanvas().style.cursor = 'pointer';

                        var coordinates = e.features[0].geometry.coordinates.slice();
                        var description = e.features[0].properties.name;

                        // Ensure that if the map is zoomed out such that multiple
                        // copies of the feature are visible, the popup appears
                        // over the copy being pointed to.
                        while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
                            coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
                        }

                        // Populate the popup and set its coordinates
                        // based on the feature found.
                        popup.setLngLat(coordinates)
                            .setHTML(description)
                            .addTo(map);
                    });

                    map.on('mouseleave', 'unclustered-point', function() {
                        map.getCanvas().style.cursor = '';
                        popup.remove();
                    });

                });
        </script>
    </body>

</html>

Do you want to try Viamap? Download “starter kit