forked from davidflanagan/jstdg7
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pieChart.js
98 lines (84 loc) · 4.47 KB
/
pieChart.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/**
* Create an <svg> element and draw a pie chart into it.
*
* This function expects an object argument with the following properties:
*
* width, height: the size of the SVG graphic, in pixels
* cx, cy, r: the center and radius of the pie
* lx, ly: the upper-left corner of the chart legend
* data: an object whose property names are data labels and whose
* property values are the values associated with each label
*
* The function returns an <svg> element. The caller must insert it into
* the document in order to make it visible.
*/
function pieChart(options) {
let {width, height, cx, cy, r, lx, ly, data} = options;
// This is the XML namespace for svg elements
let svg = "http://www.w3.org/2000/svg";
// Create the <svg> element, and specify pixel size and user coordinates
let chart = document.createElementNS(svg, "svg");
chart.setAttribute("width", width);
chart.setAttribute("height", height);
chart.setAttribute("viewBox", `0 0 ${width} ${height}`);
// Define the text styles we'll use for the chart. If we leave these
// values unset here, they can be set with CSS instead.
chart.setAttribute("font-family", "sans-serif");
chart.setAttribute("font-size", "18");
// Get labels and values as arrays and add up the values so we know how
// big the pie is.
let labels = Object.keys(data);
let values = Object.values(data);
let total = values.reduce((x,y) => x+y);
// Figure out the angles for all the slices. Slice i starts at angles[i]
// and ends at angles[i+1]. The angles are measured in radians.
let angles = [0];
values.forEach((x, i) => angles.push(angles[i] + x/total * 2 * Math.PI));
// Now loop through the slices of the pie
values.forEach((value, i) => {
// Compute the two points where our slice intersects the circle
// These formulas are chosen so that an angle of 0 is at 12 o'clock
// and positive angles increase clockwise.
let x1 = cx + r * Math.sin(angles[i]);
let y1 = cy - r * Math.cos(angles[i]);
let x2 = cx + r * Math.sin(angles[i+1]);
let y2 = cy - r * Math.cos(angles[i+1]);
// This is a flag for angles larger than a half circle
// It is required by the SVG arc drawing component
let big = (angles[i+1] - angles[i] > Math.PI) ? 1 : 0;
// This string describes how to draw a slice of the pie chart:
let path = `M${cx},${cy}` + // Move to circle center.
`L${x1},${y1}` + // Draw line to (x1,y1).
`A${r},${r} 0 ${big} 1` + // Draw an arc of radius r...
`${x2},${y2}` + // ...ending at to (x2,y2).
"Z"; // Close path back to (cx,cy).
// Compute the CSS color for this slice. This formula works for only
// about 15 colors. So don't include more than 15 slices in a chart.
let color = `hsl(${(i*40)%360},${90-3*i}%,${50+2*i}%)`;
// We describe a slice with a <path> element. Note createElementNS().
let slice = document.createElementNS(svg, "path");
// Now set attributes on the <path> element
slice.setAttribute("d", path); // Set the path for this slice
slice.setAttribute("fill", color); // Set slice color
slice.setAttribute("stroke", "black"); // Outline slice in black
slice.setAttribute("stroke-width", "1"); // 1 CSS pixel thick
chart.append(slice); // Add slice to chart
// Now draw a little matching square for the key
let icon = document.createElementNS(svg, "rect");
icon.setAttribute("x", lx); // Position the square
icon.setAttribute("y", ly + 30*i);
icon.setAttribute("width", 20); // Size the square
icon.setAttribute("height", 20);
icon.setAttribute("fill", color); // Same fill color as slice
icon.setAttribute("stroke", "black"); // Same outline, too.
icon.setAttribute("stroke-width", "1");
chart.append(icon); // Add to the chart
// And add a label to the right of the rectangle
let label = document.createElementNS(svg, "text");
label.setAttribute("x", lx + 30); // Position the text
label.setAttribute("y", ly + 30*i + 16);
label.append(`${labels[i]} ${value}`); // Add text to label
chart.append(label); // Add label to the chart
});
return chart;
}