A visualization of Indian state finances (revenues and expenditures) for 2010-2011
created for India60
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { position: relative; } | |
.chart { height: 500px; overflow: auto; } | |
.tooltip { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 956px; | |
height: 121px; | |
padding: 0 0 16px 0; | |
background: #f5f5f5; | |
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.15); | |
border: 1px solid #F7F7F7; | |
} | |
.tooltip.stick { | |
position: fixed; | |
top: 0; | |
z-index: 1000; | |
} | |
.tooltip hgroup { | |
margin-top: 0; | |
padding: 6px 16px; | |
background: #eee; | |
text-shadow: 1px 1px 1px #fff; | |
border-bottom: 1px solid #ddd; | |
border-right: 1px solid #ddd; | |
border-radius: 3px 3px 0 0; | |
float: left; | |
width: 200px; | |
} | |
.tooltip h1 { | |
margin: 0; | |
font-size: 18px; | |
text-transform: uppercase; | |
} | |
.tooltip h6 { | |
margin: 0; | |
line-height: 10px; | |
} | |
.tooltip-content { | |
padding: 0 16px 16px 16px; | |
} | |
.tooltip h2 { | |
font-size: 14px; | |
} | |
.revenue, .expenditure { | |
float: left; | |
margin-left: 60px; | |
} | |
</style> | |
<body> | |
<div class="chart"></div> | |
<p style="font-size: 12px; margin: 0;"><em>Source: <a href="http://saiindia.gov.in/english/home/Our_Products/Accounts/Combined_Finance/2010_11/2010_11.html">Comptroller and Auditor General of India</a></em></p> | |
<div class="tooltip"> | |
<hgroup> | |
<h1>Legend</h1> | |
<h6>in billions of rupees</h6> | |
</hgroup> | |
<div class="tooltip-content"> | |
<section class="revenue"> | |
<h2>Revenue <span></span></h2> | |
<div class="chart-revenue"></div> | |
</section> | |
<section class="expenditure"> | |
<h2>Expenditure <span></span></h2> | |
<div class="chart-expenditure"></div> | |
</section> | |
</div> | |
</div> | |
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
d3.json("/d/4132655/india-state-finances-2011.json", function(error, data) { | |
data.shift(); //remove union data for now. | |
var formatter = d3.format(',d'); | |
var rupee = formatter//function(v) { return '₹' + formatter(v); }; | |
var midX = 480; | |
var contentPadding = 36; | |
var barHeight = 10; | |
var barPadding = 3; | |
var statePadding = 20; | |
var maxRevenue = d3.max(data, function(d) { return d3.sum(d.revenue, function(d) { return d.value; }); }); | |
var maxExpenditure = d3.max(data, function(d) { return d3.sum(d.expenditure, function(d) { return d.value; }); }); | |
var tooltip = d3.select('.tooltip'); | |
var xRevenue = d3.scale.linear() | |
.domain([0, maxRevenue]) | |
.rangeRound([0, 400]); | |
var xExpenditure = d3.scale.linear() | |
.domain([0, maxExpenditure]) | |
.rangeRound([0, 400]); | |
var colorRevenue = d3.scale.ordinal() | |
.domain([0,1,2]) | |
.range(['#8C510A', '#F6E8C3', '#5AB4AC']); | |
var colorExpenditure = d3.scale.ordinal() | |
.domain([0, 1, 2, 3]) | |
.range(['#D73027', '#FC8D59', '#FEE08B', '#91CF60']); | |
var svg = d3.select('.chart').append('svg') | |
.attr('width', '960') | |
.attr('height', '1024') | |
.style('margin', '160px 0 0 0'); | |
var state = svg.selectAll('g.state') | |
.data(data) | |
.enter().append('g') | |
.attr('class', 'state') | |
.on('mouseover', function(d) { | |
tooltip.select('h1').html(d.name); | |
var rmax = d3.max(d.revenue, function(r) { return r.value; }); | |
var emax = d3.max(d.expenditure, function(r) { return r.value; }); | |
var w = 268; | |
var x = d3.scale.linear() | |
.domain([0, d3.max([rmax, emax])]) | |
.range([0, w - 100]); | |
var y = function(d1, i) { return (barHeight + barPadding) * i; }; | |
tooltip.select('.revenue h2 span') | |
.html(' – ₹' + Math.round(d3.sum(d.revenue, function(d) { return d.value; })/100) + ' billion'); | |
d3.select('.chart-revenue').html(''); | |
var svgRevenue = d3.select('.chart-revenue').append('svg') | |
.attr('width', w) | |
.attr('height', (barHeight + barPadding) * d.revenue.length); | |
svgRevenue.selectAll('.label-name') | |
.data(d.revenue) | |
.enter().append('text') | |
.attr('class', 'label-name') | |
.attr('x', 0) | |
.attr('y', function(d2,i) { return y(d2, i) + 5; }) | |
.attr("dy", ".35em") | |
.style("fill", '#6b6b6b') | |
.style("font-size", '12px') | |
.style("text-transform", 'capitalize') | |
.text(function(d) { return d.name; }); | |
svgRevenue.selectAll('label-value') | |
.data(d.revenue) | |
.enter().append('text') | |
.attr('class', 'label-value') | |
.attr('x', 95) | |
.attr('y', function(d2,i) { return y(d2, i) + 5; }) | |
.attr("dy", ".35em") | |
.attr("text-anchor", "end") | |
.style("fill", '#43403f') | |
.style("font-size", '12px') | |
.style("font-weight", 'bold') | |
.text(function(d) { return rupee(Math.round(d.value/100)); }); | |
svgRevenue.selectAll('rect') | |
.data(d.revenue) | |
.enter().append('rect') | |
.attr('x', 100) | |
.attr('y', function(d2, i) { return y(d2, i); }) | |
.attr('height', barHeight) | |
.attr('width', function(d1) { return x(d1.value); }) | |
.style('fill', function(d1, i) { return colorRevenue(i); }); | |
x = d3.scale.linear() | |
.domain([0, d3.max([rmax, emax])]) | |
.range([0, w-100]); | |
tooltip.select('.expenditure h2 span') | |
.html(' – ₹' + Math.round(d3.sum(d.expenditure, function(d) { return d.value; })/100) + ' billion'); | |
d3.select('.chart-expenditure').html(''); | |
var svgExpenditure = d3.select('.chart-expenditure').append('svg') | |
.attr('width', w) | |
.attr('height', (barHeight + barPadding) * d.expenditure.length); | |
svgExpenditure.selectAll('text') | |
.data(d.expenditure) | |
.enter().append('text') | |
.attr('x', 0) | |
.attr('y', function(d2,i) { return y(d2, i) + 5; }) | |
.attr("dy", ".35em") | |
.style("fill", '#6b6b6b') | |
.style("font-size", '12px') | |
.text(function(d) { return d.name.replace(/services|payments/gi, ''); }); | |
svgExpenditure.selectAll('label-value') | |
.data(d.expenditure) | |
.enter().append('text') | |
.attr('class', 'label-value') | |
.attr('x', 95) | |
.attr('y', function(d2,i) { return y(d2, i) + 5; }) | |
.attr("dy", ".35em") | |
.attr("text-anchor", "end") | |
.style("fill", '#43403f') | |
.style("font-size", '12px') | |
.style("font-weight", 'bold') | |
.text(function(d) { return rupee(Math.round(d.value/100)); }); | |
svgExpenditure.selectAll('rect') | |
.data(d.expenditure) | |
.enter().append('rect') | |
.attr('x', 100) | |
.attr('y', function(d2, i) { return y(d2, i); }) | |
.attr('height', barHeight) | |
.attr('width', function(d1) { return x(d1.value); }) | |
.style('fill', function(d1, i) { return colorExpenditure(i); }); | |
d3.event.stopPropagation(); | |
}); | |
var revenue = state.selectAll('rect.revenue') | |
.data(function(d, i) { | |
d.bottomX = 0; | |
d.bottomY = (barHeight * 2 + barPadding + statePadding/2) * (i + 1) + contentPadding; | |
var total = d3.sum(d.revenue, function(d1) { return d1.value; }); | |
d.revenue.forEach(function(r, j) { | |
r.i = i; | |
r.total = total; | |
r.x0 = j === 0 ? midX - xRevenue(total)/2 : d.revenue[j-1].x0 + xRevenue(d.revenue[j-1].value); | |
d.bottomX = d.bottomX === 0 ? r.x0 : d3.min([d.bottomX, r.x0]); | |
}); | |
return d.revenue; | |
}) | |
.enter().append('rect') | |
.attr('class', 'revenue') | |
.attr('x', (function(d) { | |
return d.x0; | |
})) | |
.attr('y', function(d, i) { | |
return (barHeight + barPadding + statePadding) * d.i; | |
}) | |
.attr('width', function(d) { | |
return xRevenue(d.value); | |
}) | |
.attr('height', barHeight) | |
.style('fill', function(d, i) { return colorRevenue(i); }); | |
state.append('text') | |
.attr('class', '.state-name') | |
.attr('x', function(d) { return d.revenue[0].x0 - 5; }) | |
.attr('y', function(d,i) { | |
return (barHeight + barPadding + statePadding) * i + 5; | |
}) | |
.attr("dy", ".35em") | |
.attr("text-anchor", "end") | |
.style('fill', '#6b6b6b') | |
.style("font-size", '10px') | |
.style("text-transform", 'uppercase') | |
.style("letter-spacing", "1px") | |
.text(function(d) { return d.name; }); | |
var expenditure = state.selectAll('rect.expenditure') | |
.data(function(d, i) { | |
var total = d3.sum(d.expenditure, function(d1) { return d1.value; }); | |
d.expenditure.forEach(function(r, j) { | |
r.i = i; | |
r.total = total; | |
r.x0 = j === 0 ? midX - xExpenditure(total)/2 : d.expenditure[j-1].x0 + xExpenditure(d.expenditure[j-1].value); | |
d.bottomX = d.bottomX === 0 ? r.x0 : d3.min([d.bottomX, r.x0]); | |
}); | |
return d.expenditure; | |
}) | |
.enter().append('rect') | |
.attr('class', 'expenditure') | |
.attr('x', (function(d) { | |
return d.x0; | |
})) | |
.attr('y', function(d, i) { | |
return (barHeight + barPadding + statePadding) * d.i + (barHeight + barPadding); | |
}) | |
.attr('width', function(d) { | |
return xExpenditure(d.value); | |
}) | |
.attr('height', barHeight) | |
.style('fill', function(d, i) { return colorExpenditure(i); }); | |
}); | |
var tooltip_relocate = function() { | |
var window_top = $(window).scrollTop(); | |
var div_top = $('.chart').offset().top; | |
if (window_top > div_top) | |
$('.tooltip').addClass('stick') | |
else | |
$('.tooltip').removeClass('stick'); | |
} | |
$(function() { | |
$(window).scroll(tooltip_relocate); | |
}) | |
</script> |