Public
HTTP
fil-d3chorddiagram.web.val.run
October 23, 2023
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
99
100
import { svgServer } from "https://esm.town/v/fil/svgServer";
export async function d3ChordDiagram(req) {
// Import D3 and create a DOM document for server-side-rendering.
const d3 = await import("npm:d3");
const document = await import("https://esm.sh/linkedom@0.15").then((l) =>
l.parseHTML("<a>").document
);
// Data
const data = Object.assign([
[11975, 5871, 8916, 2868],
[1951, 10048, 2060, 6171],
[8010, 16145, 8090, 8045],
[1013, 990, 940, 6907],
], {
names: ["black", "blond", "brown", "red"],
colors: ["#000000", "#ffdd89", "#957244", "#f26223"],
});
// Compute the SVG and return it through the web endpoint
return svgServer(req, chart(data).outerHTML);
//
// ======================================================
//
function chart(data) {
const width = 640;
const height = width;
const outerRadius = Math.min(width, height) * 0.5 - 30;
const innerRadius = outerRadius - 20;
const { names, colors } = data;
const sum = d3.sum(data.flat());
const tickStep = d3.tickStep(0, sum, 100);
const tickStepMajor = d3.tickStep(0, sum, 20);
const formatValue = d3.formatPrefix(",.0", tickStep);
const chord = d3.chord()
.padAngle(20 / innerRadius)
.sortSubgroups(d3.descending);
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
const ribbon = d3.ribbon()
.radius(innerRadius);
const svg = d3.select(document.body).append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto; font: 10px sans-serif;");
const chords = chord(data);
const group = svg.append("g")
.selectAll()
.data(chords.groups)
.join("g");
group.append("path")
.attr("fill", (d) => colors[d.index])
.attr("d", arc)
.append("title")
.text((d) => `${d.value.toLocaleString("en-US")} ${names[d.index]}`);
const groupTick = group.append("g")
.selectAll()
.data((d) => groupTicks(d, tickStep))
.join("g")
.attr(
"transform",
(d) =>
`rotate(${d.angle * 180 / Math.PI - 90}) translate(${outerRadius},0)`,
);
groupTick.append("line")
.attr("stroke", "currentColor")
.attr("x2", 6);
groupTick
.filter((d) => d.value % tickStepMajor === 0)
.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr(
"transform",
(d) => d.angle > Math.PI ? "rotate(180) translate(-16)" : null,
)
.attr("text-anchor", (d) => d.angle > Math.PI ? "end" : null)
.text((d) => formatValue(d.value));
svg.append("g")
.attr("fill-opacity", 0.7)
.selectAll()
.data(chords)
.join("path")
.attr("d", ribbon)
.attr("fill", (d) => colors[d.target.index])
.attr("stroke", "white")
.append("title")
.text((d) =>
`${d.source.value.toLocaleString("en-US")} ${names[d.source.index]} → ${
names[d.target.index]
}${
d.source.index !== d.target.index
? `\n${d.target.value.toLocaleString("en-US")} ${
names[d.target.index]
} → ${names[d.source.index]}`
: ``
}`
);
return svg.node();
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
October 23, 2023