概要
d3.jsを使ってバーンダウンチャートを作ってみました。短いコードでデータをビジュアル化できるd3.jsは素晴らしいですね!! いろいろ試した結果、出来上がったJavaScript部分のコードをこちらに掲載します。
機能概要
サーバサイドからJSONデータを受け取りd3.jsを使ってSVG領域にバーンダウンチャートを描画します。予定線をグレー、実績線をオレンジ、工数合計線をグリーンとして折れ線グラフを作ります。各データのポイント部はマウスオーバー時に数値を表示します。
動作イメージ
フィルタ条件に対応したJSONをAjaxで出力し、d3.jsによりSVG領域を再描画します。d3.jsの処理が高速なので、ストレス無く快適に操作できます。
JSONデータの例
データベースにはチケット毎に以下の項目を時系列に格納しています。各項目を日付毎にサマライズしたJSONを生成しd3.jsに渡します。
- Day: 日付
- Total: 工数合計
- Planned: 予定
- Earned: 実績
[
{"Day":"2016/03/05","Total":30.0,"Planned":30.0,"Earned":28.0},
{"Day":"2016/03/06","Planned":20.0},
{"Day":"2016/03/07","Planned":10.0}
]
JavaScriptコードの例
コードの前半はsvg領域の初期化、jsonのパース、スケールの設定、軸の描画、当日線の描画を行います。drawメソッド以下は折れ線グラフの描画、データのポイントにサークルを描画しマウスオーバー時に表示する数値の格納を行います。
func.drawBurnDown = function () {
var $svg = $('#BurnDown');
if ($svg.length !== 1) {
return;
}
$svg.empty();
var dataSet = JSON.parse($('#BurnDownJson').val());
if (dataSet.length === 0) {
$svg.hide();
return;
}
$svg.show();
var svg = d3.select('#BurnDown');
var padding = 40;
var axisPadding = 70;
var width = parseInt(svg.style('width'));
var height = parseInt(svg.style('height'));
var bodyWidth = width - axisPadding - (padding);
var bodyHeight = height - axisPadding - (padding);
var minDate = new Date(d3.min(dataSet, function (d) { return d.Day; }));
var maxDate = new Date(d3.max(dataSet, function (d) { return d.Day; }));
var dayWidth = (bodyWidth - padding) / dateDiff('d', maxDate, minDate);
var xScale = d3.time.scale()
.domain([minDate, maxDate])
.range([padding, bodyWidth]);
var yScale = d3.scale.linear()
.domain([d3.max(dataSet, function (d) {
return d.Total !== undefined || d.Earned !== undefined
? Math.max.apply(null, [d.Total, d.Planned, d.Earned])
: d.Planned;
}), 0])
.range([padding, bodyHeight])
.nice();
var xAxis = d3.svg.axis()
.scale(xScale)
.tickFormat(d3.time.format('%m/%d'))
.ticks(10);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left');
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(' + axisPadding + ', ' + (height - axisPadding) + ')')
.call(xAxis)
.selectAll('text')
.attr('x', -20)
.attr('y', 20)
.style('text-anchor', 'start');
svg.append('g')
.attr('class', 'axis')
.attr('transform', 'translate(' + axisPadding + ', 0)')
.call(yAxis)
.selectAll('text')
.attr('x', -20);
var now = axisPadding + xScale(new shortDate());
var nowLineData = [
[now, axisPadding - 40],
[now, yScale(0) + 20]];
var nowLine = d3.svg.line()
.x(function (d) { return d[0]; })
.y(function (d) { return d[1]; });
svg.append('g').attr('class', 'now').append('path').attr('d', nowLine(nowLineData));
draw('total', 0, dataSet.filter(function (d) { return d.Total !== undefined; }));
draw('planned', 1, dataSet.filter(function (d) { return d.Planned !== undefined; }));
draw('earned', 2, dataSet.filter(function (d) { return d.Earned !== undefined; }));
function draw(css, n, ds) {
var line = d3.svg.line()
.x(function (d) {
return (dateDiff('d', new Date(d.Day), minDate) * dayWidth)
+ axisPadding + padding;
})
.y(function (d) {
return yScale(prop(d));
});
var g = svg.append('g').attr('class', css);
g.append('path').attr('d', line(ds));
g.selectAll('circle')
.data(ds)
.enter()
.append('circle')
.attr('cx', function (d, i) { return i * dayWidth + axisPadding + padding })
.attr('cy', function (d) { return yScale(prop(d)); })
.attr('r', 4)
.append('title')
.text(function (d) { return prop(d); });
function prop(d) {
switch (n) {
case 0: return d.Total;
case 1: return d.Planned;
case 2: return d.Earned;
}
}
}
}
ご参考
このコード例は「.NETで動くチケット管理ツール : プリザンター」で使用しています。サーバサイドのコード(ASP.NET C#)やCSSは、こちらをご参照ください。
https://github.com/Implem/Implem.Pleasanter
チャートの具体的な使い方
プロジェクトマネジメントにおける実践的なデータ活用例
http://qiita.com/Implem/items/112bfd38433f7f5d7bb8