#はじめに
今回も要望がありd3.jsのライブラリや記事を探していたら、近いことをされている方がいたのでその方の記事のX軸とY軸を切り替えたものを今回記事にしました。
睡眠時間のデータを時間ごとの帯グラフの上に描画するイメージです。
#ソースはこちら
詳しくはその方の記事の解説を見てもらえるとわかりますが、このグラフは直近7日分の日付を動的に取得しているので場合によって静的にするか-1日だけの表示にするか変更が必要です。また、データの分割は同じ方のこの記事が参考になります。
オリジナル版との違いはXY軸の変換とデータの配列の3つめに種類を与えることで色分けを可能にしているところです。
Yellowfinのレポートの値を使うとしたらこの記事が参考になると思います。このソースの場合はdoDrawingの中のdata変数にデータが入っています。
//generateChart は必須の関数であり、Javascriptグラフの作成において呼び出しされます
generateChart = function (options) {
// これはお使いのブラウザのデバッガのブレークポイントのトリガとなるため、Javascriptをデバッグすることができます
debugger;
// グラフを描画する部分です
var $chartDrawDiv = $(options.divSelector);
// Javascriptに関係なく任意のポートレットでスクロールバーを停止するには、このCSSクラスを使用します
$chartDrawDiv.addClass('jsChartNoOverflow');
// これにより、datasetから高さと幅が取得されます。グラフの作成時にこれらを使用して、
// ダッシュボード、カンバス、ストーリーボードに正しく合わせます
//var height = options.dataset.chart_information.height;
//var width = options.dataset.chart_information.width;
var height = 400;
var width = 800;
// 必要に応じて、RAWデータJSONをグラフに必要な形式に変換します。
var processedData = processData(options.dataset.data);
// グラフの実績の描画を $chartDiv に行います。
doDrawing(processedData, $chartDrawDiv, height, width, options.errorCallback);
},
processData = function (dataset) {
// データが格納されるdatasetはカラム(列)ベースの形式であり、各カラム(列)にはデータの配列が含まれます
// eg. dataset.column_name[0].raw_data, dataset.column_name[0].formatted_data
},
doDrawing = function (data, $chartDiv, height, width, errorFunction) {
// require を使用して必要なJavascriptライブラリを読み込みます
// 付属するライブラリとその場所:
// js/chartingLibraries/c3/c3
// js/chartingLibraries/chartjs/Chart
// js/chartingLibraries/d3_3.5.17/d3_3.5.17
require(['js/chartingLibraries/d3/d3'], function (d3) {
try {
let divided = [
[
new Date("2022-01-10 03:00:00"),
new Date("2022-01-10 05:00:00"),
10
],
[
new Date("2022-01-10 05:30:00"),
new Date("2022-01-10 07:00:00"),
11
],
[
new Date("2022-01-11 01:00:00"),
new Date("2022-01-11 05:00:00"),
12
]
];
var $canvas = $('<svg id="svg"></svg>');
$chartDiv.append($canvas);
var margin = {
left: 30,
right: 30,
bottom: 50,
top: 30,
};
let svg = d3.select("#svg")
.attr("width", width)
.attr("height", height);
// グラフ描画コードはこちら
/* y 方向のスケールを作成する */
let nDates = 7; // 一週間分のデータを表示する
let y = d3.scaleLinear()
.domain([0, nDates])
.range([margin.top, height - margin.bottom]);
/* x 方向のスケールを作成する */
let x = d3.scaleLinear()
.domain([0, 24]) // 00:00:00 ~ 24:00:00
.range([margin.left, width - margin.right]);
/* 日付文字列のリストを取得する */
let tod = new Date();
let dateStrings = [];
let tmpDate = null;
for (let i = 0; i < nDates; i++) {
tmpDate = new Date();
tmpDate.setDate(tod.getDate() - i);
let ymd = tmpDate.toLocaleString().split(" ")[0];
let md = ymd.substring(5); // 年を取り除く
dateStrings.push(md);
}
dateStrings.reverse();
/* x 軸と目盛りを作成する */
let xAxis = svg.append("g")
.attr("class", "x_axis")
.attr(
"transform",
"translate(" +
[
0,
height - margin.bottom
].join(",") + ")"
)
.call(
d3.axisBottom(x)
.ticks(24)
.tickSize(-height + margin.top + margin.bottom)
.tickFormat(function (d) {
return d + ":00";
})
);
/* y 軸と目盛りを作成する */
let yAxis = svg.append("g")
.attr("class", "y_axis")
.attr(
"transform",
"translate(" +
[
margin.left,
0
].join(",") + ")"
)
.call(
d3.axisLeft(y)
.ticks(nDates)
.tickSize(-width + margin.left + margin.right)
.tickFormat(function (d, i) {
return dateStrings[i];
})
);
/* バーを描画する */
let innerWidth = width - margin.left - margin.right; // 内側の表示部分 (横幅)
let innerHeight = height - margin.bottom - margin.top; // 内側の表示部分 (高さ)
let barWidth = innerWidth / 24; // バーの横幅
let barheight = innerHeight / nDates;
let bars = svg.selectAll("rect")
.data(divided)
.enter()
.append("rect")
.attr("x", function (d, i) {
let start = d[0];
return x(toHour(start));
})
.attr("y", function (d, i) {
let start = d[0];
let startYmd = start.toLocaleString().split(" ")[0];
let startMd = startYmd.substring(5); // 2018/02/15 → 02-15
return y(dateStrings.indexOf(startMd));
})
.attr("height", barheight)
.attr("width", function (d, i) {
let milliSecInDay = 86400000; // Date - Date でミリ秒が返るため
let start = d[0];
let finish = d[1];
return innerWidth * (finish - start) / milliSecInDay;
})
.attr("fill", function (d, i) {
let colornum = d[2];
let color = "blue";
if (colornum == 10) {
color = "red";
} else if (colornum == 11) {
color = "green";
}
return color;
})
.style("opacity", 0.6);
/* その日の始めから何秒経過したかを返す */
function toSec(d) {
let elapsed = d.getHours() * 60 * 60 +
d.getMinutes() * 60 +
d.getSeconds();
return elapsed;
}
/* その日の始めから何時間経過したかを返す */
function toHour(d) {
let sec = toSec(d); // 経過秒
let secInDay = 86400; // 24 * 60 * 60
return sec / secInDay * 24;
}
/* 目盛りの日付文字列の位置を調整する */
yAxis.selectAll("g.tick")
.selectAll("text")
.attr(
"transform",
"translate(" +
[
0,
barheight / 2
].join(",") + ")"
);
} catch (err) {
errorFunction(err);
}
});
}
#まとめ
先人様のソースを見ることでかなり理解を深められました。これで工場の稼働時間などの可視化なども可能になりそうですね。
#参考にさせていただきました
D3.js -- 睡眠時間をグラフにプロットする
https://zdassen.hatenablog.com/entry/2018/02/21/012842
D3.js -- x軸、y軸の目盛りを描画する & グリッドを描画する
https://zdassen.hatenablog.com/entry/2018/02/15/131910
JavaScript -- 日付変更線をまたぐデータを翌日の00:00:00で分割する
https://zdassen.hatenablog.com/entry/2018/02/13/234150