如何绘制热力图

热力图长什么样?
(截图来自g2热力图)

image.png

什么是热力图?

热力图是数据可视化项目中,比较常用的显示方式。通过颜色变化程度,他可以直观反应出热点分布,区域聚集等数据信息。
“热力图”一词最初是由软件设计师Cormac Kinney于1991年提出并创造的,用来描述一个2D显示实时金融市场信息。最开始的热力图,是矩形色块加上颜色编码。经过多年的演化,习语上的热力图,如今更规范,更被大多数人理解的是这种经过平滑模糊过的热力图谱。
热力图一般是基于离散点、线或面的分析与表达,或者基于连续表面的密度分析得到的。

如何实现热力图?

大致分为三个步骤:
1.为每个数据点设置一个从中心向外灰度渐变的圆。
2.利用灰度可以叠加的原理,计算每个像素点数据交叉叠加得到的灰度值。
3.根据每个像素计算得到的灰度值,在一条彩色色带中进行颜色映射,最后对图像进行着色,得到热力图。

代码实现过程:
1.创建800*800的画布

<canvas id="heatmap" width="800" height="800" style="border:1px solid #000000;"></canvas>

2.声明一个长度为10的list,x、y代表坐标轴,value代表该点的值。比如声音分贝。

 var pointsdata = [{
     x: 471,
     y: 280,
     value: 25
 }, {
     x: 438,
     y: 300,
     value: 97
 }, {
     x: 420,
     y: 330,
     value: 71
 }, {
     x: 473,
     y: 70,
     value: 63
 }, {
     x: 463,
     y: 95,
     value: 97
 }, {
    x: 590,
    y: 437,
    value: 34
 }, {
    x: 377,
    y: 442,
    value: 66
 }, {
    x: 171,
    y: 254,
    value: 20
 }, {
     x: 6,
     y: 582,
     value: 64
 }, {
     x: 387,
     y: 477,
     value: 14
 }]

3.为每个数据点设置一个从中心向外灰度渐变的圆

 var canvas = document.getElementById('heatmap')
 var context = canvas.getContext('2d')
 context.clearRect(0, 0, 800, 800)  
// radius为该圆的半径,max和min为强弱阈值,可自行设置
 var radius = 30
 var max = 97
 var min = 14
 pointsdata.forEach(point => {
     let {
           x,
           y,
           value
     } = point
    // 开始为每个数据点绘制圆,圆心为[x,y],半径为radius
     context.beginPath()
     context.arc(x, y, radius, 0, 2 * Math.PI)
     context.closePath()
   // 给圆加上渐变色,圆心至边的渐变色为#000至透明
     let radialGradient = context.createRadialGradient(x, y, 0, x, y, radius)
     radialGradient.addColorStop(0, 'rgba(0,0,0,1)')
     radialGradient.addColorStop(1, 'rgba(0,0,0,0)')
     context.fillStyle = radialGradient
 // 给圆加上透明度,值为globalAlpha
     let globalAlpha = (value - min) / (max - min)
     context.globalAlpha = Math.max(Math.min(globalAlpha, 1), 0)
     context.fill()
 })

此时效果:


image.png

4.对图像进行着色

 const defaultColorStops = {
     '0.0': '#6E32C2',
     '0.1': '#1890FF',
     '0.2': '#12CCCC',
     '0.3': '#80FF73',
     '0.4': '#FAFFA8',
     '0.5': '#FFC838',
     '0.6': '#FF8C12',
     '0.7': '#FA541C',
     '1.0': '#F51D27',
 }
 const width = 256
 const height = 1

// 创建一条宽为256,高为1的像素带,颜色可自定义。
 function getColorPaint() {
     let paletteCanvas = document.createElement('canvas')
     paletteCanvas.width = width
     paletteCanvas.height = height
     let ctx = paletteCanvas.getContext('2d')
     let linearGradient = ctx.createLinearGradient(0, 0, width, height)
     for (const key in defaultColorStops) {
         // 绘制彩色渐变色条
         linearGradient.addColorStop(key, defaultColorStops[key])
     }
     ctx.fillStyle = linearGradient
     ctx.fillRect(0, 0, width, height)
     // 获取色带图像上的像素点。(像素信息为[r, g, b, a, r, g, b, a…],数组中容纳了所有的像素点信息,每个像素点依次排列,每个像素点信息包含r、g、b、a,总共有256个r、g、b、a)
     return ctx.getImageData(0, 0, width, height).data
 }

 let palette = getColorPaint()
 // 再获取整张800 * 800画布上的像素信息pointImgData(含灰度渐变圆)(同样,像素信息为[r, g, b, a, r, g, b, a…])
 let pointImg = context.getImageData(0, 0, 800, 800)
 let pointImgData = pointImg.data
// 获取pointImgData中每个像素点point的alpha值(范围0-255),根据获取到的alpha值,再到色带上一一对应,取色带上对应的像素点(rgb)覆盖到point上。(如当前取到的alpha值为0,则在彩色渐变色带上获取到的便是第一个像素点;若当前取到的alpha值为50,则是第50个像素点。)
 for (var i = 3; i < pointImgData.length; i += 4) {
     let alpha = pointImgData[i]
     let offset = alpha * 4
     pointImgData[i - 3] = palette[offset]
     pointImgData[i - 2] = palette[offset + 1]
     pointImgData[i - 1] = palette[offset + 2]
 }
 context.putImageData(pointImg, 0, 0)

此时效果:


image.png

同样的数据在g2热力图中渲染的效果:
【半径为20】


image.png

【半径为30】


image.png

同样的数据使用heatmap.js绘制的效果:
【半径为30】


image.png

部分文字介绍来源于G2
绘制原理来源于互联网

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容