如何繪制熱力圖

熱力圖長(zhǎng)什么樣?
(截圖來(lái)自g2熱力圖)

image.png

什么是熱力圖?

熱力圖是數(shù)據(jù)可視化項(xiàng)目中,比較常用的顯示方式。通過(guò)顏色變化程度,他可以直觀反應(yīng)出熱點(diǎn)分布,區(qū)域聚集等數(shù)據(jù)信息。
“熱力圖”一詞最初是由軟件設(shè)計(jì)師Cormac Kinney于1991年提出并創(chuàng)造的,用來(lái)描述一個(gè)2D顯示實(shí)時(shí)金融市場(chǎng)信息。最開(kāi)始的熱力圖,是矩形色塊加上顏色編碼。經(jīng)過(guò)多年的演化,習(xí)語(yǔ)上的熱力圖,如今更規(guī)范,更被大多數(shù)人理解的是這種經(jīng)過(guò)平滑模糊過(guò)的熱力圖譜。
熱力圖一般是基于離散點(diǎn)、線或面的分析與表達(dá),或者基于連續(xù)表面的密度分析得到的。

如何實(shí)現(xiàn)熱力圖?

大致分為三個(gè)步驟:
1.為每個(gè)數(shù)據(jù)點(diǎn)設(shè)置一個(gè)從中心向外灰度漸變的圓。
2.利用灰度可以疊加的原理,計(jì)算每個(gè)像素點(diǎn)數(shù)據(jù)交叉疊加得到的灰度值。
3.根據(jù)每個(gè)像素計(jì)算得到的灰度值,在一條彩色色帶中進(jìn)行顏色映射,最后對(duì)圖像進(jìn)行著色,得到熱力圖。

代碼實(shí)現(xiàn)過(guò)程:
1.創(chuàng)建800*800的畫(huà)布

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

2.聲明一個(gè)長(zhǎng)度為10的list,x、y代表坐標(biāo)軸,value代表該點(diǎn)的值。比如聲音分貝。

 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.為每個(gè)數(shù)據(jù)點(diǎn)設(shè)置一個(gè)從中心向外灰度漸變的圓

 var canvas = document.getElementById('heatmap')
 var context = canvas.getContext('2d')
 context.clearRect(0, 0, 800, 800)  
// radius為該圓的半徑,max和min為強(qiáng)弱閾值,可自行設(shè)置
 var radius = 30
 var max = 97
 var min = 14
 pointsdata.forEach(point => {
     let {
           x,
           y,
           value
     } = point
    // 開(kāi)始為每個(gè)數(shù)據(jù)點(diǎn)繪制圓,圓心為[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()
 })

此時(shí)效果:


image.png

4.對(duì)圖像進(jìn)行著色

 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

// 創(chuàng)建一條寬為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)
     // 獲取色帶圖像上的像素點(diǎn)。(像素信息為[r, g, b, a, r, g, b, a…],數(shù)組中容納了所有的像素點(diǎn)信息,每個(gè)像素點(diǎn)依次排列,每個(gè)像素點(diǎn)信息包含r、g、b、a,總共有256個(gè)r、g、b、a)
     return ctx.getImageData(0, 0, width, height).data
 }

 let palette = getColorPaint()
 // 再獲取整張800 * 800畫(huà)布上的像素信息pointImgData(含灰度漸變圓)(同樣,像素信息為[r, g, b, a, r, g, b, a…])
 let pointImg = context.getImageData(0, 0, 800, 800)
 let pointImgData = pointImg.data
// 獲取pointImgData中每個(gè)像素點(diǎn)point的alpha值(范圍0-255),根據(jù)獲取到的alpha值,再到色帶上一一對(duì)應(yīng),取色帶上對(duì)應(yīng)的像素點(diǎn)(rgb)覆蓋到point上。(如當(dāng)前取到的alpha值為0,則在彩色漸變色帶上獲取到的便是第一個(gè)像素點(diǎn);若當(dāng)前取到的alpha值為50,則是第50個(gè)像素點(diǎn)。)
 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)

此時(shí)效果:


image.png

同樣的數(shù)據(jù)在g2熱力圖中渲染的效果:
【半徑為20】


image.png

【半徑為30】


image.png

同樣的數(shù)據(jù)使用heatmap.js繪制的效果:
【半徑為30】


image.png

部分文字介紹來(lái)源于G2
繪制原理來(lái)源于互聯(lián)網(wǎng)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容