Published
Edited
May 5, 2020
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// D3.js 読み込み
d3 = require('d3@5')
Insert cell
Insert cell
// JSONをD3.jsが読み込める形に変換
testdataEntries = d3.entries(testdata)
Insert cell
Insert cell
pie = d3.pie().value(d => d.value)
Insert cell
Insert cell
// 円グラフの角度を計算
pieData = pie(testdataEntries)
Insert cell
Insert cell
// 塗りつぶし用の色スケール
colorSeq = d3
.scaleSequential()
.domain([0, Object.keys(testdata).length])
.interpolator(d => d3.hsl(d * 360, 0.5, 0.7))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
// 描画領域
const svg = d3
.create('svg')
.attr('viewBox', [0, 0, width * 2, height * 2])
.attr('width', width * 2)
.attr('height', height * 2);
// グラフを真ん中にするためのエレメント
const chart = svg
.append('g')
.attr("transform", `translate(${width * 0.75}, ${height})`);

// グラフの半径
const radius = Math.min(width, height) / 2;

// グラフの扇型を生成する
const arc = d3
.arc()
.innerRadius(0)
.outerRadius(radius);

// 円グラフを描画する
chart
.selectAll(null) // 空のSelection
.data(pieData)
.enter()
.append('path')
.attr('d', datum => arc(datum)) // 扇型を描画する
.attr('fill', datum => colorSeq(datum.index)) // 塗りつぶし
.attr("stroke", "gray")
.style("stroke-width", "1px");

// 線分の開始位置のための円弧
const arcTarget = d3
.arc()
.innerRadius(radius * 0.8)
.outerRadius(radius);

// 後で使うのに便利な値を計算しておく
pieData.forEach(x => {
const centroid = arcTarget.centroid(x);
x.midAngle = (x.startAngle + x.endAngle) / 2; //開始角度と終了角度の間
x.centroid = centroid; // 線分の開始位置
x.centroidX = centroid[0];
x.centroidY = centroid[1];
});

// 円弧の中心点X座標降順にソート
// グラフの右側から左側に向かって、線分を長くしていくために使います。
const sortedPieData = pieData
.sort((a, b) => ssInvOp(arcTarget.centroidX, arcTarget.centroidX))
.map((x, i) => {
x.xOrder = i + 1;
return x;
});

// 線分の角度を計算
const calcLineRadical = d => {
const firstAngle = d.midAngle;
const firstRadius =
(d.xOrder * 30) / Math.abs(Math.cos(Math.PI - firstAngle));
const xpos = radius * 1.5 - d.centroidX;
const secondAngle =
Math.PI + Math.atan(xpos / (firstRadius * Math.cos(firstAngle)));
const secondRadius = Math.abs(xpos / Math.sin(secondAngle));
const fixedSecondAngle =
secondAngle < Math.PI ? secondAngle : secondAngle - Math.PI;

return [
[0, 0],
[firstAngle, firstRadius],
[fixedSecondAngle, secondRadius]
];
};

// 円弧の中心から外へ向かって線分を描画する
const line = d3.lineRadial();

chart
.selectAll(null)
.data(sortedPieData)
.enter()
.append('path')
.attr('d', datum => line(calcLineRadical(datum)))
.attr('transform', datum => `translate(${datum.centroid})`)
.attr('fill', 'none')
.attr('stroke', 'black')
.style('stroke-width', '1px');

// 値ラベルを描画する
const calcLabelY = d => {
const center = arcTarget.centroid(d);
const sign = Math.sign(Math.cos(d.midAngle));
return center[1] - sign * d.xOrder * 30 + 10;
};

chart
.selectAll(null)
.data(sortedPieData)
.enter()
.append('text')
.text(datum => datum.data.key) // 表示するテキスト
.attr('x', radius * 1.5 + 10)
.attr('y', calcLabelY)
.style("font-size", 20);

return svg.node();
}
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more