聚合与热力图
点位太密时,逐点绘制既看不清也没有信息量。聚合把相近的点合并成一个计数标记, 热力图把密度变成颜色,两者解决的是同一个问题的不同侧面。
聚合是数据源的包装
ol.source.Cluster 不改变原始数据,而是在每次解析时把距离相近的要素
合并为一个新要素。新要素的几何是一个点,属性里带 features 数组,
记录被合并的原始要素。
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' })
})
});
}
});
distance 是屏幕像素,不是米。缩放后同样的 40 像素覆盖的地面范围变化很大,
所以聚合结果会随缩放变化,这是预期行为。
聚合后没有原始几何。取 feature.getGeometry()
得到的是聚合中心点。要拿原始点,遍历 features 数组,
每个元素的几何才是真实位置。
点击聚合点展开
常见交互是「点一下聚合点,放大到能看清单个点位」。判断聚合大小后调整视图, 比直接遍历原始要素简单。
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] });
});
热力图的三个参数
ol.layer.Heatmap 基于点的权重在画布上叠加径向渐变。
radius 是单个点的模糊半径(像素),blur 控制衰减,
weight 决定每个点的贡献值。权重需要归一化,否则颜色会全部饱和。
var maxFlow = flowStats.p95; // 预先算好的 95 分位值
var heat = new ol.layer.Heatmap({
source: raw,
radius: 14,
blur: 18,
weight: function (feature) {
var v = feature.get('flow') || 0;
return Math.min(v / maxFlow, 1);
},
gradient: [
'rgba(0, 0, 255, 0)',
'rgba(0, 128, 255, 0.6)',
'rgba(0, 255, 200, 0.8)',
'rgba(255, 220, 0, 0.9)',
'rgba(255, 80, 0, 1)'
]
});
权重函数返回 0 到 1 之间的数。直接用原始流量做权重时, 少数极大值会把整片区域染成暖色,其余点全被压成冷色。
两种表达怎么选
| 关注点 | 聚合 | 热力图 |
|---|---|---|
| 回答的问题 | 这个位置有几个点 | 哪一片区域密度高 |
| 可读性 | 精确,能读出计数 | 连续,适合看趋势 |
| 交互 | 可点击、可展开 | 只能看,不适合拾取 |
| 数据量 | 万级以内表现良好 | 十万级仍可渲染 |
| 缩放适配 | 随缩放自动拆分 | 半径固定,缩放后模糊 |
业务报表里经常两者并用:低缩放级别用热力图看分布,
放大到街区级别切换成聚合或原始点位。切换时把两个图层放在同一位置,用
setVisible 控制,不要让它们同时渲染。
注意事项
- 聚合的
distance以像素计,设备像素比不同时,同样的数值观感会略有差异。 minZoom只对聚合生效,超过该级别后聚合源直接透出原始要素。- 聚合样式函数每次重绘都会调用,样式对象要按半径区间缓存,避免频繁创建。
- 热力图不支持
declutter,叠加标注时要注意图层顺序。 - 热力图底图过亮时颜色层次会丢失,深色底图或加一层半透明遮罩效果更稳。
先算分位数再定权重。用最大值归一化会让绝大多数点接近 0; 用 95 分位作为上限,颜色分布更均匀,极端值也不会让整图过曝。
经纬志(2026)。《聚合与热力图》。https://www.jingweizhi.com/openlayers/tutorial/cluster-heatmap.html(访问日期 2026-11-03)。