Create and style clusters
This example uses Mapbox GL JS' built-in cluster
functions to visualize points in a circle
layer as clusters.
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Create and style clusters</title><meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"><link href="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.css" rel="stylesheet"><script src="https://api.mapbox.com/mapbox-gl-js/v2.14.1/mapbox-gl.js"></script><style>body { margin: 0; padding: 0; }#map { position: absolute; top: 0; bottom: 0; width: 100%; }</style></head><body><div id="map"></div> <script> // TO MAKE THE MAP APPEAR YOU MUST // ADD YOUR ACCESS TOKEN FROM // https://account.mapbox.com mapboxgl.accessToken = 'YOUR_MAPBOX_ACCESS_TOKEN';const map = new mapboxgl.Map({container: 'map',// Choose from Mapbox's core styles, or make your own style with Mapbox Studiostyle: 'mapbox://styles/mapbox/dark-v11',center: [-103.5917, 40.6699],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.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson',cluster: true,clusterMaxZoom: 14, // Max zoom to cluster points onclusterRadius: 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.mapbox.com/style-spec/reference/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': ['get', 'point_count_abbreviated'],'text-font': ['DIN Offc Pro Medium', 'Arial Unicode MS Bold'],'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 clickmap.on('click', 'clusters', (e) => {const features = map.queryRenderedFeatures(e.point, {layers: ['clusters']});const clusterId = features[0].properties.cluster_id;map.getSource('earthquakes').getClusterExpansionZoom(clusterId,(err, zoom) => {if (err) return; map.easeTo({center: features[0].geometry.coordinates,zoom: 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;const tsunami =e.features[0].properties.tsunami === 1 ? 'yes' : '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 mapboxgl.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>