Public
Edited
Mar 16, 2023
Insert cell
Insert cell
chart1 = {
const svg = d3.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height]);

const chords = chord(data);
console.log(chords);

const textId = DOM.uid("text");

const group = svg.append("g")
.attr("font-size", 16)
.attr("font-family", "roboto")
.selectAll("g")
.data(chords.groups)
.join("g");

group.append("path")
.attr("fill", d => color(names[d.index]))
.attr("d", arc);

group.append("title")
.text(d => `${names[d.index]}
${d.value}`);

const groupTick = group.append("g")
.selectAll("g")
.data(ticks)
.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.append("text")
.attr("x", 8)
.attr("dy", "0.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 => d.value);

group.append("text")
.each(d => {
d.angle = (d.startAngle + d.endAngle) / 2;
})
.attr("dy", ".35em")
.attr("class", "titles")
.attr("font-size", 18)
.attr("font-weight", "bold")

.attr("text-anchor", function(d) {
return d.angle > Math.PI ? "end" : null;
})
.attr("transform", function(d) {
if (names[d.index] === "Europe") {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 60) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "") +
"rotate(-79)";
} else if (names[d.index] === "Oceania") {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 60) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "") +
"rotate(-85)";
} else if (names[d.index] === "Other") {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 50) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "") +
"rotate(-74)";
} else if (names[d.index] === "Asia") {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 70) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "") +
"rotate(27)";
} else if (names[d.index] === "Africa") {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 50) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "") +
"rotate(-43)";
} else if (names[d.index] === "Latin America and the Caribbean") {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 80) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "") +
"rotate(40)";
} else if (names[d.index] === "Northern America") {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 60) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "") +
"rotate(0)";
} else {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")" +
"translate(" + (outerRadius + 60) + ")" +
(d.angle > Math.PI ? "rotate(180)" : "");
}
})
.selectAll("tspan")
.data(d => {
const name = names[d.index];
return name === "Latin America and the Caribbean"
? ["Latin America", "and the Caribbean"]
: name.split(/\s+/g);
})
.join("tspan")
.attr("x", 0)
.attr("y", (d, i, nodes) => `${i - nodes.length / 2 + 0.8}em`)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(d => d);

svg.append("g")
.attr("fill-opacity", 0.4)
.selectAll("path")
.data(chords)
.join("path")
.style("mix-blend-mode", "multiply")
.on("mouseover", function(d) { // add a mouseover event listener to the path element
d3.select(this)
.attr("stroke-width", 2)
.attr("stroke", "#000");
})
.on("mouseout", function(d) { // add a mouseout event listener to the path element
d3.select(this)
.attr("stroke-width", null)
.attr("stroke", null);
})
.attr("fill", d => color(names[d.source.index]))
.attr("d", ribbon)
.append("title")
.text(d => `${formatValue(d.source.value)} migrants moved from ${names[d.source.index]} to ${names[d.target.index]} `)
.style("font-size", "16px !important");

return svg.node();
}
Insert cell
formatValue = x => `${x.toFixed(0)} MM`
Insert cell
d3 = require("d3@6")
Insert cell
outerRadius = Math.min(width, height) * 0.5 - 100
Insert cell
innerRadius = outerRadius - 10
Insert cell
height = width
Insert cell
data1 = [{source: "Asia", target: "Asia", value: 68.497762},
{source: "Europe", target: "Europe", value: 44.246425},
{source: "Latin America and the Caribbean", target: "Northern America", value: 25.535633},
{source: "Asia", target: "Europe", value: 23.203976},
{source: "Africa", target: "Africa", value: 20.917565},
{source: "Asia", target: "Northern America", value: 17.549235},
{source: "Latin America and the Caribbean", target: "Latin America and the Caribbean", value: 11.297173},
{source: "Africa", target: "Europe", value: 11.024274},
{source: "Europe", target: "Asia", value: 7.16963},
{source: "Europe", target: "Northern America", value: 6.869872},
{source: "Latin America and the Caribbean", target: "Europe", value: 5.395924},
{source: "Africa", target: "Asia", value: 4.720103},
{source: "Other", target: "Asia", value: 4.176425},
{source: "Other", target: "Northern America", value: 4.053153},
{source: "Asia", target: "Oceania", value: 4.050511},
{source: "Africa", target: "Northern America", value: 3.268757},
{source: "Europe", target: "Oceania", value: 2.983395},
{source: "Other", target: "Africa", value: 2.515243},
{source: "Europe", target: "Latin America and the Caribbean", value: 1.355886},
{source: "Other", target: "Europe", value: 1.338129},
{source: "Northern America", target: "Latin America and the Caribbean", value: 1.293053},
{source: "Asia", target: "Africa", value: 1.207631},
{source: "Oceania", target: "Oceania", value: 1.107706},
{source: "Northern America", target: "Europe", value: 1.100304},
{source: "Northern America", target: "Northern America", value: 1.08852},
{source: "Europe", target: "Africa", value: 0.648455},
{source: "Africa", target: "Oceania", value: 0.587673},
{source: "Northern America", target: "Asia", value: 0.538199},
{source: "Latin America and the Caribbean", target: "Asia", value: 0.414658},
{source: "Asia", target: "Latin America and the Caribbean", value: 0.402369},
{source: "Oceania", target: "Europe", value: 0.397036},
{source: "Other", target: "Latin America and the Caribbean", value: 0.391721},
{source: "Oceania", target: "Northern America", value: 0.343625},
{source: "Northern America", target: "Oceania", value: 0.254319},
{source: "Latin America and the Caribbean", target: "Oceania", value: 0.214569},
{source: "Other", target: "Oceania", value: 0.18248},
{source: "Oceania", target: "Asia", value: 0.101725},
{source: "Northern America", target: "Africa", value: 0.053563},
{source: "Africa", target: "Latin America and the Caribbean", value: 0.048791},
{source: "Latin America and the Caribbean", target: "Africa", value: 0.032524},
{source: "Oceania", target: "Africa", value: 0.014483},
{source: "Oceania", target: "Latin America and the Caribbean", value: 0.00563}];
Insert cell
data = get_matrix(data1, "source", "target", "value");
Insert cell
names = data.names === undefined ? d3.range(data.length) : data.names
Insert cell
colors = data.colors === undefined ? d3.quantize(d3.interpolateRainbow, names.length) : data.colors
Insert cell
chart = {
const svg = d3.create("svg")
.attr("viewBox", [-width / 2, -height / 2, width, height]);

const chords = chord(data);
console.log(chords);

const group = svg.append("g")
.attr("font-size", 16)
.attr("font-family", "roboto")
.selectAll("g")
.data(chords.groups)
.join("g");

group.append("path")
.attr("fill", d => color(names[d.index]))
.attr("d", arc);

group.append("title")
.text(d => `${names[d.index]}
${d.value}`);

const groupTick = group.append("g")
.selectAll("g")
.data(ticks)
.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.append("text")
.attr("x", 8)
.attr("dy", "0.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 => d.value);

group.select("text")
.attr("font-weight", "bold")
.text(function(d) {
return this.getAttribute("text-anchor") === "end"
? `↑ ${names[d.index]}`
: `${names[d.index]} ↓`;
});

svg.append("g")
.attr("fill-opacity", 0.4)
.selectAll("path")
.data(chords)
.join("path")
.style("mix-blend-mode", "multiply")
.on("mouseover", function(d) { // add a mouseover event listener to the path element
d3.select(this)
.attr("stroke-width", 2)
.attr("stroke", "#000");
})
.on("mouseout", function(d) { // add a mouseout event listener to the path element
d3.select(this)
.attr("stroke-width", null)
.attr("stroke", null);
})
.attr("fill", d => color(names[d.source.index]))
.attr("d", ribbon)
.append("title")
.text(d => `${formatValue(d.source.value)} migrants moved from ${names[d.source.index]} to ${names[d.target.index]} `)
.style("font-size", "16px !important");

return svg.node();
}
Insert cell
function get_matrix(ldata, source_field, target_field, value_field) {
const rdata = {};
const namesSet = new Set();
for (let i = 0; i < ldata.length; i++) {
var rDataKey = ldata[i][source_field] + '-' + ldata[i][target_field];
rdata[rDataKey] = ldata[i];
namesSet.add(ldata[i][source_field]);
namesSet.add(ldata[i][target_field]);
}
const names = Array.from(namesSet);
// console.log(rdata);
// console.log(names);
const matrix = new Array(names.length);
// console.log("matrix pre-init: "+ matrix);
for (var i = 0; i < matrix.length; i++) {
matrix[i] = new Array(names.length);
}
// console.log("matrix init: "+ matrix);
for (let i = 0; i < names.length; i++) {
for (let j = 0; j < names.length; j++) {
const rDataKey1 = names[i]+"-"+names[j];
const rDataKey2 = names[j]+"-"+names[i];
if (rdata.hasOwnProperty(rDataKey1)){
// console.log(rdata[rDataKey1]);
// console.log(i, j, rDataKey1, rdata[rDataKey1][value_field]);
matrix[i][j] = +rdata[rDataKey1][value_field];
}else if (rdata.hasOwnProperty(rDataKey2)){
// console.log(rdata[rDataKey2]);
// console.log(i, j, rDataKey2, rdata[rDataKey2][value_field]);
matrix[i][j] = +rdata[rDataKey2][value_field];
}else{
// console.log(i, j, rDataKey1, rDataKey2, 0);
matrix[i][j] = 0;
}
}
}
// console.log("matrix populated: ");
// console.log(matrix);
var l = Object.assign(matrix, {"names": names})
return l;
}
Insert cell
chord = d3.chordDirected()
.padAngle(10 / innerRadius)
.sortSubgroups(d3.descending)
.sortChords(d3.descending)
// initialize a new chord generator
Insert cell
arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
Insert cell
ribbon = d3.ribbonArrow()
.radius(innerRadius - 5)
.padAngle(1 / innerRadius)
// initialize a new ribbon generator
Insert cell
unicefColors = [
"#961a49",
"#00aeef",
"#80bd41",
"#00833d",
"#ffc20e",
"#777779",
"#374ea2"
];
// create a UNICEF color array
Insert cell
color = d3.scaleOrdinal()
.domain(names)
.range(Object.values(unicefColors));
Insert cell
function ticks({startAngle, endAngle, value}) {
const k = (endAngle - startAngle) / value;
return d3.range(0, value, tickStep).map(value => {
return {value, angle: value * k + startAngle};
});
}
Insert cell
tickStep = d3.tickStep(0, d3.sum(data.flat()), 20)
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