Skip to content

Instantly share code, notes, and snippets.

@mell0kat
Last active February 25, 2018 00:39
Show Gist options
  • Save mell0kat/bd217110a483d987e871733d2ae11d05 to your computer and use it in GitHub Desktop.
Save mell0kat/bd217110a483d987e871733d2ae11d05 to your computer and use it in GitHub Desktop.
Euro Debt Crisis (d3 chord) v4
license: mit

Built with blockbuilder.org

The Euro Debt Crisis by M. Bostock - converted to d3v4 by Me

Based on M. Bostock's original

What I learnt

  • d3.arc creates a donut-like shape and can be using for adding labels to groups created by d3.chord

  • d3.format can be used to format numbers (sig figs, commas, etc.)

  • Can use second argument of d3.csv (and other fetch functions) to format each row

  • svg element 'textPath' is used to draw text along a path (include an href attr with a reference to the path)

  • .darker() function available for d3.rgb

  • svg 'title' is used as a tooltip (on hover) description of svg elements (not all browsers support)

creditor debtor amount risk
Britain France 22.4 3
Britain Greece 0.55 0
Britain Italy 26 0
Britain Portugal 19.4 0
Britain United States 345 1
France Germany 53.8 1
France Greece 53.9 0
France Ireland 17.3 0
France Italy 366 0
France Japan 7.73 1
France Portugal 18.3 0
France Spain 118 2
France United States 322 1
Germany Britain 321 1
Germany Greece 19.3 0
Germany Ireland 48.9 0
Germany Portugal 32.5 0
Germany Spain 57.6 2
Germany United States 324 1
Ireland Britain 12 1
Ireland Greece 0.34 0
Ireland Spain 6.38 2
Italy Germany 111 1
Italy Greece 3.22 0
Italy Ireland 2.83 0
Italy Portugal 0.87 0
Japan Britain 28.2 1
Japan Germany 88.5 1
Japan Greece 1.37 0
Japan Ireland 18.9 0
Japan Italy 38.8 0
Japan Portugal 2.18 0
Japan Spain 25.9 2
Japan United States 796 1
Portugal Greece 10.1 0
Portugal Ireland 3.77 0
Portugal United States 0.52 1
Spain Britain 326 1
Spain Greece 0.78 0
Spain Italy 9.79 0
Spain Portugal 62 0
Spain United States 163 1
United States Greece 3.1 0
United States Ireland 11.1 0
United States Italy 3.16 0
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; background-color: black; }
h3 {
color: white;
font-family: sans-serif;
text-align: center
}
</style>
</head>
<h3>The Euro Debt Crisis by M. Bostock - converted to d3v4 by Me</h3>
<body>
<script>
const width = 480
const height = 500
const outerRadius = Math.min(width, height) / 2 - 4
const innerRadius = outerRadius - 20
const numFormat = d3.format(',.3r') // grouped thousands with three significant digits, "4,000"
const debits = []
const credits = []
// chord layout for computing angles of chords
const layout = d3.chord()
.sortGroups(d3.descending)
.sortSubgroups(d3.descending)
.sortChords(d3.descending)
.padAngle(.02)
// Color scale for "risk"
const fill = d3.scaleOrdinal()
.domain([0, 1, 2]) // question: what's the significance of these numbers?
. range(["#DB704D", "#D2D0C6", "#ECD08D", "#F8EDD3"])
// This is for creating donut chart around the circle (for groups)
const arc = d3.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
// ribbon generator for the chords
const ribbon = d3.ribbon()
.radius(innerRadius)
// creating two divs, one to hold debits, one to hold credits
const svg = d3.select('body')
.selectAll('div')
.data([debits, credits])
.enter()
.append('div')
.style('display', 'inline-block')
.style('width', `${width}px`)
.style('height', `${height}px`)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', `translate(${width/2},${height / 2})`)
function value () {
return this.amount
}
const formatRow = (row) => {
const _row = {}
_row.amount = +row.amount
_row.risk = +row.risk
_row.valueOf = value // for chord layout -- question: where is this being used
return { ...row, ..._row}
}
d3.csv('debts.csv', formatRow, (data) => {
let countryByName = d3.map()
let countryIndex = -1
const countryByIndex = []
data.forEach(d => {
if (countryByName.has(d.creditor)) {
d.creditor = countryByName.get(d.creditor)
} else {
countryByName.set(d.creditor, d.creditor = { name: d.creditor, index: ++countryIndex })
}
if (countryByName.has(d.debtor)) {
d.debtor = countryByName.get(d.debtor)
} else {
countryByName.set(d.debtor, d.debtor = { name: d.debtor, index: ++countryIndex })
}
d.debtor.risk = d.risk
})
// square matrix of debits and credits
for (let i = 0; i <= countryIndex; i++) {
debits[i] = []
credits[i] = []
for (let j =0; j <= countryIndex; j++){
debits[i][j] = 0
credits[i][j] = 0
}
}
data.forEach(d => {
debits[d.creditor.index][d.debtor.index] = d
credits[d.debtor.index][d.creditor.index] = d
countryByIndex[d.creditor.index] = d.creditor
countryByIndex[d.debtor.index] = d.debtor
})
svg.each(function(matrix, j){
const svg = d3.select(this)
svg.selectAll('.chord')
.data(layout(matrix))
.enter().append('path')
.attr('class', 'chord')
.style('fill', d => fill(d.source.value.risk))
.style('stroke', d => d3.rgb(fill(d.source.value.risk)).darker())
.attr('d', ribbon)
.append('title')
.text(d => `${d.source.value.debtor.name} owes ${d.source.value.creditor.name} $${numFormat(d.source.value)} B.`)
// Add groups
const g = svg.selectAll('.group')
.data(layout(matrix).groups)
.enter().append('g')
.attr('class', 'group')
// Add group "donut chart" arc
g.append('path')
.style('fill', d => fill(countryByIndex[d.index].risk))
.attr('id', (d,i) => `group${d.index}-${j}`)
.attr('d', arc)
.append('title')
.text(d => `${countryByIndex[d.index].name} ${(j ? 'owes' : 'is owed')} $${numFormat(d.value)}B.`)
g.append('text')
.attr('x', 6)
.attr('dy', 15)
.filter(d => d.value > 110)
.append('textPath')
.attr('href', d => `#group${d.index}-${j}`)
.text(d => countryByIndex[d.index].name)
})
})
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment