|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
text { |
|
font: 12px sans-serif; |
|
fill: #555; |
|
} |
|
text.title { |
|
font-size: 14px; |
|
} |
|
.calc, |
|
.real { |
|
stroke: white; |
|
stroke-width: .75; |
|
} |
|
.calc { |
|
fill: #ccc; |
|
} |
|
.axis-y line { |
|
stroke: #888; |
|
stroke-dasharray: 2 2; |
|
} |
|
.axis-y .zero { |
|
stroke-dasharray: none; |
|
} |
|
.axis-y path, |
|
.axis-x path { |
|
display: none; |
|
} |
|
</style> |
|
<body> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
var margin = {top: 40, right: 10, bottom: 30, left: 40}, |
|
width = 470 - margin.left - margin.right, |
|
height = 250 - margin.top - margin.bottom; |
|
|
|
var x = d3.scaleBand().rangeRound([0, width]).padding(0.2), |
|
y = d3.scaleLinear().range([height, 0]); |
|
|
|
var palette = { |
|
'Sjálfstæðisflokkur': '#5DA5DA', |
|
'Framsóknarflokkur': '#B276B2', |
|
'Alþýðubandalag': '#B2912F', |
|
'Vinstrihreyfingin - grænt framboð': '#60BD68', |
|
'Alþýðuflokkur': "#F17CB0", |
|
'Samfylkingin': "#F15854", |
|
}; |
|
|
|
var dhont = ( parties, total_seats ) => { |
|
var quot = d => d.percent / ( d._seats + 1 ); |
|
parties.forEach(d => { d._seats = 0; }); |
|
for (var round=0; round<total_seats; round++) { |
|
var top = parties.sort((a, b) => quot(b) - quot(a))[0]; |
|
top._seats++; |
|
} |
|
}; |
|
|
|
var y2d = ( d ) => { |
|
var y = d % 100; |
|
return y < 10 ? `'0${y}` : `'${y}`; |
|
}; |
|
|
|
var rowParse = d => { |
|
d.year = +d.year; |
|
d.percent = (+d.percent)/100; |
|
d.seats = +d.seats; |
|
return d; |
|
}; |
|
|
|
var num_mps = { |
|
1963: 60, 1967: 60, 1971: 60, 1974: 60, |
|
1978: 60, 1979: 60, 1983: 60, 1987: 63, |
|
1991: 63, 1995: 63, 1999: 63, 2003: 63, |
|
2007: 63, 2009: 63, 2013: 63, 2016: 63, |
|
}; |
|
|
|
d3.csv('elections.csv', rowParse, data => { |
|
y.domain([0, 26]); |
|
x.domain(data.map(d => d.year).sort(d3.ascending)); |
|
|
|
// calculate seats as if there was 1 constituency |
|
x.domain().forEach(year => { |
|
dhont(data.filter(d => d.year === year), num_mps[year]); |
|
}); |
|
|
|
plot(['Sjálfstæðisflokkur'], data); |
|
plot(['Framsóknarflokkur'], data); |
|
plot(['Alþýðuflokkur', 'Samfylkingin'], data); |
|
plot(['Alþýðubandalag', 'Vinstrihreyfingin - grænt framboð'], data); |
|
}); |
|
|
|
|
|
function plot (parties, data, polls) { |
|
|
|
var svg = d3.select("body").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})`); |
|
|
|
// axis |
|
svg.append("g") |
|
.attr("class", "axis axis-x") |
|
.attr("transform", `translate(0,${height})`) |
|
.call(d3.axisBottom(x).tickSize(3).tickFormat(y2d)); |
|
svg.append("g") |
|
.attr("class", "axis axis-y") |
|
.call(d3.axisLeft(y).tickSize(-width).ticks(5)) |
|
.selectAll('line') |
|
.attr('class', d => d ? '' : 'zero'); |
|
|
|
// title |
|
svg.append("text") |
|
.attr("class", "title") |
|
.attr("x", width/2) |
|
.attr("y", -18) |
|
.attr("dy", ".32em") |
|
.attr("text-anchor", "middle") |
|
.text(parties.join(' → ')); |
|
|
|
// split to seats |
|
var dt = [], oc = []; |
|
data |
|
.filter(d => parties.indexOf(d.party) !== -1) |
|
.forEach(d => { |
|
for (var i = 0; i < d.seats; i++) { |
|
dt.push({ |
|
party: d.party, |
|
seat: i + 1, |
|
year: d.year, |
|
seats: d.seats, |
|
percent: d.percent, |
|
}); |
|
} |
|
for (var i = 0; i < d._seats; i++) { |
|
oc.push({ |
|
party: d.party, |
|
seat: i + 1, |
|
year: d.year, |
|
seats: d.seats, |
|
percent: d.percent, |
|
}); |
|
} |
|
}); |
|
|
|
var w = x.bandwidth() / 2; |
|
svg.selectAll(".real") |
|
.data(dt).enter() |
|
.append("rect") |
|
.attr("class", "real") |
|
.attr("x", d => x(d.year)) |
|
.attr("y", d => y(d.seat) - 0.5) |
|
.attr("width", w) |
|
.attr("height", d => y(d.seat) - y(d.seat+1)) |
|
.attr("fill", d => palette[d.party]); |
|
|
|
// if there was only 1 constituency |
|
svg.selectAll(".calc") |
|
.data(oc).enter() |
|
.append("rect") |
|
.attr("class", "calc") |
|
.attr("x", d => x(d.year) + w) |
|
.attr("y", d => y(d.seat) - 0.5) |
|
.attr("width", w) |
|
.attr("height", d => y(d.seat) - y(d.seat+1)); |
|
|
|
} |
|
</script> |