Clustering and heatmaps
Quick answer
This tutorial compares two ways to tame dense point data in OpenLayers: clustering counts nearby features and heatmaps render density as colour. It wraps a source in ol.source.Cluster with distance and minZoom, reads the features array to size the symbol, and zooms via view.fit on a computed extent. It then configures ol.layer.Heatmap with radius, blur, weight and gradient normalised to the 95th percentile. Readers can choose and combine both representations.
Key points
- Treat Cluster distance as pixels, so clustering recomputes as the user zooms.
- Normalise heatmap weight against a high percentile rather than the maximum value.
- Toggle heatmap and cluster layers with setVisible instead of rendering both at once.
Example
var raw = new ol.source.Vector({
url: '/data/stations.geojson',
format: new ol.format.GeoJSON({
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857'
})
});
var clustered = new ol.source.Cluster({
distance: 40, // 聚合阈值,单位是像素
minZoom: 13, // 低于该级别不聚合
source: raw
});
var clusterLayer = new ol.layer.Vector({
source: clustered,
style: function (feature) {
var items = feature.get('features');
var size = items.length;
var radius = 8 + Math.min(size, 60) * 0.35;
return new ol.style.Style({
image: new ol.style.Circle({
radius: radius,
fill: new ol.style.Fill({ color: 'rgba(31, 95, 208, 0.85)' }),
stroke: new ol.style.Stroke({ color: '#ffffff', width: 2 })
}),
text: new ol.style.Text({
text: String(size),
font: '600 11px sans-serif',
fill: new ol.style.Fill({ color: '#ffffff' })
})
});
}
});map.on('singleclick', function (event) {
var hit = map.forEachFeatureAtPixel(event.pixel, function (f) { return f; }, {
hitTolerance: 6,
layerFilter: function (layer) { return layer === clusterLayer; }
});
if (!hit) return;
var items = hit.get('features');
if (items.length === 1) {
console.log('单点', items[0].get('name'));
return;
}
var extent = ol.extent.createEmpty();
items.forEach(function (f) {
ol.extent.extend(extent, f.getGeometry().getExtent());
});
map.getView().fit(extent, { duration: 400, maxZoom: 17, padding: [40, 40, 40, 40] });
});
Notes
- getGeometry on a cluster feature returns the cluster centre, not an original point.
- Heatmap layers ignore declutter, so label order must be managed through zIndex.
English edition written for Atlaslog. Code is taken from the original Chinese page and runs unchanged.
Atlaslog (2026). Clustering and heatmaps. https://www.jingweizhi.com/en/openlayers/tutorials/cluster-heatmap.html (accessed 2026-11-03).