Display clusters
Estimated reading time: 2 minutesUse MapLibre GL JS built-in functions to visualize points as clusters.
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<link href="https://unpkg.com/[email protected]/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://unpkg.com/[email protected]/dist/maplibre-gl.js"></script>
<style>
html,
body {
margin: 0;
padding: 0;
height: 100%;
}
#map {
min-height: 500px;
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
// Don't forget to replace <YOUR_ACCESS_TOKEN> by your own access token
const accessToken = "<YOUR_ACCESS_TOKEN>";
const map = new maplibregl.Map({
container: "map",
style: `https://api.jawg.io/styles/jawg-lagoon.json?access-token=${accessToken}`,
zoom: 2,
center: [2.3210938, 48.7965913],
hash: true,
}).addControl(new maplibregl.NavigationControl(), "top-right");
// This plugin is used for right to left languages
maplibregl.setRTLTextPlugin("https://unpkg.com/@mapbox/[email protected]/mapbox-gl-rtl-text.min.js");
map.on("load", async () => {
// Add the tree cluster image
const clusterImage = await map.loadImage(
"https://jawg.github.io/maplibre-gl-js-examples/icons/cluster/tree-cluster.png",
);
map.addImage("tree-cluster", clusterImage.data);
// Add the simple tree image
const treeImage = await map.loadImage("https://jawg.github.io/maplibre-gl-js-examples/icons/cluster/tree.png");
map.addImage("tree", treeImage.data);
// Add a new source from our GeoJSON data and set the "cluster" option to true.
// MapLibre will add the "point_count" property to your source data
map.addSource("trees", {
type: "geojson",
data: "https://media.jawg.io/trees.geojson",
cluster: true,
clusterRadius: 50, // Radius of each cluster when clustering points (defaults to 50)
});
// Display the tree type as a separate symbol layer.
// This layer is optional, to disable you can remove it completely
map.addLayer({
id: "tree-label",
type: "symbol",
source: "trees",
filter: ["!", ["has", "point_count"]],
layout: {
"text-field": ["get", "libellefrancais"],
"text-padding": 0,
"text-allow-overlap": false,
"text-size": 11,
"text-font": ["Roboto Regular", "Noto Regular"],
"text-offset": [0, 1.75],
"text-anchor": "top",
},
paint: {
"text-color": "#5C5C5C",
"text-halo-color": "#FFFFFF",
"text-halo-width": 1,
},
});
// Add a tree symbol as a symbol layer with icon.
map.addLayer({
id: "tree",
type: "symbol",
source: "trees",
layout: {
// If the feature has the property "point_count" it means it's a cluster then we use the image "tree-cluster"
// Otherwise we use the simple "tree" image.
"icon-image": ["case", ["has", "point_count"], "tree-cluster", "tree"],
// Display the cluster point count if >= 2
"text-field": [
"step",
["get", "point_count"],
"",
// If the point_count is < 99 then display as "99+"
2,
["step", ["get", "point_count"], ["get", "point_count"], 99, "99+"],
],
"icon-padding": 0,
"text-padding": 0,
"text-overlap": "always",
"icon-overlap": "always",
"text-size": 12,
"text-font": ["Roboto Bold", "Noto Bold"],
"text-anchor": "center",
},
paint: {
"text-color": "#5C5C5C",
// Translate the text to fit in the center of the top right area
// Depending on your image, you might tune this value
"text-translate": [13, -14],
"text-translate-anchor": "viewport",
},
});
// On click on a cluster, zoom to the expansion zoom level
map.on("click", "tree", async (e) => {
const feature = e.features[0];
var clusterId = feature.properties.cluster_id;
if (!clusterId) return;
const zoom = await map.getSource("trees").getClusterExpansionZoom(clusterId);
map.easeTo({
center: feature.geometry.coordinates,
zoom: zoom + 0.5,
});
});
map.on("mouseenter", "tree", () => {
map.getCanvas().style.cursor = "pointer";
});
map.on("mouseleave", "tree", () => {
map.getCanvas().style.cursor = "";
});
});
</script>
</body>
</html>