Population growth in European cities/districts, 2003-2012.
Data from eurostat.
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
font: 16px/1.5 georgia, serif;
-webkit-font-smoothing: antialiased;
-webkit-hyphens: auto;
padding: 30px;
}
svg { padding: 10px; }
.axis path,
.axis line {
fill: none;
stroke: gray;
shape-rendering: crispEdges;
}
.axis text {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 0.7em;
}
.x.axis path {
display: none;
}
.area {
fill: #d1e5f0;
}
.overlay {
fill: none;
pointer-events: all;
}
.label {
fill: black;
opacity: 0.6;
font-size: 1.1em !important;
}
.line {
fill: none;
stroke: #7a868c;
}
.positive { color: #00441b; }
.negative { color: #67001f; }
.muted { color: #bababa; }
hr { opacity: 0.5; width: 98%; }
</style>
<body>
<aside>
<select>
<option value='lines'>Lines</option>
<option value='table'>Percent change (table)</options>
</select>
</aside>
<div class="charts">
<div>
<label>
Unify scales
<input autofocus type="checkbox" id="toggle-scale">
</label>
</div>
</div>
<div class="table"></table>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script>
d3.select('select').on('change', function () {
var wantsTable = this.value === 'table';
d3.select('.table').style('display', wantsTable ? null : 'none');
d3.select('.charts').style('display', wantsTable ? 'none' : null);
})
var margin = { top: 20, right: 20, bottom: 30, left: 80 },
width = 300 - margin.left - margin.right,
height = 300 - margin.bottom - margin.top;
var x = d3.time.scale().range([0, width]);
var parseDate = d3.time.format('%Y').parse;
var toggle = d3.select('#toggle-scale');
var formatDate = d3.time.format('%Y');
var formatPercent = d3.format(',.2f');
var xAxis = d3.svg.axis()
.scale(x)
.ticks(3)
.orient('bottom');
d3.csv('data.csv', function(error, data) {
data = parseData(data);
var totalDomain = d3.extent(d3.merge(data.map(function (d) {
return d.values.map(function (e) { return e.value; })
})));
renderTable(data);
data.forEach(function (c) {
var localDomain = d3.extent(c.values, function (d) { return d.value; });
var y = d3.scale.linear().range([height, 0]);
var yAxis = d3.svg.axis()
.scale(y)
.orient('left')
.ticks(5)
.tickFormat(function (d) { return d3.format(',s')(d); });
var area = d3.svg.area()
.x(function (d) { return x(d.date); })
.y0(height)
.y1(function (d) { return y(d.value); })
.defined(function (d) { return !isNaN(d.value); });
var line = d3.svg.line()
.x(function (d) { return x(d.date); })
.y(function (d) { return y(d.value); })
.defined(function (d) { return !isNaN(d.value); });;
var startArea = d3.svg.area()
.x(function (d) { return x(d.date); })
.y0(height)
.y1(function (d) { return y(y.domain()[0]); });
var startLine = d3.svg.line()
.x(function (d) { return x(d.date); })
.y(height);
x.domain(d3.extent(c.values, function (d) { return d.date; }));
y.domain(localDomain);
var svg = d3.select('.charts').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
svg.datum(c.values, function (d) { d.date; });
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(xAxis);
svg.append('path')
.attr('class', 'area')
.attr('d', startArea)
.transition()
.attr('d', area)
.duration(1000);
svg.append('path')
.attr('class', 'line')
.attr('d', startLine)
.transition()
.attr('d', line)
.duration(1000);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis)
.append('text')
.attr('class', 'label')
.attr('x', 6)
.attr('y', 6)
.attr('dy', '.71em')
.text(c.code);
toggle.on('click.' + c.code, function () {
var newDomain = JSON.stringify(y.domain()) == JSON.stringify(localDomain) ? totalDomain : localDomain;
y.domain(newDomain);
svg.selectAll('g.y.axis')
.transition()
.call(yAxis)
svg.selectAll('path.area')
.transition()
.attr('d', area)
.ease('elastic')
.duration(1000);
svg.selectAll('path.line')
.transition()
.attr('d', line)
.ease('elastic')
.duration(1000);
});
});
})
function parseData(data) {
var map = d3.map({});
var parseYear = d3.time.format('%Y').parse;
data.forEach(function (row) {
var city = row.CITIES;
var e = {
value: +row.Value.replace(/\s/g, ""),
date: parseYear(row.TIME),
};
if (map.has(city)) {
map.get(city).push(e)
} else {
map.set(city, [e])
}
});
return map.entries().map(function (e) {
return {code: e.key, values: e.value}
}).filter(function (e) {
var len = e.values.filter(function (d) { return !isNaN(d.value); }).length;
return len > 5 && e.values[len - 1].value > 15e4;
}).sort(function (a, b) {
return d3.ascending(a.code, b.code)
})
}
function percentChange(values) {
return d3.pairs(values).map(function (pair) {
var left = pair[0].value,
right = pair[1].value;
return ((right - left) / left) * 100;
})
}
function renderTable(data) {
var headers = d3.set(d3.merge(data.map(function (d) {
return d.values.map(function (e) { return formatDate(e.date); });
}))).values();
headers.unshift('City')
headers.push('Acc.')
var table = d3.select('.table')
.style('display', 'none')
.append('table')
table.append('thead');
table.append('tbody');
table.select('thead').append('tr').selectAll('th')
.data(headers)
.enter().append('th')
.text(String)
var row = table.select('tbody').selectAll('tr')
.data(data)
.enter().append('tr')
row.append('th').text(function (d) { return d.code; })
row.selectAll('td')
.data(function (d) {
var changes = percentChange(d.values);
var sum = d3.sum(changes);
changes.unshift(NaN)
changes.push(sum);
return changes;
})
.enter().append('td')
.attr('class', function (d) {
if (isNaN(d)) {
return 'muted';
} else if (d > 0) {
return 'positive';
} else if (d < 0) {
return 'negative';
}
})
.text(function (d) { return formatPercent(d); })
}
</script>
</body>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js