mirror of
https://github.com/hyperknot/openfreemap.git
synced 2026-05-21 14:02:15 +00:00
feat(cluster.html): add example HTML file to demonstrate clustering with MapLibre GL JS for visualizing earthquake data
This commit is contained in:
149
website/src/examples/cluster.html
Normal file
149
website/src/examples/cluster.html
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Create and style clusters</title>
|
||||||
|
<meta property="og:description" content="Use MapLibre GL JS' built-in functions to visualize points as clusters." />
|
||||||
|
<meta charset='utf-8'>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@4/dist/maplibre-gl.css' />
|
||||||
|
<script src='https://unpkg.com/maplibre-gl@4/dist/maplibre-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 maplibregl.Map({
|
||||||
|
container: 'map',
|
||||||
|
style: 'https://tiles.openfreemap.org/styles/positron',
|
||||||
|
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://maplibre.org/maplibre-gl-js/docs/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://maplibre.org/maplibre-style-spec/#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': ['Noto Sans 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 maplibregl.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>
|
||||||
Reference in New Issue
Block a user