Yellowfinで睡眠情報の帯グラフを作成する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223443/daee613f-ba66-982a-e28d-f4274f83dc53.png)


#はじめに
今回も要望がありd3.jsのライブラリや記事を探していたら、近いことをされている方がいたので[その方の記事](https://zdassen.hatenablog.com/entry/2018/02/21/012842)のX軸とY軸を切り替えたものを今回記事にしました。
睡眠時間のデータを時間ごとの帯グラフの上に描画するイメージです。

#ソースはこちら
詳しくは[その方の記事](https://zdassen.hatenablog.com/entry/2018/02/21/012842)の解説を見てもらえるとわかりますが、このグラフは直近7日分の日付を動的に取得しているので場合によって静的にするか-1日だけの表示にするか変更が必要です。また、データの分割は[同じ方のこの記事](https://zdassen.hatenablog.com/entry/2018/02/13/234150)が参考になります。
オリジナル版との違いはXY軸の変換とデータの配列の3つめに種類を与えることで色分けを可能にしているところです。
Yellowfinのレポートの値を使うとしたら[この記事](https://qiita.com/celery/items/d68e1e480e7455e1ecf4)が参考になると思います。このソースの場合はdoDrawingの中のdata変数にデータが入っています。

```javascript:time.js
//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