Last active
November 5, 2015 18:31
-
-
Save alansmithy/3838b9d7070cd18a7017 to your computer and use it in GitHub Desktop.
'Squeezed'
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Tax credit changes and the low income family</title> | |
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<style type="text/css"> | |
body, text{font-family:metric,sans-serif;} | |
h2{font-weight:500;font-size:21px;margin-bottom:-10px;} | |
body{background-color:#dedede} | |
.axis text{font-size:11px;} | |
.axis path{fill:none;stroke:none;} | |
.axis line{stroke:#555;stroke-width:1px;} | |
.y line{stroke-dasharray:2,2} | |
.area{fill-opacity:0.8;} | |
.source{font-size:11px;fill:#555;} | |
.subtitle{font-weight:500;font-size:18px;} | |
.value{font-weight:500;font-size:18px;} | |
.origin line{stroke:#828778;stroke-dasharray:0,0} | |
#container { | |
width: 600px; | |
margin-left: auto; | |
margin-right: auto; | |
margin-top: 30px; | |
background-color: white; | |
box-shadow: 2px 2px 4px 5px #ccc; | |
} | |
#intro{ | |
padding-left:50px; | |
padding-top:10px; | |
padding-right:50px; | |
} | |
p{color:gray;} | |
</style> | |
</head> | |
<body> | |
<div id="container"> | |
<div id="intro"> | |
<h2>Squeezed: Tax credit changes and the low income family</h2> | |
<p>The House of Lords recently blocked Chancellor George Osborne's plans to make cuts of £4.6bn to income subsidies for people on low incomes, known as tax credits. The government argues that other changes, including an increase in the minimum wage and raising the tax allowance will offset the cuts for most households. But those changes are set to take effect over the coming years, while the tax credit cuts will be imposed from April next year. The graphic below shows House of Commons Library research on how the planned changes would affect a low income family*.</p> | |
</div> | |
</div> | |
<script type="text/javascript"> | |
//Width, height, padding, colours | |
var width = 600; | |
var height = 250; | |
var padding = {left:50,right:80,top:30,bottom:50}; | |
var w = width-(padding.left+padding.right); | |
var h = height-(padding.top+padding.bottom); | |
var colour = ["#5999B3","#D9AB44","#0079A1"]; | |
var labelColour = ["#666","#111","#fff"]; | |
var labelPlacement = [[[280,-30],[280,5],[280,70]],[[280,-18],[280,5],[280,70]]]; | |
var source="Source: House of Commons Library; *Single-earner couple or lone parent, with 2 children, working 35 hrs a week"; | |
//should load this from a csv and create this structure | |
var data=[]; | |
var desc=["Without budget changes applied","With budget changes applied"]; | |
//ignoring budget | |
data[0] = [ | |
{ | |
label: "child benefit", | |
value: [ | |
{ x: "2016", y: 1794 }, | |
{ x: "2017", y: 1794 }, | |
{ x: "2018", y: 1815 }, | |
{ x: "2019", y: 1846 }, | |
{ x: "2020", y: 1880 }, | |
{ x: "2021", y: 1914 } | |
] | |
}, | |
{ | |
label: "tax credit", | |
value: [ | |
{ x: "2016", y: 8504 }, | |
{ x: "2017", y: 8280 }, | |
{ x: "2018", y: 8223 }, | |
{ x: "2019", y: 8183 }, | |
{ x: "2020", y: 8131 }, | |
{ x: "2021", y: 8132 } | |
] | |
}, | |
{ | |
label: "net earnings", | |
value: [ | |
{ x: "2016", y: 11401 }, | |
{ x: "2017", y: 11774 }, | |
{ x: "2018", y: 12116 }, | |
{ x: "2019", y: 12539 }, | |
{ x: "2020", y: 13026 }, | |
{ x: "2021", y: 13693 } | |
] | |
} | |
] | |
//data with budget applied | |
data[1] = [ | |
{ | |
label: "child benefit", | |
value: [ | |
{ x: "2016", y: 1794 }, | |
{ x: "2017", y: 1794 }, | |
{ x: "2018", y: 1794 }, | |
{ x: "2019", y: 1794 }, | |
{ x: "2020", y: 1794 }, | |
{ x: "2021", y: 1828 } | |
] | |
}, | |
{ | |
label: "tax credit", | |
value: [ | |
{ x: "2016", y: 8504 }, | |
{ x: "2017", y: 6426 }, | |
{ x: "2018", y: 5988 }, | |
{ x: "2019", y: 5550 }, | |
{ x: "2020", y: 5068 }, | |
{ x: "2021", y: 4753 } | |
] | |
}, | |
{ | |
label: "net earnings", | |
value: [ | |
{ x: "2016", y: 11402 }, | |
{ x: "2017", y: 12102 }, | |
{ x: "2018", y: 12769 }, | |
{ x: "2019", y: 13441 }, | |
{ x: "2020", y: 14179 }, | |
{ x: "2021", y: 15120 } | |
] | |
} | |
]; | |
//Set up stack method | |
var stackIndex=0; | |
var stack = d3.layout.stack() | |
.values(function(d) { | |
return d.value; | |
}) | |
.order("reverse"); | |
//'stack' the data [inject y0 components]; | |
stack(data[0]); | |
stack(data[1]); | |
//extract and parse dates | |
var parseDate = d3.time.format("%Y").parse; | |
var dates = []; | |
data[0].forEach(function(obj,i){ | |
obj.value.forEach(function(vals,i){ | |
vals.x=parseDate(vals.x); | |
dates.push(vals.x); | |
}) | |
}) | |
data[1].forEach(function(obj,i){ | |
obj.value.forEach(function(vals,i){ | |
vals.x=parseDate(vals.x); | |
}) | |
}) | |
//determine max value - need to do this for both | |
var maxValue = 0; | |
data[0].forEach(function(obj,i){ | |
obj.value.forEach(function(vals,i){ | |
maxValue = d3.max([maxValue,(vals.y+vals.y0)]); | |
}) | |
}) | |
//create svg | |
var svg = d3.select("#container").append("svg") | |
.attr("width",width) | |
.attr("height",height) | |
//title etc | |
svg.append("text") | |
.attr("class","source") | |
.attr("x",padding.left) | |
.attr("y",height-10) | |
.text(source); | |
svg.append("text") | |
.attr("id","subtitle") | |
.attr("class","subtitle") | |
.attr("x",padding.left) | |
.attr("y",15) | |
.text(desc[stackIndex]); | |
//Set up scales | |
var xScale = d3.time.scale() | |
.domain(d3.extent(dates)) | |
.range([0,w]); | |
var yScale = d3.scale.linear() | |
.domain([0,maxValue]) | |
.range([h,0]); | |
//Axis generators | |
var xAxis = d3.svg.axis() | |
.scale(xScale) | |
.orient("bottom") | |
var yAxis = d3.svg.axis() | |
.scale(yScale) | |
.orient("left") | |
.tickSize(-w) | |
.ticks(5); | |
//Create axes | |
svg.append("g") | |
.attr("class", "axis x") | |
.attr("transform","translate("+padding.left+","+(height-padding.bottom+5)+")") | |
.call(xAxis); | |
var yAxis = svg.append("g") | |
.attr("class", "axis y") | |
.attr("transform","translate("+padding.left+","+padding.top+")") | |
.call(yAxis); | |
//style 0 line | |
yAxis.selectAll(".tick").filter(function(d, i) { | |
return d==0; | |
}).classed('origin',function(d,i){ | |
return (d == 0); | |
}); | |
//Configure area generator | |
var area = d3.svg.area() | |
.x(function(d) { | |
return xScale(d.x); | |
}) | |
.y0(function(d) { | |
return yScale(d.y0); | |
}) | |
.y1(function(d) { | |
return yScale(d.y0 + d.y); | |
}); | |
//Make a path for each element | |
var pathGrp = svg.append("g") | |
.attr("transform","translate("+padding.left+","+padding.top+")"); | |
var paths = pathGrp.selectAll("path") | |
.data(data[stackIndex]) | |
.enter() | |
.append("path") | |
.attr("class", "area") | |
.attr("d", function(d) { | |
return area(d.value); | |
}) | |
.attr("stroke", "none") | |
.attr("fill", function(d, i) { | |
return colour[i]; | |
}); | |
//labels for stacks | |
var labelGrp = svg.append("g") | |
.attr("transform","translate("+padding.left+","+padding.right+")") | |
var labels = labelGrp.selectAll("text") | |
.data(data[stackIndex]) | |
.enter() | |
.append("text") | |
.attr("text-anchor","middle") | |
.attr("fill",function(d,i){return labelColour[i]}) | |
.attr("x",function(d,i){return labelPlacement[stackIndex][i][0]}) | |
.attr("y",function(d,i){return labelPlacement[stackIndex][i][1]}) | |
.text(function(d){return d.label;}) | |
//annotate totals | |
var commaFormat = d3.format(','); | |
var total = []; | |
total[0] = data[0][0].value[5].y0+data[0][0].value[5].y; | |
total[1] = data[1][0].value[5].y0+data[1][0].value[5].y; | |
var label = svg.append("g").attr("id","anno") | |
.attr("transform","translate("+padding.left+","+padding.top+")") | |
.append("text") | |
.attr("class","value") | |
.attr("x",function(){return 10+xScale(dates[dates.length-1])}) | |
.attr("y",function(){return 5+yScale(total[stackIndex])}) | |
.text("£"+commaFormat(total[stackIndex])) | |
var marker = d3.select("#anno").append("circle") | |
.attr("cx",function(){return xScale(dates[dates.length-1])}) | |
.attr("cy",function(){return yScale(total[stackIndex])}) | |
.attr("r",5) | |
.attr("fill","red"); | |
function changeStack(index){ | |
//rebind data and transition | |
paths.datum(function(d,i){ | |
return data[index][i]; | |
}) | |
.transition() | |
.duration(1000) | |
.attr("d", function(d) { | |
return area(d.value); | |
}) | |
.each("end", function(){ | |
d3.select("#subtitle").text(desc[stackIndex]); | |
}); | |
label.transition() | |
.duration(1000) | |
.attr("y",function(){ | |
return 5+yScale(total[stackIndex]) | |
}) | |
.each("end", function(){ | |
label.text("£"+commaFormat(total[stackIndex])) | |
}); | |
marker.transition() | |
.duration(1000) | |
.attr("cy",function(){ | |
return yScale(total[stackIndex]) | |
}) | |
labels.transition() | |
.duration(1000) | |
.attr("y",function(d,i){return labelPlacement[stackIndex][i][1]}) | |
} | |
setInterval(function(){ | |
if (stackIndex==0){ | |
stackIndex=1; | |
} else { | |
stackIndex=0; | |
}; | |
changeStack(stackIndex) | |
}, 3000); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment