- 数据可视化
- 数据分析
【免费下载链接】kepler.gl
Kepler.gl is a powerful open source geospatial analysis tool for large-scale data sets.
kepler.gl 的热力图(Heatmap)图层通过彩色叠加直观地描述地理点数据在空间上的强度分布,并支持通过数值字段对强度进行加权。本文基于 i-heatmap.md 这一官方用户指南,结合 kepler.gl 仓库中热力图图层的完整源码实现(heatmap-layer.ts、deck-heatmap-layer.ts),系统讲解该图层的核心概念、三种数据列模式、全部视觉配置参数的含义与取值范围、权重字段的图例行为,以及底层的 GPU 密度渲染原理(含 Globe 3D 模式与反子午线修复)。读完本文,你将能够熟练地在 kepler.gl 中创建、调优热力图,并理解其渲染性能与行为边界。
一、Heatmap 图层是什么
按照官方用户指南的定义:
Heatmap layers describe the intensity of data at geographical points through a colored overlap. The intensity can be weighted by a numerical field.(热力图图层通过彩色叠加描述地理点数据的强度,强度可以由数值字段加权。)
与普通点图层(Point Layer)逐点绘制符号不同,热力图是一种聚合图层(aggregation layer):它将成千上万个点通过高斯核(Gaussian kernel)叠加成一张密度纹理,再按照色带(color ramp)映射为从冷色到暖色的"热点"效果。数据越密集、数值权重越大的区域,颜色越"热",从而在宏观层面揭示空间分布规律,例如:
- 城市中共享单车/出租车行程的起点密度;
- 地震、犯罪事件的空间聚集热点;
- 客流、基站信号等海量点事件分布。
在源码层面,这一"聚合"属性有明确标识:HeatmapLayer类重写了get isAggregated(),直接返回true(见 heatmap-layer.ts),并且getHoverData()返回null——即聚合后的热力图不支持逐点悬停拾取。同时getDefaultDeckLayerProps中显式设置pickable: false,并移除了聚合图层不支持的 GPU 数据过滤扩展(见 heatmap-layer.ts)。这些设计决定了热力图的交互与性能边界:它适合宏观热区观察,而非单点精确查询。
二、创建 Heatmap 图层:三种数据列模式
在 kepler.gl 中添加 Heatmap 图层时,首先需要数据具备空间坐标信息。从源码看,HeatmapLayer支持三种列模式(column mode),由SUPPORTED_COLUMN_MODES定义(见 heatmap-layer.ts):
| 列模式 | 必选列 | 说明 |
|---|---|---|
points(默认) | lat、lng | 最常见的经纬度双列模式,每行一个点 |
geojson | geojson | 每行一个 GeoJSON 几何对象(如多边形/折线) |
geoarrow | geoarrow | GeoArrow 编码的点列 |
- points 模式:
pointPosAccessor直接从数据容器中按lat.fieldIdx、lng.fieldIdx读取坐标(见 heatmap-layer.ts),这是性能最高的默认路径。 - geojson 模式:通过
_buildGeojsonDataToFeature将原始 GeoJSON 值解析为 Feature 对象,并预先计算每个几何的质心(centroid)(Point 取自身坐标,LineString/Polygon 取顶点平均)。聚合时每个 Feature 只在质心位置贡献一个热力点(见 heatmap-layer.ts)。同时该模式会扫描所有几何顶点计算出数据包围盒_geojsonBounds,用于更新图层元信息。 - geoarrow 模式:
geoarrowPosAccessor通过getGeoArrowPointCoords从 GeoArrow 编码中直接解出[x, y]坐标(见 heatmap-layer.ts)。
在自动添加图层时,静态方法findDefaultLayerProps会优先检测数据集中的geojson/geoarrow类型字段(仅接受GEOMETRY、GEOMETRY_FROM_STRING、PAIR_GEOMETRY_FROM_STRING等解析器类型),将其作为备选图层配置提供给用户(见 heatmap-layer.ts)。
三、视觉配置参数详解(含默认值与取值范围)
Heatmap 图层的全部可视化配置由heatmapVisConfigs注册(见 heatmap-layer.ts),并在构造器中通过this.registerVisConfig(heatmapVisConfigs)生效。各参数如下:
| 参数 | 类型 | 默认值 | 取值范围 | 步长 | 说明 |
|---|---|---|---|---|---|
opacity | number | — | — | — | 图层整体不透明度 |
colorRange | colorRange | — | — | — | 密度 → 颜色的映射色带,toColorRamp会在首尾各复制一次端点色以保证渐变完整(见 heatmap-layer.ts) |
radius | number | 20 | [0.1, 100] | 0.1 | 热力点半径(像素),定义见 layers.ts |
intensity | number | 1 | [0.001, 20] | 0.001 | 密度强度倍率,值越大颜色越饱和、热区越集中,聚焦权重 0.4 |
threshold | number | 0.18 | [0.01, 1] | 0.001 | 密度阈值,低于该值的区域被裁剪,用于去除噪声和"晕边" |
aggregation | select | SUM | SUM / MEAN | — | 权重聚合方式,见下方说明 |
3.1 聚合方式(aggregation)
HEATMAP_AGGREGATION_TYPES定义了两种聚合方式(见 heatmap-layer.ts):
- SUM(求和):每个点的权重累加,热点强度与事件数量成正比,适合展示"总量"分布(如事故总数)。
- MEAN(均值):在求和基础上除以覆盖该像素的点数,展示"平均强度",适合消除样本量差异的影响(如人均密度)。
该值会直接传入底层 deck.gl 图层:aggregation: (visConfig.aggregation || 'SUM') as 'SUM' | 'MEAN'(见 heatmap-layer.ts)。
3.2 权重字段(Weight by)
默认情况下每个点权重为 1,密度即点数量。通过"按字段加权"(weight by a numerical field),可以让强度由某个数值字段决定,这正是官方指南强调的"intensity can be weighted by a numerical field"。
源码层面,HeatmapLayer的visualChannels仅注册了一个weight通道(见 heatmap-layer.ts):
- 支持字段类型:
real(浮点)与integer(整数); - 通道比例尺:
CHANNEL_SCALES.size; - 默认度量:
property.density(即未选字段时以密度为权重)。
选中weightField后,图层会在formatLayerData中通过getVisChannelScale(weightScale, weightDomain, [0, 1])构造线性比例尺,将权重字段值归一化到[0, 1]区间参与聚合(见 heatmap-layer.ts)。权重通道的默认配置为:weightField: null、weightDomain: [0, 1]、weightScale: linear(见 heatmap-layer.ts)。
此外还有一个与权重相关的"weight intensity"概念:weight配置项(默认值 1,范围 [0.01, 500],仅当weightField存在时生效,定义见 layers.ts)。
3.3 图例行为的特殊性
值得注意的是,热力图的颜色不来自任何数据字段,而是密度 × 色带的结果。源码中专门定义了HEATMAP_LEGEND_COLOR_CHANNEL(见 heatmap-layer.ts),并注明:
Legend-only: heatmap GPU color is density × colorRange, not a data field. Keep this off
visualChannelsso layer type change does not copy colorField.(仅用于图例:热力图 GPU 颜色是密度 × 色带,而非数据字段。它不放入visualChannels,以免切换图层类型时错误复制 colorField。)
因此当你把某图层类型切换为 Heatmap 时,assignConfigToLayer会主动清空继承来的colorField,并把colorScale固定为quantize、colorDomain固定为[0, 1](见 heatmap-layer.ts)。图例的度量标签为property.density;仅当用户显式选择权重字段时,图例才会额外显示"weight by <字段>"通道(见 heatmap-layer.ts)。
四、渲染原理:从高斯核到密度纹理
HeatmapLayer本身是一个 kepler.gl 业务层(负责数据解析、列模式分发、配置管理),真正的渲染由它实例化的KeplerHeatmapLayer完成——后者继承自 deck.gl 官方聚合层HeatmapLayer(来自@deck.gl/aggregation-layers),并对其做了大量定制(见 deck-heatmap-layer.ts)。
4.1 针对旧版 Mapbox 热力图的视觉对齐
kepler.gl 曾使用 Mapbox GL 的 heatmap 图层渲染,迁移到 deck.gl 后为了让既有已保存地图的视觉效果完全一致,KeplerHeatmapLayer对 GPU 着色器做了两处补丁(见 deck-heatmap-layer.ts):
- 权重片元着色器(高斯核):将高斯核输出除以 8.5 并裁剪掉小于 0.00443 的微小值,消除半径边缘的硬边;同时把距离输入从
2. * dist改为dist,使核衰减与配置的半径正确对应。 - 最大权重片元着色器:强制红色通道为 1.0,因为 Mapbox 在采样色带时假定最大权重为 1.0。
此外,图层还从 weights / max-weights 变换着色器中移除了layer着色器模块——该模块会注入包含uniform float opacity的 uniform 块,而 GLSL ES 3.0 规范禁止在 uniform 块内使用存储限定符,会导致部分严格的移动端 GPU 驱动(如 Mali、Adreno)编译失败。由于 opacity 在这些变换阶段并未使用,移除它是安全的。
4.2 随缩放插值的强度与半径
renderLayer中通过interpolateByZoom实现了两个关键参数的缩放自适应(见 heatmap-layer.ts):
- intensity:在 zoom 0 时等于配置值,zoom 18(
MAX_ZOOM_LEVEL)时放大到配置值的 3 倍; - radiusPixels:在 zoom 0 时为 2 像素,zoom 18 时达到配置的
radius值。
这种设计保证放大后热区仍保持足够的细节与强度,不会因为像素尺寸随缩放变化而显得稀疏。
4.3 密度纹理参数
每次渲染都会创建weightsTextureSize: 512(即 512×512 的密度权重纹理)与debounceTimeout: 0的 GPU 图层(见 heatmap-layer.ts),同时传入colorRange、intensity、threshold、aggregation、radiusPixels等配置,并通过updateTriggers精确控制属性更新时机(列/列模式变化、权重字段变化时才会重新计算)。
五、Globe 3D 模式与 2D 反子午线修复
KeplerHeatmapLayer是仓库中对 deck.gl 热力图扩展最深的部分,主要解决两个渲染正确性问题(见 deck-heatmap-layer.ts 的设计注释)。
5.1 Globe 模式支持
原生 deck.gl HeatmapLayer 只适用于平面 WebMercator 视口:它通过反投影当前视口的四个屏幕角来推算密度纹理范围,并以一个 4 顶点的平面四边形渲染最终结果。这两种做法在球体(Globe)上都不可行——屏幕角无法反投影出有意义的经纬度包围盒,平面四边形也不会贴合球面。
为此,KeplerHeatmapLayer在收到globeMode+densityBounds(数据包围盒[minLng, minLat, maxLng, maxLat])时:
- 使用固定 WebMercator 视口(
_buildMercatorViewport,1024×1024 离屏帧)替代实时 Globe 相机来计算密度纹理,行为等同 2D 俯视场景,只是以数据而非屏幕为基准; - 将最终纹理渲染在一个**细分网格(tessellated mesh)**上,每个顶点经 deck.gl 的
project_position_to_clipspace投影到球面——这与 deck.gl BitmapLayer 将平面图像弯曲到球面的技术一致(createGlobeHeatmapMesh,见 deck-heatmap-layer.ts); - 通过
GlobeHeatmapTriangleLayer(自定义Layer子类)完成密度 → 色带的片元着色与透明度混合(见 deck-heatmap-layer.ts)。
在球体模式下还实现了**细节层级(LOD)**优化:当相机放大到可视区域完全落在数据范围内时,密度纹理转而围绕"可视区域"取帧(_computeDensityBounds),相同尺寸的纹理覆盖更小的地理范围,细节显著提升;同时以数据范围视口为基准对radiusPixels做比例缩放,避免取帧切换时热斑大小发生跳变(_updateWeightmap,见 deck-heatmap-layer.ts)。
5.2 2D 反子午线 / 缩小修复
在普通 2D 模式下,deck.gl 原生的"屏幕角四边形"在缩小到可见多个世界副本或平移跨越 ±180° 反子午线时,单块 4 顶点四边形会横跨子午线接缝,GPU 投影后顶点坍缩导致热力图消失或镜像。
_updateTextureRenderingBounds的修复思路是:不再把渲染四边形绑定到屏幕,而是绑定到数据自身的范围(densityBounds),并固定到离相机最近的一个世界副本(等同其他图层的wrapLongitude行为),同时关闭子图层的wrapLongitude以避免整世界宽四边形在 +180° 处回卷镜像(见 deck-heatmap-layer.ts)。这使得热力图无论缩放多远、平移多远,都稳定地出现在底图的单一位置。
六、实用提示与行为边界
综合以上源码分析,使用 kepler.gl Heatmap 图层时有几点值得注意:
- 适合大数据量宏观分布:聚合渲染在 GPU 上完成(512×512 密度纹理),点量很大时仍能流畅显示,但不支持逐点悬停与点击拾取(
getHoverData()返回null、pickable: false)。 - 颜色即密度:Heatmap 的颜色不绑定任何字段,调节
colorRange只影响密度到颜色的映射;想要按某字段加权,请使用"weight by"权重字段,并可用weight(强度倍率,0.01–500)与aggregation(SUM/MEAN)进一步控制。 - 去噪靠 threshold:默认阈值 0.18 会裁剪掉低密度"噪点",若热区边缘出现明显晕圈,可适当提高阈值;
intensity(0.001–20)则控制整体色彩饱和度与集中度。 - 注意自动图层类型切换:从其他图层切换为 Heatmap 时,旧图层的
colorField会被自动清空、colorScale重置为quantize,属预期行为。 - 数据包围盒的作用:
densityBounds不仅在 Globe 模式中支撑取帧与网格化,也保证了 2D 模式下跨反子午线、多世界副本缩放时的正确渲染——因此使用 Heatmap 前确保数据坐标合法、有有效包围盒非常重要。
七、继续深入
- 官方用户指南入口:docs/user-guides/README.md,Heatmap 位于"图层类型(c-types-of-layers)"章节;
- 业务层实现(列模式、配置、聚合、图例):heatmap-layer.ts;
- 渲染层实现(着色器补丁、Globe 网格、反子午线修复):deck-heatmap-layer.ts;
- 图层图标:heatmap-layer-icon.tsx;
- 图层注册与类型常量:src/layers/src/index.ts;
- 半径等通用视觉配置默认值:src/constants/src/layers.ts。
通过本文的配置表格与源码级原理,你已经可以按需调优热力图的聚合方式、半径、强度、阈值与权重字段,并在 2D 与 Globe 模式下获得稳定、一致的热区可视化效果。
- 数据可视化
- 数据分析
【免费下载链接】kepler.gl
Kepler.gl is a powerful open source geospatial analysis tool for large-scale data sets.
相关推荐
Kepler.gl Grid 网格图层完全指南:点密度聚合、属性配置与渲染原理
Kepler.gl Grid 网格图层完全指南:点密度聚合、属性配置与渲染原理 Grid(网格)图层是 kepler.gl 中一类基于经纬度点数据做空间聚合的图
数据可视化数据分析如何高效配置洛雪音乐音源:3步获取全网无损音乐的完整指南
如何高效配置洛雪音乐音源:3步获取全网无损音乐的完整指南 洛雪音乐音源配置是获取高质量音乐体验的关键,通过正确的音源选择和配置,你可以免费收听各大平台的音乐内容
数据可视化数据分析react-native-maps Heatmap 组件实战:在 Google Maps 上渲染数据密度热力图
react native maps Heatmap 组件实战:在 Google Maps 上渲染数据密度热力图 本篇指南围绕 react native maps
移动开发UI组件前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考