Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

MENU

javascriptでヒートマップ表示をまとめてみる

今回はjavascriptでヒートマップを表示する方法をまとめていきたいと思います!!

先日、赤外線アレイセンサ amg8833を用いて温度分布の取得や表示を行いました。
elsammit-beginnerblg.hatenablog.com
elsammit-beginnerblg.hatenablog.com

こちらの温度分布をリアルタイムでweb上に表示させることが出来ないかな??🤔
と考え調べてみたところ、、、

javascriptでヒートマップを描く方法あるではないですか!!

ということで、今回はjavascriptでヒートマップを描く方法についてまとめようと思います!!



javascriptでヒートマップ作成のためのライブラリについて

用いるライブラリはChart.jsです。
こちらのライブラリを用いると棒グラフや円グラフ、折れ線グラフがキレイに表示させることができるのですが、
ヒートマップは標準で用意されていません。

過去に調べ、用意されていないことが分かった自分はそこで検討を止めてしまっていました😢
しかしながら、世の中には工夫を凝らしてChart.jsでヒートマップを表示する方法を模索した方がいらっしゃいました。
こちらに詳細がまとめられております。
qiita.com

どうやら、積み上げ棒グラフを利用してヒートマップっぽく表示出来るようです!!

今回はこちらの記事を参考にして定期的にヒートマップの表示を切りかえる方法をまとめていこうと思います。

■ヒートマップを表示してみる

まずは先ほどの記事を利用させていただき、ランダムに表示させる方法を試してみます!!
コードはこちら。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>Test</title>
        <link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.css'>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js"></script>
        <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
    </head>
    <body>
        <div style="height:800px; width:800px">
            <canvas id="heatMap" style="height:800px; width:800px"></canvas>
        </div>
        <script type="text/javascript">
            'use strict'

            const mapHeight = 8;
            const mapWidth = 16;
            const maxVal = 741; 

            var datalist = (function(){
                const dlist = []
                for(let i=0;i<mapHeight * mapWidth;i++){
                    dlist.push(Math.random())
                }
                return dlist
            })()
            
            const generateDatasets = function(){
                const datasets = []
                
                for(let i=0; i<mapHeight; i++){
                    datasets.push({
                        data: new Array(mapWidth).fill(1),
                        borderWidth: 0.2,
                        borderColor: "#FFFFFF",
                        backgroundColor: generateColor(i) 
                    })
                }
                return datasets    
            }

            const generateColor = function(y){
                const datasetColors = []
                
                for(let x=0; x<mapWidth;x++){
                    const opa = ((datalist[x + (mapHeight-y-1) * mapWidth])*0.7 + 0.3).toFixed(2);
                    datasetColors.push("rgb(235,10,10,"+opa+")")
                }
                return datasetColors;
            }

            const generateLabels = function(){
                let labels = []
                for (var i=1; i<mapWidth+1; i++){
                    labels.push(i)
                }
                return labels
            }

            const ctx = document.getElementById('heatMap').getContext('2d')
            const heatMap = new Chart(ctx, {
                type: 'bar',
                data: {
                    datasets: generateDatasets(),
                    labels: generateLabels()
                },
                options: {
                    title: {
                    display: true,
                    text: 'Heat Map Sample',
                    fontSize: 18,
                },
                animation: false,
                animation: {
                    duration: 0
                },
                legend: {
                    display: false
                },
                scales: {
                    xAxes: [{
                        gridLines: {
                            color: '#FFFFFF',
                        },
                        barPercentage: 0.99,
                        categoryPercentage: 0.99,
                        stacked: true,
                        ticks: {
                            min: 0,
                            display: false,
                        }
                    }],
                    yAxes: [{
                        gridLines: {
                            color: '#FFFFFF',
                            zeroLineWidth: 0
                        },
                        stacked: true,
                        ticks: {
                            min: 0,
                            stepSize: 1,
                            display: false
                        }
                    }]
                },
            }
        });
        </script>
    </body>
</html>

実行した結果はこちらの通り。
f:id:Elsammit:20210220105933p:plain

■定期的にヒートマップを切り替える

最初に記載した通り、今回はサーモグラフィの計測結果をweb上で表示することが目的となります。
このため、定期的にヒートマップを切り替える必要があります。
そこで、先ほどのコードを定期実行に切り替えます!!

といってもほとんど先ほどのコードと同じで、javascriptにこちらを追記すればOKです。

const interval = function(){
    // ランダムな数値を生成.
    var buf = (function(){
        const dlist = []
        
        for(let i=0;i<mapHeight * mapWidth;i++){
            dlist.push(Math.random())
        }
        
        return dlist
     })()
     datalist = buf; // datalistに取得したランダム数値をセット.

     // ヒートマップにデータをセット.
     heatMap.data = {         
        datasets: generateDatasets(),
        labels: generateLabels()
    }
    
    heatMap.update();
}
setInterval(interval, 500);

~~~同じなので割愛

const heatMap = new Chart(ctx, {
    type: 'bar',
    data: {
        datasets: generateDatasets(),
        labels: generateLabels()
    },
    options: {
        title: {
        display: true,
        text: 'Heat Map Sample',
        fontSize: 18,
    },
    animation: false,
    animation: {
        duration: 0     // アニメーションを停止.
    },
    legend: {
        display: false
    },
    ~~~同じなので割愛
}

こちらのコードですが、
・setIntervalで500ms毎に関数コール
・ランダムな数値を生成しヒートマップにデータセット
・heatMap.update()によりヒートマップの表示をアップロード
となります。
ここで、

    animation: {
        duration: 0     // アニメーションを停止.
    },

を入れておかないと表示アニメーションも定期的に実行されてしまい、煩わしくなってしまうので注意。

こちらのコードを実行して、ランダムな数値を定期的に表示した結果がこちらになります。
f:id:Elsammit:20210220111241g:plain

■最後に

世の中にはすごい人がたくさんいますね🤔
自分で思いつかない方法で解決していきます。
自分も近づけるように頑張っていこう!!

まずはamg8833からセンサ値をweb上に表示させてみます!!