本文详解如何将原始经纬度数据聚合成频次统计,并据此动态设置 d3 绘制的 svg 圆圈半径,实现地理热点强度可视化。核心在于使用 d3 的 `d3.group()` 对坐标去重计数,并将频次映射为圆圈半径(如 `r = baseradius * count`)。
在 D3 与 Leaflet 协同绘制地理散点图时,若原始数据包含大量重复的经纬度组合(例如用户打卡、事件上报等场景),直接为每条记录渲染一个圆圈会导致视觉重叠、遮挡严重,且无法直观反映空间分布的密度差异。理想的解决方案是:先聚合数据,再按频次缩放圆圈大小。
D3 v7 提供了简洁高效的聚合工具 d3.group()。我们以经纬度数组 [lat, lng] 为键进行分组(注意需转为字符串以支持对象键比较),再统计每组出现次数:
// 假设原始 data 是 d3.csv 加载的数组,每项含 sub_district_lat / sub_district_long 字段
const grouped = d3.group(data, d =>
[d.sub_district_lat, d.sub_district_long].toString()
);
// 转为标准数组并构造新数据结构:{ cnt, sub_district_lat, sub_district_long }
const aggregatedData = Array.from(grouped, ([_, records]) => ({
cnt: records.length,
sub_district_lat: records[0].sub_district_lat,
sub_district_long: records[0].sub_district_long
}));随后,在绑定数据时,将圆圈半径 r 属性改为基于 cnt 动态计算:
.attr("r", d => Math.max(4, 8 * Math.sqrt(d.cnt))) // 推荐:用 sqrt 缓解极端值放大效应⚠️ 注意:*不建议直接使用 `12 d.cnt`**(如原答案所示)。当某位置出现 100 次时,半径达 1200px,极易覆盖整屏且丧失可比性。更科学的做法是:使用 Math.sqrt(d.cnt) 或 Math.cbrt(d.cnt) 压缩尺度;设置最小半径(如 Math.max(3, ...))确保低频点仍可见;可结合 d3.scaleLinear() 进行精细化映射(见下文进阶示例)。
通过以上方法,你不仅能清晰呈现地理热点分布,还能让可视化结果具备真实的统计意义和专业表现力。