A line chart of Canadian Income tax [work in progress].
xxxxxxxxxx
<meta charset="utf-8">
<html>
<head>
<title></title>
<link href='https://fonts.googleapis.com/css?family=Anonymous+Pro:400,400italic,700,700italic' rel='stylesheet' type='text/css'>
<style>
html {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
font: 12px sans-serif;
margin-left: 5%;
margin-right: 5%;
}
body {
width: 90%;
font-family: Arial, sans-serif;;
}
text {
font-family: 'Anonymous Pro', Arial, sans-serif;
font-size: 15px;
font-weight: 700;
}
#menu {
text-align: left;
line-height: 1.5em;
}
#menu select {
border: none;
background-color: white;
background-color: rgba(103, 103, 103, 0.09);
}
#menu, #menu select {
font-size: 40px;
}
.axis path, .axis line {
fill: none;
stroke: #C2C2C2;
stroke-width: .5px;
stroke-linejoin: round;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
stroke-linecap: round;
}
.line:hover {
stroke-width: 2;
stroke: steelblue;
}
.highlighted, .previous {
stroke: #267129;
stroke-width: 3;
stroke-dasharray: 5,5;
}
.previous {
stroke: red;
}
.highlighted {
stroke: #267129;
}
.bgline {
stroke: rgba(103, 103, 103, 0.09);
}
.ylabel {
font-size: 1.5em;
}
.chart {
width: 100%;
height: 100%;
}
</style>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="d3kit.min.js" charset="utf-8"></script>
<script src="kodama.js" charset="utf-8"></script>
<script src="underscore-min.js" charset="utf-8"></script>
</head>
<body>
<p id="menu">
How have income taxes in
<select id="provMenu">
<option value="ab">Alberta</option>
<option value="bc">British Columbia</option>
<option value="mb">Manitoba</option>
<option value="nb">New Brunswick</option>
<option value="nl">Newfoundland and Labrador</option>
<option value="nt">Northwest Territories</option>
<option value="ns">Nova Scotia</option>
<option value="nu">Nunavut</option>
<option value="on">Ontario</option>
<option value="pe">Prince Edward Island</option>
<option value="qc">Quebec</option>
<option value="sk">Saskatchewan</option>
<option value="yt">Yukon</option>
</select><br>
changed since
<select id="yearMenu">
<option value="2015">2015</option>
<option value="2014">2014</option>
</select> ?
</p>
<div class="chart"></div>
<script>
d3.json("avgTaxAll.json", function(error, data) {
if (error) throw error;
var provChange = function() {
var prov = provMenu.property("value");
var currYear = yearMenu.property("value");
var singleProvHi = provinces["2016"].filter(function(d){
return d.name == prov;
});
var singleProvPrev = provinces[currYear].filter(function(d){
return d.name == prov;
});
chart.getRootG()
.selectAll(".highlighted")
.data(singleProvHi)
.call(highlighted.update);
chart.getRootG()
.selectAll(".previous")
.data(singleProvPrev)
.call(previous.update);
};
var provMenu = d3.select("#provMenu").on("change", provChange);
var yearMenu = d3.select("#yearMenu").on("change", provChange);
var onResize = function() {
xScale.range([0, chart.getInnerWidth()]);
yScale.range([chart.getInnerHeight(), 0]);
chart.getRootG()
.select(".x")
.transition()
.duration(1000)
.attr("transform", "translate(0, " + chart.getInnerHeight() + ")")
.call(xAxis);
chart.getRootG()
.select(".y")
.transition()
.duration(1000)
.call(yAxis);
chart.getRootG()
.select(".ylabel")
.transition()
.duration(1000)
.attr("x", -1 * chart.getInnerHeight() / 2 + 100);
chart.getRootG().selectAll('.province')
.call(lines.update);
chart.getRootG().selectAll('.highlighted')
.call(highlighted.update);
chart.getRootG().selectAll('.previous')
.call(previous.update);
};
var onData = function(newData) {
if (chart.hasData()) {
var chartLines = chart.getRootG()
.selectAll(".province")
.data(newData)
.enter()
.append("g")
.classed("province", true)
.call(lines.enter);
var prov = provMenu.property("value");
var currYear = yearMenu.property("value");
console.lo
var singleProvHi = provinces["2016"].filter(function(d){
return d.name == prov;
});
var singleProvPrev = provinces[currYear].filter(function(d){
return d.name == prov;
});
var highlightProv = chart.getRootG()
.selectAll(".highlightGroup")
.data(singleProvHi)
.enter()
.append("g")
.classed("highlightGroup", true)
.call(highlighted.enter);
var prevProv = chart.getRootG()
.selectAll(".previousGroup")
.data(singleProvPrev)
.enter()
.append("g")
.classed("previousGroup", true)
.call(previous.enter);
onResize();
provChange();
}
};
var DEFAULT_OPTIONS = {
margin: {top: 50, right: 50, bottom: 50, left: 80},
initialHeight: 500,
initialWidth: 1080
};
var chart = new d3Kit.Skeleton('.chart', DEFAULT_OPTIONS)
.autoResize('full')
.on('resize', onResize)
.on('data', onData);
var xScale = d3.scale.linear().range([0, chart.getInnerWidth()]);
var yScale = d3.scale.linear().range([chart.getInnerHeight(), 0]);
// Kodama theme
var ccTheme =
{
frame: {
padding: '4px',
background: 'rgba(103, 103, 103, 0.09)',
'font-family': '"Anonymous Pro", Arial, sans-serif',
'border': 'none',
color: 'black',
'border-radius': '4px',
'font-size': '26px',
'box-shadow': 'none'
},
title: {'text-align': 'center', 'padding': '4px'},
item_title: {'text-align': 'right', 'color': 'rgb(220,200,120)'},
item_value: {'padding': '1px 2px 1px 10px', 'color': 'rgb(234, 224, 184)'},
options: null
};
d3.kodama.themeRegistry('ccTheme', ccTheme);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(d3.format('$,.d'));
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.tickFormat(d3.format('.0%'));
var lineGen = d3.svg.line()
.interpolate("monotone")
.x(function(d) {
return xScale(d.income);
})
.y(function(d) {
return yScale(d.avgTax);
});
var taxLineChartlet = function() {
var events = [];
var chartlet = d3Kit.Chartlet(enter, update, events);
function enter(selection, done) {
selection.append("path")
.classed("line", true)
.classed("bgline", true)
.attr("d", function(d) {
return lineGen(d.values);
})
.call(d3.kodama.tooltip());
done(selection);
}
function update(selection, done) {
selection.select(".line")
.transition()
.duration(1000)
.attr("d", function(d) {
return lineGen(d.values);
})
.each('end', done);
}
return chartlet;
};
var highlightProv = function() {
var events = [];
var chartlet = d3Kit.Chartlet(enter, update, events);
function enter(selection, done) {
selection.append("path")
.classed("line", true)
.classed("highlighted", true)
.attr("d", function(d) {
return lineGen(d.values);
})
.call(d3.kodama.tooltip());
done(selection);
}
function update(selection, done) {
selection.transition()
.duration(1000)
.attr("d", function(d) {
return lineGen(d.values);
})
.each("end", done);
}
return chartlet;
};
var previousProv = function() {
var events = [];
var chartlet = d3Kit.Chartlet(enter, update, events);
function enter(selection, done) {
selection.append("path")
.classed("line", true)
.classed("previous", true)
.attr("d", function(d) {
return lineGen(d.values);
})
.call(d3.kodama.tooltip());
done(selection);
}
function update(selection, done) {
selection.transition()
.duration(1000)
.attr("d", function(d) {
return lineGen(d.values);
})
.each("end", done);
}
return chartlet;
};
// Create a nice data structue to work with. This
// stores each province's data in a different array
// element with all the data we need to plot it
// Used to convert the longer names to keys for each access later
var provinceLookup = {
"Alberta": "ab",
"British Columbia": "bc",
"Manitoba": "mb",
"New Brunswick": "nb",
"Newfoundland and Labrador": "nl",
"Northwest Territories": "nt",
"Nova Scotia": "ns",
"Nunavut": "nu",
"Ontario": "on",
"Prince Edward Island": "pe",
"Quebec": "qc",
"Saskatchewan": "sk",
"Yukon": "yt"
};
var transformData = function(year, data) {
return data.map(function(each) {
return {
name: provinceLookup[each.key],
values: each.values.map(function(d) {
return {income: d.x, avgTax: d.y};
}),
// These are all used for the Kodama tooltips
title: each.key + " - " + year,
distance: 10,
theme: 'ccTheme'
};
});
};
provinces = _.mapObject(data, function(value, key) {
return transformData(key, value);
});
// Get the max and min values of income
xScale.domain(d3.extent(provinces["2016"][0].values, function(each) {
return each.income;
}));
// Get the global max and min average tax rate from the nested object
yMin = d3.min(provinces["2016"], function(prov) {
return d3.min(prov.values, function(value) {
return value.avgTax;
});
});
yMax = d3.max(provinces["2016"], function(prov) {
return d3.max(prov.values, function(value) {
return value.avgTax;
});
});
yScale.domain([yMin, yMax*1.1]);
// Create the x-axis
chart.getRootG()
.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0, " + chart.getInnerHeight() + ")")
.call(xAxis);
// Create the y-axis and label it
chart.getRootG()
.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -55)
.attr("x", -1 * chart.getInnerHeight() / 2 + 100)
.style("text-anchor", "end")
.text("Average Tax Rate (%)")
.attr("class", "ylabel");
var lines = taxLineChartlet();
var highlighted = highlightProv();
var previous = previousProv();
chart.data(provinces["2016"]);
});
</script>
</body>
</html>
https://d3js.org/d3.v3.min.js