news 2026/10/12 3:49:55

第52篇:数据分析-三维热力图——把热力图立起来,一半地面却被削成平顶

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
第52篇:数据分析-三维热力图——把热力图立起来,一半地面却被削成平顶

上一篇,本猿立了一个 flag:换一套坐标系,看同样的账会不会又变个算法。

现在来还这笔账。

上一篇我们量的是一张贴在地面上的彩色画布——半径 70 是 70 个像素,落地是个 18.557 km 的椭圆,模糊度滑到最右反而变成硬边实心圆。那篇里所有的账,都发生在"平面"上:画布多大、笔刷多宽、颜色多深。

这一次,同一张画布被竖起来了——不再贴在地上,而是被当成一张高度图,一格一格地长出地面。

我是 Cesium酱,一只在 WebGIS 领域摸爬滚打多年的前端猿。这个系列写到第 52 篇,规矩一直没变:只要面板上有数字,就得拿进几何里对一遍账。而这一篇的账,比上一篇更离谱一点——因为这次被"算错"的不是长度,是高度。

本文目标:把一份二维热力数据"立起来"变成三维地形的全过程拆开——为什么一半的地面会落在同一个高度上、为什么 60×60 个采样点只读到了画布的 9.28%、为什么七个滑块里有一个从出生就没生效过、以及为什么相机公式给的 77.5 km 只装得下三成网格。💪

💡 本文涉及的全部代码均已开源,完整的仓库地址见文末,可自行取用、随意魔改。


一、痛点与场景:为什么「把热力图立起来」不只是一次拉伸?

🎯 背景现状:二维热力图能告诉你"哪里热",但说不出"热多少"。做汇报的时候,一张平平的彩色图很难让人有体积感——同样的数据,立起来之后,高的地方一眼就能看出来,低的地方自然沉下去。于是"三维热力图"(也叫热力地形、数据海拔)成了数据大屏上的常客。

  • 🎯告警密度地形:把网格化的告警数量当海拔,一眼看出哪个片区"堆积"得最高
  • 🎯客流热力沙盘:商圈人流采样值抬升成山丘,做选址与疏导预演
  • 🎯设备负载地图:机柜、基站、充电桩的负载值铺成起伏,运维一眼定位重载区
  • 🎯环境监测:PM2.5、噪声、温度等格网观测值做成"污染山脉"

🤦 传统痛点:把二维数据抬成三维,听起来只是"给每个格点加个 z",真做起来每一环都有坑。

方案问题
每个数据点画一根柱子只表达点,不表达面;柱子之间全是缝,看着像刺猬不像地形
用PolygonGraphics逐个抬高一个格点一个实体,几千个实体进场景,帧率当场跪下
找现成的三维热力图库大多只支持网格/体渲染,和 Cesium 的椭球坐标系之间还得再套一层转换
自己贴一张半透明彩色画布那还是二维的——有颜色,没高度,转视角就露馅

💡 本文方案:这一次我们不去新造轮子,而是复用上一篇那张画布,把它从"贴图"改成"高度图":

1️⃣生成热力画布:和上一篇完全同一份引擎——撒点、落笔、染色,得到一张200×194的 RGBA 画布;

2️⃣按网格采样:在经纬度上铺一张60×60的采样网,每个格点去画布上取一次颜色,顺手把那个像素的 alpha 读成强度;

3️⃣强度变高度:高度 = 基准高度 + 强度 × 高度倍率,把每个采样点抬到对应的海拔上;

4️⃣织成三角网:相邻格点两两成面,织出一片连续的起伏地形,扔给一个Primitive。

✨ 一句话总结:把二维热力图当成一张高度图,用画布 alpha 的能力,把平面上的"深浅",一格一格地"翻译"成三维里的"高低"。


二、核心技术原理:从一张彩色画布到一片会起伏的地形

2.1 数据结构:四个场景,一个种子,一份引擎

三维版没有自己的数据层——它和上一篇共用同一个heatmap-lib。四个场景、同一个种子:

// 源码级剧透:heatmap-lib/heatmap-data.tsexportconstheatmapScenes:HeatmapScene[]=[{id:'beijing-clusters',label:'北京多中心',bounds:{west:114.9,south:38.4,east:118.0,north:41.4},defaultRadius:70,pointCount:120,description:'三个高值核心的高斯聚集点'},{id:'shanghai-band',label:'上海沿江带状',bounds:{west:120.8,south:30.6,east:122.3,north:31.9},defaultRadius:55,pointCount:100,description:'沿江线性分布、随位置波动的高值'},{id:'guangzhou-ring',label:'广州环形',bounds:{west:112.8,south:22.5,east:114.0,north:23.6},defaultRadius:50,pointCount:80,description:'环形分布的模拟数据'},{id:'chengdu-core',label:'成都核心聚集',bounds:{west:103.6,south:30.3,east:104.6,north:31.1},defaultRadius:65,pointCount:60,description:'中心高密度聚集模拟数据'}]
  • 🔑单位还是"度":bounds写的是经纬度,四个场景的跨度都在 1°~3° 之间——上一篇算过的那笔账(1° ≈ 85~111 km)在这里继续有效;
  • 🔑种子固定:默认20260827,所以同一次运行、同一个场景,画出来的地形永远一样;点"随机生成"只是把种子换成Math.floor(Math.random() * 1e9)——换种子,不换公式;
  • 🔑引擎是同一份:createHeatmapCanvas()一个字没改,三维版和二维版读的是同一张画布——只是读的通道不一样。这一点,后面 3.4 节会算成一笔账。

2.2 完整技术路线

我们设计的完整技术路线:

核心亮点:第 3 步"把 alpha 读成高度"是本文的杀手锏 🔪——它让上一篇那张画布里的每一个像素,都同时背了两个身份:一个是颜色,一个是海拔。

2.3 为什么直接拿画布的 alpha 当高度

抬高度这件事,最少有四种做法。我们选了最"抠"的那一种:

方案原理性能复杂度
每个数据点画一个柱体CylinderGraphics逐个抬升🐢 实体数 = 点数,场景被拖垮低
每个格点建一个多边形PolygonGraphics+extrudedHeight🐢 几千个实体 + 各自的批次中
用着色器实时算高度顶点着色器里采样纹理⚡ 快,但要走CustomShader高
把画布 alpha 当高度场(本文)采一次画布,CPU 端算出 z,织一张三角网⚡ 一张网格一个Primitive低

简单说:画布的 alpha 本来就已经是"强度"了,我们只是没让它继续当强度用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 3:47:27

具身智能创新原理(190):TVA具身架构下World模型的泛化能力研究

前沿技术探索:TVA智能体(简称TVA)TVA智能体(亦称“AI智能体视觉”)是依托Transformer架构与“因式智能体”理论构建的新型工业视觉系统,也是当前最具代表性的具身视觉技术之一。它有机融合深度强化学习&…

作者头像 李华
网站建设 2026/10/12 3:47:11

Go面试必问:HTTP服务性能优化 pprof定位到连接池调优全流程

Go面试必问:HTTP服务性能优化 pprof定位到连接池调优全流程 导语 Go 的 net/http 默认配置在生产环境中往往是性能瓶颈的根源:连接复用未开启、连接池大小不合理、Goroutine 泄漏导致内存暴涨。面试中经常出现"如何用 pprof 定位 HTTP 服务的性能瓶…

作者头像 李华