Skip to content

Commit 72141ba

Browse files
committed
feat(cluster.html): add example HTML file to demonstrate clustering with MapLibre GL JS for visualizing earthquake data
1 parent f8f46a3 commit 72141ba

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

website/src/examples/cluster.html

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>Create and style clusters</title>
5+
<meta property="og:description" content="Use MapLibre GL JS' built-in functions to visualize points as clusters." />
6+
<meta charset='utf-8'>
7+
<meta name="viewport" content="width=device-width, initial-scale=1">
8+
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.css' />
9+
<script src='https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.js'></script>
10+
<style>
11+
body { margin: 0; padding: 0; }
12+
html, body, #map { height: 100%; }
13+
</style>
14+
</head>
15+
<body>
16+
<div id="map"></div>
17+
18+
<script>
19+
const map = new maplibregl.Map({
20+
container: 'map',
21+
style: 'https://tiles.openfreemap.org/styles/positron',
22+
center: [-103.59179687498357, 40.66995747013945],
23+
zoom: 3
24+
});
25+
26+
map.on('load', () => {
27+
// Add a new source from our GeoJSON data and
28+
// set the 'cluster' option to true. GL-JS will
29+
// add the point_count property to your source data.
30+
map.addSource('earthquakes', {
31+
type: 'geojson',
32+
// Point to GeoJSON data. This example visualizes all M1.0+ earthquakes
33+
// from 12/22/15 to 1/21/16 as logged by USGS' Earthquake hazards program.
34+
data: 'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson',
35+
cluster: true,
36+
clusterMaxZoom: 14, // Max zoom to cluster points on
37+
clusterRadius: 50 // Radius of each cluster when clustering points (defaults to 50)
38+
});
39+
40+
map.addLayer({
41+
id: 'clusters',
42+
type: 'circle',
43+
source: 'earthquakes',
44+
filter: ['has', 'point_count'],
45+
paint: {
46+
// Use step expressions (https://maplibre.org/maplibre-style-spec/#expressions-step)
47+
// with three steps to implement three types of circles:
48+
// * Blue, 20px circles when point count is less than 100
49+
// * Yellow, 30px circles when point count is between 100 and 750
50+
// * Pink, 40px circles when point count is greater than or equal to 750
51+
'circle-color': [
52+
'step',
53+
['get', 'point_count'],
54+
'#51bbd6',
55+
100,
56+
'#f1f075',
57+
750,
58+
'#f28cb1'
59+
],
60+
'circle-radius': [
61+
'step',
62+
['get', 'point_count'],
63+
20,
64+
100,
65+
30,
66+
750,
67+
40
68+
]
69+
}
70+
});
71+
72+
map.addLayer({
73+
id: 'cluster-count',
74+
type: 'symbol',
75+
source: 'earthquakes',
76+
filter: ['has', 'point_count'],
77+
layout: {
78+
'text-field': '{point_count_abbreviated}',
79+
'text-font': ['Noto Sans Regular'],
80+
'text-size': 12
81+
}
82+
});
83+
84+
map.addLayer({
85+
id: 'unclustered-point',
86+
type: 'circle',
87+
source: 'earthquakes',
88+
filter: ['!', ['has', 'point_count']],
89+
paint: {
90+
'circle-color': '#11b4da',
91+
'circle-radius': 4,
92+
'circle-stroke-width': 1,
93+
'circle-stroke-color': '#fff'
94+
}
95+
});
96+
97+
// inspect a cluster on click
98+
map.on('click', 'clusters', async (e) => {
99+
const features = map.queryRenderedFeatures(e.point, {
100+
layers: ['clusters']
101+
});
102+
const clusterId = features[0].properties.cluster_id;
103+
const zoom = await map.getSource('earthquakes').getClusterExpansionZoom(clusterId);
104+
map.easeTo({
105+
center: features[0].geometry.coordinates,
106+
zoom
107+
});
108+
});
109+
110+
// When a click event occurs on a feature in
111+
// the unclustered-point layer, open a popup at
112+
// the location of the feature, with
113+
// description HTML from its properties.
114+
map.on('click', 'unclustered-point', (e) => {
115+
const coordinates = e.features[0].geometry.coordinates.slice();
116+
const mag = e.features[0].properties.mag;
117+
let tsunami;
118+
119+
if (e.features[0].properties.tsunami === 1) {
120+
tsunami = 'yes';
121+
} else {
122+
tsunami = 'no';
123+
}
124+
125+
// Ensure that if the map is zoomed out such that
126+
// multiple copies of the feature are visible, the
127+
// popup appears over the copy being pointed to.
128+
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
129+
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
130+
}
131+
132+
new maplibregl.Popup()
133+
.setLngLat(coordinates)
134+
.setHTML(
135+
`magnitude: ${mag}<br>Was there a tsunami?: ${tsunami}`
136+
)
137+
.addTo(map);
138+
});
139+
140+
map.on('mouseenter', 'clusters', () => {
141+
map.getCanvas().style.cursor = 'pointer';
142+
});
143+
map.on('mouseleave', 'clusters', () => {
144+
map.getCanvas().style.cursor = '';
145+
});
146+
});
147+
</script>
148+
</body>
149+
</html>

0 commit comments

Comments
 (0)