Last active
June 20, 2016 15:12
-
-
Save soloincc/fe4f2e03ebae1131eba3ffdee44a3478 to your computer and use it in GitHub Desktop.
A standalone script for visualizing Kenya's 2016/17 revenue estimates
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> | |
<!-- | |
This is a standalone script for visualizing Kenya's 2016/17 revenue estimates. | |
The script has been adapted from Iinteractive Donut Charts, http://bl.ocks.org/erichoco/6694616, by http://bl.ocks.org/erichoco | |
Copyright (C) 2016 Wangoru Kihara, soloincc@gmail.com | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <http://www.gnu.org/licenses/>. | |
--> | |
<meta charset="utf-8"> | |
<style type="text/css"> | |
body { | |
font-size: 100%; | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
width: 800px; | |
} | |
</style> | |
<body> | |
<div id="donut-charts"></div> | |
<script src="https://code.jquery.com/jquery-1.10.1.min.js"></script> | |
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script> | |
$(function() { | |
var donutData = [ | |
{ | |
"Details": "Appropriations-in-Aid", | |
"2014_2015": 76, | |
"2015_2016": 111, | |
"2016_2017": 124.2 | |
}, | |
{ | |
"Details": "Foreign Governments Grants", | |
"2014_2015": 13.1, | |
"2015_2016": 22, | |
"2016_2017": 20.5 | |
}, | |
{ | |
"Details": "International Organizations Grants", | |
"2014_2015": 14.8, | |
"2015_2016": 54.6, | |
"2016_2017": 52 | |
}, | |
{ | |
"Details": "Domestic Lending Repayments", | |
"2014_2015": 2.9, | |
"2015_2016": 2.6, | |
"2016_2017": 4 | |
}, | |
{ | |
"Details": "Domestic Borrowing", | |
"2014_2015": 251.1, | |
"2015_2016": 191.1, | |
"2016_2017": 241 | |
}, | |
{ | |
"Details": "Foreign Governments Borrowing", | |
"2014_2015": 30.3, | |
"2015_2016": 6.7, | |
"2016_2017": 8.4 | |
}, | |
{ | |
"Details": "Foreign Commercial loans", | |
"2014_2015": 75, | |
"2015_2016": 154.3, | |
"2016_2017": 153.8 | |
}, | |
{ | |
"Details": "Social Safetynet Budget Support (Loan)", | |
"2014_2015": 3.5, | |
"2015_2016": 8.2, | |
"2016_2017": 3.9 | |
}, | |
{ | |
"Details": "Borrowing from Foreign Governments", | |
"2014_2015": 127.8, | |
"2015_2016": 204.8, | |
"2016_2017": 216.6 | |
}, | |
{ | |
"Details": "Borrowing - International Organizations", | |
"2014_2015": 60, | |
"2015_2016": 133.2, | |
"2016_2017": 120.4 | |
} | |
]; | |
var formattedData = formatData(donutData); | |
var donuts = new DonutCharts(); | |
donuts.create(formattedData); | |
function DonutCharts() { | |
var charts = d3.select('#donut-charts'); | |
var chart_m, chart_r; | |
var color = d3.scale.category20(); | |
charts.append("div") | |
.attr("width", '100%') | |
.attr("text-anchor", "middle") | |
.style("font-size", "18px") | |
.style("text-align", "center") | |
.style("text-decoration", "underline") | |
.text("Grants, Aid and Borrowings Breakdown"); | |
var getCatNames = function(dataset) { | |
var catNames = new Array(); | |
for (var i = 0; i < dataset[0].data.length; i++) { | |
catNames.push(dataset[0].data[i].cat); | |
} | |
return catNames; | |
} | |
var createLegend = function (catNames) { | |
var legends = charts.select('.legend') | |
.selectAll('g') | |
.data(catNames) | |
.enter().append('g') | |
.attr("class", "legendQuant") | |
.attr('transform', function (d, i) { | |
// we want 3 cols of 300px each | |
var top = 20 + Math.floor(i/3)*20; | |
var left = 20 + ((i+1)%3)*240; | |
return 'translate('+ left +', '+ top +')'; | |
}); | |
legends.append('circle') | |
.attr('class', 'legend-icon') | |
.attr('r', 6) | |
.style('fill', function (d, i) { | |
return color(i); | |
}); | |
legends.append('text') | |
.attr('dx', '1em') | |
.attr('dy', '.3em') | |
.text(function (d) { | |
return d; | |
}); | |
} | |
var createCenter = function (pie) { | |
var eventObj = { | |
'mouseover': function (d, i) { | |
d3.select(this) | |
.transition() | |
.attr("r", chart_r * 0.65); | |
}, | |
'mouseout': function (d, i) { | |
d3.select(this) | |
.transition() | |
.duration(500) | |
.ease('bounce') | |
.attr("r", chart_r * 0.6); | |
}, | |
'click': function (d, i) { | |
var paths = charts.selectAll('.clicked'); | |
pathAnim(paths, 0); | |
paths.classed('clicked', false); | |
resetAllCenterText(); | |
} | |
} | |
var donuts = d3.selectAll('.donut'); | |
// The circle displaying total data. | |
donuts.append("svg:circle") | |
.attr("r", chart_r * 0.6) | |
.style("fill", "#E7E7E7") | |
.on(eventObj); | |
donuts.append('text') | |
.attr('class', 'center-txt type') | |
.attr('y', chart_r * -0.16) | |
.attr('text-anchor', 'middle') | |
.style('font-weight', 'bold') | |
.text(function (d, i) { | |
return d.type; | |
}); | |
donuts.append('text') | |
.attr('class', 'center-txt value') | |
.attr('text-anchor', 'middle'); | |
donuts.append('text') | |
.attr('class', 'center-txt percentage') | |
.attr('y', chart_r * 0.16) | |
.attr('text-anchor', 'middle') | |
.style('fill', '#A2A2A2'); | |
} | |
var setCenterText = function (thisDonut) { | |
var sum = d3.sum(thisDonut.selectAll('.clicked').data(), function (d) { | |
return d.data.val; | |
}); | |
thisDonut.select('.value') | |
.text(function (d) { | |
return (sum) ? sum.toFixed(1) + d.unit | |
: d.total.toFixed(1) + d.unit; | |
}); | |
thisDonut.select('.percentage') | |
.text(function (d) { | |
return (sum) ? (sum / d.total * 100).toFixed(2) + '%' | |
: ''; | |
}); | |
} | |
var resetAllCenterText = function () { | |
charts.selectAll('.value') | |
.text(function (d) { | |
return d.total.toFixed(1) + d.unit; | |
}); | |
charts.selectAll('.percentage') | |
.text(''); | |
} | |
var pathAnim = function (path, dir) { | |
switch (dir) { | |
case 0: | |
path.transition() | |
.duration(500) | |
.ease('bounce') | |
.attr('d', d3.svg.arc() | |
.innerRadius(chart_r * 0.7) | |
.outerRadius(chart_r) | |
); | |
break; | |
case 1: | |
path.transition() | |
.attr('d', d3.svg.arc() | |
.innerRadius(chart_r * 0.7) | |
.outerRadius(chart_r * 1.08) | |
); | |
break; | |
} | |
} | |
var updateDonut = function () { | |
var eventObj = { | |
'mouseover': function (d, i, j) { | |
pathAnim(d3.select(this), 1); | |
var thisDonut = charts.select('.type' + j); | |
thisDonut.select('.value').text(function (donut_d) { | |
return d.data.val.toFixed(1) + donut_d.unit; | |
}); | |
thisDonut.select('.percentage').text(function (donut_d) { | |
return (d.data.val / donut_d.total * 100).toFixed(2) + '%'; | |
}); | |
}, | |
'mouseout': function (d, i, j) { | |
var thisPath = d3.select(this); | |
if (!thisPath.classed('clicked')) { | |
pathAnim(thisPath, 0); | |
} | |
var thisDonut = charts.select('.type' + j); | |
setCenterText(thisDonut); | |
}, | |
'click': function (d, i, j) { | |
var thisDonut = charts.select('.type' + j); | |
if (0 === thisDonut.selectAll('.clicked')[0].length) { | |
thisDonut.select('circle').on('click')(); | |
} | |
var thisPath = d3.select(this); | |
var clicked = thisPath.classed('clicked'); | |
pathAnim(thisPath, ~~(!clicked)); | |
thisPath.classed('clicked', !clicked); | |
setCenterText(thisDonut); | |
} | |
}; | |
var pie = d3.layout.pie() | |
.sort(null) | |
.value(function (d) { | |
return d.val; | |
}); | |
var arc = d3.svg.arc() | |
.innerRadius(chart_r * 0.7) | |
.outerRadius(function () { | |
return (d3.select(this).classed('clicked')) ? chart_r * 1.08 | |
: chart_r; | |
}); | |
// Start joining data with paths | |
var paths = charts.selectAll('.donut') | |
.selectAll('path') | |
.data(function (d, i) { | |
return pie(d.data); | |
}); | |
paths | |
.transition() | |
.duration(1000) | |
.attr('d', arc); | |
paths.enter() | |
.append('svg:path') | |
.attr('d', arc) | |
.style('fill', function (d, i) { | |
return color(i); | |
}) | |
.style('stroke', '#FFFFFF') | |
.on(eventObj) | |
paths.exit().remove(); | |
resetAllCenterText(); | |
} | |
this.create = function (dataset) { | |
var $charts = $('#donut-charts'); | |
chart_m = $charts.innerWidth() / dataset.length / 2 * 0.14; | |
chart_r = $charts.innerWidth() / dataset.length / 2 * 0.85; | |
charts.append('svg') | |
.attr('class', 'legend') | |
.attr('width', '100%') | |
.attr('font-size', '0.8em') | |
.attr('height', 100) | |
.attr('transform', 'translate(0, -100)'); | |
var donut = charts.selectAll('.donut') | |
.data(dataset) | |
.enter().append('svg:svg') | |
.attr('width', (chart_r + chart_m) * 2) | |
.attr('height', (chart_r + chart_m) * 2) | |
.append('svg:g') | |
.attr('class', function (d, i) { | |
return 'donut type' + i; | |
}) | |
.attr('transform', 'translate(' + (chart_r + chart_m) + ',' + (chart_r + chart_m) + ')'); | |
createLegend(getCatNames(dataset)); | |
createCenter(); | |
updateDonut(); | |
} | |
this.update = function (dataset) { | |
// Assume no new categ of data enter | |
var donut = charts.selectAll(".donut") | |
.data(dataset); | |
updateDonut(); | |
} | |
} | |
/** | |
* Formats the data received in order to draw the json graphs | |
* | |
* @param {Array} data An array with the dataset | |
* @returns {Array} Returns an array with the formatted data | |
*/ | |
function formatData(data){ | |
var years = {'2014_2015': 'Actuals 2014/2015', '2015_2016': '2015/2016 Proj.', '2016_2017': '2016/2017 Proj.'}; | |
var formattedData = new Array(); | |
$.each(years, function(index, desc){ | |
var curY = {'total': 0, 'unit': ' B', 'type': desc, 'data': []}; | |
for(var i = 0; i < data.length; i++){ | |
var cur = data[i]; | |
var curyd = curY['data'].length; | |
curY['data'][curyd] = {'cat': cur['Details'], 'val': cur[index]}; | |
curY['total'] += cur[index]; | |
} | |
formattedData[formattedData.length] = curY; | |
}); | |
return formattedData; | |
} | |
}); | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment