function drawCDD(links, options) {
options = addDefaults(
options,
Object({
width: 500,
height: 500,
caption: 'No Label'
})
);
const innerRadius = Math.min(options.width, options.height) * 0.5 - 90;
const outerRadius = innerRadius + 10;
const rename = name =>
name.substring(name.indexOf(".") + 1, name.lastIndexOf("."));
const arc = d3
.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
const chord = d3.chordDirected().padAngle(10 / innerRadius);
const ribbon = d3
.ribbonArrow()
.radius(innerRadius - 1)
.padAngle(1 / innerRadius);
const nameSet = links.reduce(function(nameSet, d) {
nameSet.add(d.source);
nameSet.add(d.target);
return nameSet;
}, new Set());
const names = Array.from(nameSet).sort(function(a, b) {
const latLngA = COUNTRY_INFO_INDEX[a] && COUNTRY_INFO_INDEX[a].latLng[1];
const latLngB = COUNTRY_INFO_INDEX[b] && COUNTRY_INFO_INDEX[b].latLng[1];
return latLngA - latLngB;
});
options = addDefaults(
options,
Object({
nameToColor: d3.scaleOrdinal(
names,
d3.quantize(d3.interpolateRainbow, names.length)
)
})
);
const index = new Map(names.map((name, i) => [name, i]));
const matrix = Array.from(index, () => new Array(names.length).fill(0));
for (const { source, target, value } of links)
matrix[index.get(source)][index.get(target)] += value;
const svg = d3
.create("svg")
.attr("viewBox", [
-options.width / 2,
-options.height / 2,
options.width,
options.height
]);
const chords = chord(matrix);
const group = svg
.append("g")
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.selectAll("g")
.data(chords.groups)
.join("g");
group
.append("path")
.attr("fill", d => options.nameToColor(names[d.index]))
.attr("d", arc);
group
.append("text")
.each(d => (d.angle = (d.startAngle + d.endAngle) / 2))
.attr("dy", "0.35em")
.attr(
"transform",
d => `
rotate(${(d.angle * 180) / Math.PI - 90})
translate(${outerRadius + 5})
${d.angle > Math.PI ? "rotate(180)" : ""}
`
)
.attr("text-anchor", d => (d.angle > Math.PI ? "end" : null))
.text(d => names[d.index]);
group.append("title").text(
d => `${names[d.index]}
${d3.sum(chords, c => (c.source.index === d.index) * c.source.value)} outgoing →
${d3.sum(
chords,
c => (c.target.index === d.index) * c.source.value
)} incoming ←`
);
svg
.append("g")
.attr("fill-opacity", 0.75)
.selectAll("path")
.data(chords)
.join("path")
.style("mix-blend-mode", "multiply")
.attr("fill", d => options.nameToColor(names[d.source.index]))
.attr("d", ribbon)
.append("title")
.text(
d =>
`${names[d.source.index]} → ${names[d.target.index]} ${d.source.value}`
);
drawText2(svg, [0, options.height * 0.45], 'Figure: ' + options.caption);
return svg.node();
}