Skip to content

Create and style clusters

Use TrackAsia GL JS' built-in functions to visualize points as clusters.

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Create and style clusters</title>
        <meta property="og:description" content="Use TrackAsia GL JS' built-in functions to visualize points as clusters." />
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/trackasia-gl.css" />
        <script src="https://unpkg.com/[email protected]/dist/trackasia-gl.js"></script>
        <style>
            body {
                margin: 0;
                padding: 0;
            }
            html,
            body,
            #map {
                height: 100%;
            }
        </style>
    </head>
    <body>
        <div id="map"></div>

        <script>
            const map = new trackasiagl.Map({
                container: "map",
                style: "https://maps.track-asia.com/styles/v1/streets.json?key=public_key",
                center: [-103.59179687498357, 40.66995747013945],
                zoom: 3,
            });

            map.on("load", () => {
                // Add a new source from our GeoJSON data and
                // set the 'cluster' option to true. GL-JS will
                // add the point_count property to your source data.
                map.addSource("earthquakes", {
                    type: "geojson",
                    // Point to GeoJSON data. This example visualizes all M1.0+ earthquakes
                    // from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program.
                    data: "https://docs.track-asia.com/assets/earthquakes.geojson",
                    cluster: true,
                    clusterMaxZoom: 14, // 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: "earthquakes",
                    filter: ["has", "point_count"],
                    paint: {
                        // Use step expressions (https://docs.track-asia.com/style/#expressions-step)
                        // with three steps to implement three types of circles:
                        //   * Blue, 20px circles when point count is less than 100
                        //   * Yellow, 30px circles when point count is between 100 and 750
                        //   * Pink, 40px circles when point count is greater than or equal to 750
                        "circle-color": [
                            "step",
                            ["get", "point_count"],
                            "#51bbd6",
                            100,
                            "#f1f075",
                            750,
                            "#f28cb1",
                        ],
                        "circle-radius": [
                            "step",
                            ["get", "point_count"],
                            20,
                            100,
                            30,
                            750,
                            40,
                        ],
                    },
                });

                map.addLayer({
                    id: "cluster-count",
                    type: "symbol",
                    source: "earthquakes",
                    filter: ["has", "point_count"],
                    layout: {
                        "text-field": "{point_count_abbreviated}",
                        "text-font": [
                            "DIN Pro Regular",
                            "Arial Unicode MS Regular",
                        ],
                        "text-size": 12,
                    },
                });

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

                // inspect a cluster on click
                map.on("click", "clusters", async (e) => {
                    const features = map.queryRenderedFeatures(e.point, {
                        layers: ["clusters"],
                    });
                    const clusterId = features[0].properties.cluster_id;
                    const zoom = await map
                        .getSource("earthquakes")
                        .getClusterExpansionZoom(clusterId);
                    map.easeTo({
                        center: features[0].geometry.coordinates,
                        zoom,
                    });
                });

                // When a click event occurs on a feature in
                // the unclustered-point layer, open a popup at
                // the location of the feature, with
                // description HTML from its properties.
                map.on("click", "unclustered-point", (e) => {
                    const coordinates =
                        e.features[0].geometry.coordinates.slice();
                    const mag = e.features[0].properties.mag;
                    let tsunami;

                    if (e.features[0].properties.tsunami === 1) {
                        tsunami = "yes";
                    } else {
                        tsunami = "no";
                    }

                    // 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;
                    }

                    new trackasiagl.Popup()
                        .setLngLat(coordinates)
                        .setHTML(
                            `magnitude: ${mag}<br>Was there a tsunami?: ${tsunami}`
                        )
                        .addTo(map);
                });

                map.on("mouseenter", "clusters", () => {
                    map.getCanvas().style.cursor = "pointer";
                });
                map.on("mouseleave", "clusters", () => {
                    map.getCanvas().style.cursor = "";
                });
            });
        </script>
    </body>
</html>