创建和样式化集群
使用MapLibre GL JS的内置函数将点可视化为集群;
<!DOCTYPE html>
<html lang="en">
<head>
<title>创建和样式化集群</title>
<meta property="og:description" content="使用MapLibre GL JS的内置函数将点可视化为集群" />
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@5.5.0/dist/maplibre-gl.css' />
<script src='https://unpkg.com/maplibre-gl@5.5.0/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://api.maptiler.com/maps/streets/style.json?key=get_your_own_OpIi9ZULNHzrESv6T2vL',
center: [-103.59179687498357, 40.66995747013945],
zoom: 3
});
map.on('load', () => {
// 从GeoJSON数据添加新源并设置'cluster'选项为true
// GL-JS将向源数据添加point_count属性
map.addSource('earthquakes', {
type: 'geojson',
// 指向GeoJSON数据。此示例可视化USGS地震灾害计划记录的
// 2015年12月22日至2016年1月21日期间的所有M1.0+地震
data: 'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson',
cluster: true,
clusterMaxZoom: 14, // 进行点集群的最大缩放级别
clusterRadius: 50 // 集群点的半径(默认为50)
});
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'earthquakes',
filter: ['has', 'point_count'],
paint: {
// 使用step表达式(https://maplibre.org/maplibre-style-spec/#expressions-step)
// 实现三种类型的圆圈:
// * 点数小于100时,蓝色,20px圆圈
// * 点数在100到750之间时,黄色,30px圆圈
// * 点数大于或等于750时,粉色,40px圆圈
'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 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'
}
});
// 点击时检查集群
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
});
});
// 当点击unclustered-point图层中的要素时,
// 在要素位置打开弹出窗口,
// 显示其属性中的HTML描述。
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 = '是';
} else {
tsunami = '否';
}
// 确保当地图缩小时,如果有多个相同要素的副本可见,
// 弹出窗口显示在被指向的副本上。
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
new maplibregl.Popup()
.setLngLat(coordinates)
.setHTML(
`震级: ${mag}<br>是否发生海啸?: ${tsunami}`
)
.addTo(map);
});
map.on('mouseenter', 'clusters', () => {
map.getCanvas().style.cursor = 'pointer';
});
map.on('mouseleave', 'clusters', () => {
map.getCanvas().style.cursor = '';
});
});
</script>
</body>
</html>