放射線量可視化(4):Highcharts JSのグラフによる可視化

注: この作業によって作成した可視化グラフはhttp://FromTo.Cc/rad/で、結果の定期ツイートは@xckbradで公開されています。

HTMLでの準備

Highcharts JSの利用法は、公式サイトに詳細なマニュアルとサンプルが用意されていますが、 今回のページで使った手法を簡単に解説します。

まずはHighcharts JSのライブラリと、 リアルタイムデータの取り込みのためのjQueryを読み込みます。

<script 
  src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"
  type="text/javascript">
</script>
<script src="/js/highcharts.js" type="text/javascript"></script>

そして、今回のサイトでHighcharts JSを使うためのJavaScriptファイルchart.js(後述)を読み込みます。

<script src="chart.js" type="text/javascript"></script>

<body>内では、グラフを描く<div>を用意します。 そして複数のグラフを縦に並べるために、間に空の<div class="spacer">を入れています。

<div id="graph5m" class="graph"></div>
<div class="spacer"></div>

これらの<div>はスタイルシートで次のようにスタイルが定義されています。

.graph {
    background-color: white;
    width: 95%; 
    height: 400px;
    margin: auto;
}
.spacer {
    height: 30px;
}

この<div class="graph">に、chart.js内のJavaScriptで、Highchrts JSのグラフを描画します。

JavaScriptでの準備

ということで、グラフ生成のためのJavaScriptのchart.jsについて簡単に解説します。

まずは、Highcharts JSの時間軸サポートは標準でUTCとなってしまうので、ローカルタイムを使用するために次の設定を入れます。

Highcharts.setOptions({
    global: {
        useUTC: false
    }
});

で、変数を宣言。

var firsttime5m;
var chart5m;

jQueryでページ表示時に初期化してもらいます。適当にコメントを入れてみました。 表示するグラフのデータは後述するreqeustData5m()という関数でAjaxを使って初期化します。 とりあえずは空でデータの入れ場所を用意しておきます。

$(document).ready(function() {
    firsttime5m = true;
    // グラフの生成
    chart5m = new Highcharts.Chart({
        chart: {
            // 描画対象の<div>のIDはgraph5m
            renderTo: 'graph5m',
            // 折れ線グラフを指定
            type: 'line',
            // requestData5m()で表示データを初期化する
            events: {
                load: requestData5m
            }
        },
        title: {
            text: '空間線量測定値 (1日分・5分間隔)'
        },
        subtitle: {
            text: '[神奈川県川崎市多摩区]'
        },
        xAxis: {
            // X軸に関しては時間軸サポートで詳細なフォーマットを指定可能
            type: 'datetime',
            dateTimeLabelFormats: {
                second: '%H:%M:%S',
                minute: '%H:%M',
                hour: '%m/%e %H:%M',
                day: '%y/%m/%e',
                week: '%y/%m/%e',
                month: '%Y/%m',
                year: '(%Y)'
            }
        },
        yAxis: {
            title: {
                text: '5分間平均線量(μSv/h)'
            },
            min: 0
        },
        tooltip: {
            // グラフ上にマウスカーソルを持っていった場合の表示
            formatter: function() {
                return '<b>'+ this.series.name +'</b><br/>'+
                    Highcharts.dateFormat('%m月%e日%H時%M分 ', this.x) + 
                    ': ' + this.y +'μSv/h';
            }
        },
        series: [{
            name: '5分間平均線量(μSv/h)',
            // データはとりあえず空で初期化、Ajaxで読み込む
            data: []
        }]
});

特に、Highcharts JSは時間軸のフォーマットに関して、 表示する時系列データの幅に基づいて、 指定したdateTimeLabelFormats:に従って自動的に調整してくれます。 この機能はなかなか素晴らしい。

そして、データ初期化のrequestData5m()関数はこのような処理です。 こちらも適宜コメントを追加してみました。 jQueryのおかげで非常に簡単にAjaxでJSONのデータを読むことができます。 ありがたい。

function requestData5m() {
    // jQueryによるAjaxの処理
    $.ajax({
        // JSONデータを読み込む相対URL
        url: 'このファイルからの相対パス/doserae2-1day.json',
        // 成功した場合、Highcharts JSのAPIを用いてデータを設定
        // (データの参照はseries.dataで可能だが更新はAPIが必要)
        success: function(points) {
            var series = chart5m.series[0];
            // JSON形式で転送されてきた配列のデータは、
            // pointsという変数に入っているので、
            // 最新のものをlastpoint変数に取得
            var lastpoint = points.pop();
            if (firsttime5m) {
                // 完全初期化の場合
                // JSONデータの全てをグラフに追加する
                for (var i in points) {
                    series.addPoint(points[i], false, false);
                }
                // 最後に、最新のデータを入れるタイミングでグラフを再描画する
                // (addPoint()の第2引数はredraw)
                series.addPoint(lastpoint, true, false);
                firsttime5m = false;
            }
            else {
                // 差分更新の場合
                // JSONデータについて、最新以外のすべてのデータが
                // 既にグラフ上に存在しているかをそれぞれチェック
                for (var i in points) {
                    var seen = false;
                    for (var j in series.data) {
                        if (series.data[j].x == points[i][0]) {
                            seen = true;
                            break;
                        }
                    }
                    // 存在していなければAPIで該当データを追加
                    // 再描画や古いデータの削除は行わない
                    if (!seen) {
                        series.addPoint(points[i], false, false);
                    }
                }
                // JSONデータの最新のものが、
                // 既にグラフ上に存在しているかどうかをチェック
                var seen = false;
                for (var i in series.data) {
                    if (series.data[i].x == lastpoint[0]) {
                        seen = true;
                        break;
                    }
                }
                // 存在していなければ最新データを追加する
                // 追加時に、最古のデータは削除される
                // (addPoint()の第2引数はredraw, 第3引数はshift)
                if (!seen) {
                    series.addPoint(lastpoint, true, true);
                }
            }
            // 150秒ごとにこの関数は呼び出される
            setTimeout(requestData5m, 150*1000);
        },
        // Ajaxデータの読み込み時にキャッシュしないようにする
        cache: false
    });
}

これで、グラフが表示できるようになりました。 同様の処理を2時間平均、1日平均のグラフについても行なっています。 これはhttp://FromTo.Cc/rad/でソースを表示することでも確認できます。

さて、最後はこの集計結果をTwitter Botとしてつぶやく処理です。 これは来週の日曜プログラミングに続く。