Skip to content

Instantly share code, notes, and snippets.

@ypetya
Last active October 27, 2019 09:19
Show Gist options
  • Save ypetya/c595ec53dbff9802772e512795c77327 to your computer and use it in GitHub Desktop.
Save ypetya/c595ec53dbff9802772e512795c77327 to your computer and use it in GitHub Desktop.
GDP - 2019 ksh.hu d3.v5

Line chart with legend

Used d3 v5 with svg.

D3 features

  • path with curve lineGenerator
  • mouse hover, scale invert
  • legend clickable
  • legend switch All
  • color scale
  • recalculating y-scale on change
  • data sort without accents

Data-set

Exported gdp data from http://www.ksh.hu/

country 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018
Ausztria 213.6 220.5 226.7 231.9 242.3 254.1 267.8 284.0 293.8 288.0 295.9 310.1 318.7 323.9 333.1 344.3 356.2 369.9 386.1
Belgium 258.2 265.8 275.1 282.6 298.7 311.5 326.7 344.7 354.1 348.8 365.1 379.1 387.5 392.3 400.1 411.1 424.6 439.2 450.5
Bulgária 14.3 15.8 17.3 18.7 20.9 23.9 27.2 32.4 37.2 37.3 38.2 41.3 41.9 41.9 42.8 45.3 48.1 51.7 55.2
Ciprus 10.8 11.6 12.1 12.9 13.9 15.0 16.3 17.6 19.0 18.7 19.3 19.7 19.5 18.1 17.6 17.7 18.5 19.6 20.7
Csehország 66.8 75.4 87.1 88.2 96.0 109.6 123.9 138.3 161.3 148.7 156.7 164.0 161.4 157.7 156.7 168.5 176.4 191.7 206.8
Dánia 178.0 184.0 189.8 193.4 202.4 212.8 225.5 233.4 241.6 231.3 243.2 247.9 254.6 258.7 265.8 273.0 282.1 292.8 297.6
Egyesült Királyság 1787.3 1816.2 1881.2 1809.1 1934.5 2030.9 2150.3 2252.5 1984.0 1725.4 1850.5 1894.9 2089.6 2074.0 2287.9 2611.9 2403.4 2338.0 2393.7
Észtország 6.2 7.0 7.8 8.7 9.7 11.3 13.5 16.2 16.5 14.1 14.7 16.7 17.9 18.9 20.1 20.7 21.7 23.6 25.7
Finnország 136.3 144.4 148.3 151.6 158.5 164.4 172.6 186.6 193.7 181.0 187.1 196.9 199.8 203.3 205.5 210.0 216.1 223.9 233.6
Franciaország 1478.6 1538.2 1587.8 1630.7 1704.0 1765.9 1848.2 1941.4 1992.4 1936.4 1995.3 2058.4 2088.8 2117.2 2149.8 2198.4 2234.1 2295.1 2353.1
Görögország 143.0 152.2 163.5 178.9 193.7 199.2 217.9 232.7 242.0 237.5 226.0 207.0 191.2 180.7 178.7 177.3 176.5 180.2 184.7
Hollandia 452.0 481.9 501.1 512.8 529.3 550.9 584.5 619.2 647.2 624.8 639.2 650.4 653.0 660.5 671.6 690.0 708.3 737.0 773.4
Horvátország 23.6 26.0 28.5 30.7 33.4 36.5 40.2 43.9 48.1 45.1 45.2 44.8 44.0 43.8 43.4 44.6 46.6 49.0 51.5
Írország 108.4 122.0 136.0 145.6 156.1 170.2 185.0 197.2 187.8 170.1 167.7 171.1 175.2 179.9 195.3 262.5 273.2 294.1 318.5
Lengyelország 186.4 212.4 210.1 192.3 206.1 246.2 274.6 313.9 366.2 317.1 361.8 380.2 389.4 394.7 411.2 430.3 426.5 467.3 496.5
Lettország 8.6 9.4 10.2 10.5 11.7 13.7 17.3 22.7 24.4 18.7 17.8 20.2 22.1 22.8 23.6 24.3 25.0 27.0 29.5
Litvánia 12.5 13.7 15.2 16.7 18.2 21.0 24.1 29.0 32.7 26.9 28.0 31.3 33.3 35.0 36.6 37.4 38.8 42.2 45.1
Luxemburg 23.1 23.8 25.1 26.2 27.9 30.0 33.8 37.2 38.1 37.0 40.2 43.2 44.1 46.5 49.8 51.6 53.3 55.3 58.9
Magyarország 51.3 60.1 71.9 75.5 83.8 90.9 91.8 102.2 108.1 94.3 98.8 101.3 99.5 101.9 105.5 110.9 113.9 124.1 131.9
Málta 4.4 4.5 4.7 4.8 4.9 5.1 5.4 5.8 6.1 6.1 6.6 6.8 7.2 7.6 8.5 9.6 10.3 11.3 12.3
Németország 2,116.5 2,179.9 2,209.3 2,220.1 2,270.6 2,300.9 2,393.3 2,513.2 2,561.7 2,460.3 2,580.1 2,703.1 2,758.3 2,826.2 2,938.6 3,048.9 3,159.8 3,277.3 3,386.0
Olaszország 1,239.3 1,298.9 1,345.8 1,390.7 1,448.4 1,489.7 1,548.5 1,609.6 1,632.2 1,572.9 1,604.5 1,637.5 1,613.3 1,604.6 1,621.8 1,652.1 1,689.8 1,727.4 1,757.0
Portugália 128.5 135.8 142.6 146.2 152.4 158.7 166.2 175.5 178.9 175.4 179.9 176.2 168.4 170.3 173.1 179.8 186.5 194.6 201.6
Románia 40.6 45.1 48.7 51.1 60.4 79.2 97.2 127.6 146.6 125.2 125.4 131.9 133.1 143.8 150.5 160.3 170.4 187.5 202.9
Spanyolország 646.3 699.5 749.3 803.5 861.4 930.6 1,008.0 1,080.8 1,116.2 1,079.1 1,080.9 1,070.4 1,039.8 1,025.7 1,037.8 1,081.2 1,118.7 1,166.3 1,208.2
Svédország 282.2 268.2 281.0 293.8 307.8 313.6 335.3 356.9 352.7 310.0 369.5 405.4 423.8 436.2 433.1 449.2 463.1 475.2 466.9
Szlovákia 22.3 23.9 26.3 30.1 34.7 39.3 45.5 56.2 66.0 64.0 67.6 70.6 72.7 74.2 76.1 79.1 81.2 84.9 90.2
Szlovénia 21.9 23.2 25.1 26.3 27.7 29.2 31.6 35.2 38.0 36.2 36.3 36.9 36.1 36.2 37.6 38.9 40.4 43.0 45.9
Európai Unió–28 9,661.0 10,059.3 10,427.5 10,583.2 11,109.7 11,604.4 12,272.0 13,005.7 13,086.5 12,330.6 12,841.5 13,217.5 13,484.2 13,596.8 14,072.0 14,828.7 14,963.8 15,389.3 15,884.0
Eurózóna–19 7,030.3 7,356.1 7,611.9 7,830.5 8,164.3 8,460.8 8,906.0 9,404.6 9,640.7 9,296.2 9,552.2 9,805.6 9,846.8 9,944.0 10,175.2 10,534.8 10,833.2 11,212.0 11,581.0
Észak-Macedónia 4.1 4.1 4.2 4.4 4.6 5.0 5.5 6.1 6.8 6.8 7.1 7.5 7.6 8.1 8.6 9.1 9.7 10.0 10.7
Norvégia 185.9 194.4 207.8 202.4 212.9 248.3 275.3 293.3 317.2 278.6 324.0 358.7 397.1 394.0 376.6 348.4 335.7 354.3 368.4
Oroszország 302.1 368.0 392.3 410.3 512.0 661.5 849.9 1,022.6 1,220.7 947.0 1,238.8 1,474.5 1,707.2 1,727.4 1,554.4 1,222.7 1,160.5 .. ..
Svájc 294.9 311.3 320.2 312.4 317.5 328.7 343.5 350.7 378.2 390.2 441.1 504.0 519.7 518.4 534.9 612.7 605.8 601.4 597.3
Szerbia .. .. 17.1 18.7 20.0 22.3 25.9 31.6 35.7 32.5 31.5 35.4 33.7 36.4 35.5 35.7 36.7 39.2 42.8
Törökország 296.9 222.6 249.6 276.1 324.7 401.7 436.3 492.8 521.8 461.9 581.0 596.5 678.5 714.3 703.4 773.0 780.2 753.9 ..
India .. 540.7 535.5 523.7 558.4 653.4 732.6 857.1 858.2 932.5 1,245.8 1,346.4 1,449.6 1,441.5 1,538.5 1,933.3 2,051.0 2,281.1 ..
Izrael .. 145.9 128.0 112.5 108.9 114.5 122.7 130.5 147.4 149.2 176.7 188.1 200.4 220.6 233.8 270.8 288.7 313.0 312.9
Japán 5,295.1 4,812.3 4,370.5 3,935.3 3,875.1 3,830.0 3,608.3 3,297.3 3,415.6 3,755.6 4,304.5 4,428.7 4,829.3 3,880.7 3,662.4 3,955.9 4,459.1 4,302.1 4,209.4
Kína .. 1,495.5 1,555.2 1,467.8 1,571.8 1,837.3 2,192.3 2,592.6 3,122.6 3,657.9 4,593.8 5,424.0 6,644.9 7,262.6 7,834.2 9,837.4 10,065.8 10,758.3 11,530.5
Koreai Köztársaság 608.7 595.9 648.2 602.1 615.8 722.2 806.0 819.5 687.7 649.6 826.0 864.7 951.5 983.2 1,062.9 1,244.8 1,278.5 1,355.3 1,372.0
Dél-afrikai Köztársaság 148.1 136.1 122.9 155.4 184.4 207.0 215.6 218.4 196.5 214.8 283.3 299.5 308.4 275.8 264.2 285.8 268.0 309.2 312.1
Egyiptom 113.4 114.2 95.5 75.3 66.6 75.7 89.9 100.0 116.1 142.2 173.5 178.0 217.0 216.9 230.0 299.3 300.4 209.4 211.3
Brazília .. 624.5 533.7 495.1 538.4 714.9 881.5 1,021.4 1,163.1 1,204.4 1,666.7 1,881.1 1,919.5 1,858.5 1,851.6 1,620.3 1,625.3 1,817.8 ..
Egyesült Államok 11,100.4 11,815.3 11,565.6 10,129.3 9,818.9 10,478.8 11,002.4 10,545.0 10,003.3 10,359.1 11,308.8 11,165.6 12,606.6 12,638.2 13,189.1 16,421.2 16,900.5 17,248.3 17,353.2
Kanada 807.0 825.5 804.5 793.3 826.2 942.2 1,051.2 1,074.8 1,062.6 991.4 1,220.5 1,289.2 1,422.8 1,390.1 1,360.7 1,403.1 1,383.6 1,462.1 1,450.9
Mexikó 766.2 844.5 813.7 644.2 628.9 705.0 776.3 768.3 758.3 647.0 798.6 848.3 935.8 959.5 989.7 1,053.1 973.4 1,027.8 1,036.9
Ausztrália 443.7 435.5 461.0 495.4 545.6 610.6 651.9 720.2 723.6 734.0 982.2 1,112.0 1,238.3 1,160.3 1,103.6 1,124.9 1,185.6 1,254.2 1,227.3
Új-Zéland 59.1 60.4 66.4 74.3 82.5 92.3 88.8 100.3 91.3 87.8 110.7 121.2 137.1 143.6 151.4 159.5 169.9 179.4 173.6
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.y.axis path {
display: none;
}
g.legend {
cursor: pointer;
}
</style>
<body>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script>
const
title = "A GDP nagysága (folyó áron) – ESA2010 (2000–) [milliárd euró]",
legend = {width: 100, height: 16 * 50},
margin = {top: 20, right: 20, bottom: 30, left: 40},
width = document.body.clientWidth - margin.left - margin.right - legend.width,
height = Math.max(document.documentElement.clientHeight - margin.top - margin.bottom, legend.height - margin.top - margin.bottom),
y = d3.scaleLinear().range([height, 0]),
x = d3.scaleBand().range([0, width]),
colorScale = d3.scaleBand().range([0, 1]),
colors = x => d3.interpolateSinebow(colorScale(x)),
xAxis = d3.axisBottom().scale(x),
yAxis = d3.axisLeft().scale(y).tickFormat(d3.format(".2s")),
isNumber = v => Number.isFinite(Number(v)),
rowKeys = data => Object.keys(data).filter(isNumber),
sanitize = str => typeof str == 'string' ? str.replace(/,/g, '') : str,
rowVals = rowData => Object.values(rowData).map(sanitize).filter(isNumber),
countries = data => data.map(d => d.country).sort((a, b) => a.localeCompare(b, 'en', {sensitivity: 'base'})),
maxVal = data => {
const rowMaxArr = data.map(d => Math.max(...rowVals(d)));
return Math.max(...rowMaxArr);
};
makeInvertable = (axis, t) => {
axis.invert = x => {
const domain = axis.domain(),
range = axis.range(),
invertScale = t ? t.domain(range).range(domain) :
d3.scaleQuantize().domain(range).range(domain);
return invertScale(x);
};
};
makeInvertable(x);
makeInvertable(y, d3.scaleLinear());
const svgNode = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right + legend.width)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`);
svgNode.append('text')
.text(title);
drawHower = d => {
hoverNode.attr('transform', `translate(${d.x || 0},${d.y || 0})`);
hoverNode.select('text.value').text(`${d.value ? d.value.toFixed(2) : ''}`);
hoverNode.select('text.country').text(`${d.country || ''}`);
hoverNode.selectAll('line')
.data([d])
.join('line')
.attr('x1', 0)
.attr('x2', 0)
.attr('y1', 0)
.attr('y2', d.y ? height - d.y : 0)
.attr('style', d => `stroke:${colors(d.country)}`);
};
// global data
let legendData = null;
let labels;
// fix nodes
let linesNode = svgNode.append('g'),
hoverNode = svgNode.append('g')
.attr('class', 'hover'),
legendNode;
hoverNode.append('text').attr('class', 'value');
hoverNode.append('text').attr('class', 'country').attr('transform', `translate(0,10)`);
const selectedCountriesData = d => {
const selected = legendData.filter(l => l.selected).map(l => l.name);
return d.filter(d => selected.includes(d.country));
};
const draw = (data) => {
// Initially generate legendData
if (!legendData) {
legendData = countries(data).map(d => ({name: d, selected: true}));
legendData.push({name: 'All'})
labels = rowKeys(data[0]);
x.domain(labels);
colorScale.domain(countries(data));
legendNode = svgNode.append('g')
.attr('class', 'legend')
.attr('transform', `translate(${width},0)`);
}
// data -> all the countries
// filteredData -> countries selected on legend
const filteredData = selectedCountriesData(data);
y.domain([0, maxVal(filteredData)]); // always recalc
// Always redraw Y axis REMOVE + APPEND
svgNode.selectAll('g.axis').remove();
svgNode.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".7em")
.style("text-anchor", "end");
svgNode.append("g")
.attr("class", "x axis")
.attr("transform", `translate(${-x.bandwidth() / 2},${height})`)
.call(xAxis);
const linesJoin = linesNode
.selectAll('path')
.data(filteredData);
// JOIN lifecycle
linesJoin
.join('path')
.attr('style', d => `fill:none;stroke:${colors(d.country)};stroke-width:2`)
.attr('country', d => d.country)
.datum(d => rowVals(d).map(v => ({
value: v
})
))
.attr('d',
d3.line()
.curve(d3.curveMonotoneX)
.x((v, i) => x(labels[i]))
.y(v => y(v.value)))
.on('mouseover', function (d) {
const [x1, y1] = d3.mouse(this);
drawHower({
country: d3.select(this).attr('country'),
date: x.invert(x1),
value: y.invert(y1),
x: x1,
y: y1
});
});
const drawLegend = legendData => {
const legendCountriesJoin = legendNode.selectAll('.country')
.data(legendData);
/*
* WHEN legendCountriesJoin ENTERS new data
* */
const country = legendCountriesJoin
.enter()
.append('g')
.attr('class', 'country')
.attr('transform', (d, i) => `translate(0,${i * 15})`);
/**
* WHEN legendCountriesJoin UPDATES data
*/
legendCountriesJoin
.join()
.select('polyline')
.attr('style', d => `fill:${d.selected ? colors(d.name) : 'none'};stroke:black;stroke-width:1`);
country.on('click', d => {
if (d.name == 'All') {
legendData.forEach(d => d.selected = !d.selected)
} else {
d.selected = !d.selected;
}
drawHower({});
draw(data);
});
country.append('polyline')
.attr('points', '5,0 10,5 5,10 0,5 5,0')
.attr('style', d => `fill:${d.selected ? colors(d.name) : 'none'};stroke:black;stroke-width:1`)
.attr('transform', 'translate(0,-8)');
country.append('text')
.text(d => d.name)
.attr('transform', 'translate(15,0)');
//https://stackoverflow.com/questions/30617719/why-d3-updates-entire-data
function key(d, i, j) {
console.log((Array.isArray(this) ? "data\t" : "node\t") + d);
return i;
}
};
drawLegend(legendData);
};
// Let's kick-off the party
d3.csv("gdp_world.csv").then(draw);
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment