A graph for showing how close election results are for a particular political party in a set of constituencies.
The green bars represent seats that the party has won (or is leading in), and the black bars represent the seats that the party has lost (or is trailing in).
The bars are further color coded according to the party's performance last time.
This visualization was used for the 2014 General elections in India at www.electioncharts.com. The github repo for that site can be found at https://github.com/mickeykedia/electioncharts
Built with blockbuilder.org
xxxxxxxxxx
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>Bird Swing Graph</title>
<!-- Bootstrap Core CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom CSS -->
<style>
.bar.postive {
fill:steelblue;
}
.bar.negative {
fill:brown;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
rect:hover {
opacity: 0.5;
}
.gridStyle {
border: 1px solid rgb(212,212,212);
width: 100%;
height: 400px;
}
</style>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<!-- jQuery Version 1.12.3 -->
<script src="https://code.jquery.com/jquery-1.12.3.min.js" integrity="sha256-aaODHAgvwQW1bFOGXMeX+pC4PZIPsvn2h1sArYOhgXQ=" crossorigin="anonymous"></script>
<!-- Bootstrap Core JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script type="text/javascript">
// Finally we have data and now and we are going to do something with it !
function drawMap(error, data, results){
var margin = {top:40,right:10,bottom:10,left:50};
var svg = d3.select('.container').append("svg");
var numberFormat = function (x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
};
console.log(error);
console.log(data);
/**
* going to be used to calculate position of dotted line.
* @type {number}
*/
var i = 0;
var j = 0;
// Sorting data by lead
data = data.sort(function(a,b){
return b.lead - a.lead;
});
// Adding the total number of constituencies which
// in which the party is leading/winning
i = results.wins + results.leads;
// last index of total data length
j = data.length-i;
var height = 400 - margin.top - margin.bottom,
// Finding the width of the first element and using that as the default width for responsiveness
width = 900 - margin.left - margin.right,
// square root scale for the leads
yScale = d3.scale.pow().exponent(0.5)
.domain(d3.extent(data,function(d){
return d.lead;
}))
.range([height,0]).clamp(true),
// ordinal scale for individual constituencies
xScale= d3.scale.ordinal()
.domain(data.map(function(d){
return d.constituency_id+":"+d.candidate_name;
})).rangeBands([0,width],0.15,0.05);
console.log(results);
// defining colours to use for each bar
var colour_swing = "#006600",
colour ="#4D944D",
colourTrail="#7D7D7E",
colourTrail_swing="#000000";
// minimum lead (prolly a large negative number )
var min = d3.min(data,function(d){
return d.lead;
})
/**
* find the first candidate with a lead, first candidate with a trail and the first candidate.
* Figure out the x axis position of those three points and draw lines and write text
* on the SVG. Text must count the number of wins/leads and trails as well.
* Add (lead %)
*/
svg.attr('height',height + margin.top + margin.bottom)
.attr('width',width + margin.left + margin.right)
.attr("border",1);
// Adding a 'g' element to the SVG
var svgG = svg.append("g")
.attr("transform","translate("+margin.left+","+margin.top+")");
console.log(svg);
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svgG.selectAll('.bar')
.data(data)
.enter()
.append('rect')
.attr("class",function(d){
return "bar";
})
.attr('width',xScale.rangeBand())
.attr('y',function(d){
return yScale(Math.max(0, d.lead));
})
.attr('x',function(d){
return xScale(d.constituency_id+":"+d.candidate_name);
})
.attr('fill',function(d){
if(d.status=="TRAIL" || d.status=="LOST"){
if(d.party_id == d.last_time_party_id){
return colourTrail_swing;
}else {
return colourTrail;
}
}else {
if(d.party_id != d.last_time_party_id){
return colour_swing;
}else {
return colour;
}
}
})
.attr('title',function(d){
var str = d.constituency_name+" ("+d.status+")";
if(d.party_id == d.last_time_party_id){
if(d.status =="TRAIL" || d.status =="LOST"){
str=str+" | <b>SWING SEAT</b><br/>";
}
}else {
if(d.status =="WIN" || d.status =="LEAD"){
str=str+" | <b>SWING SEAT</b><br/>";
}
}
return str;
})
.attr('data-content',function(d){
var str = "Candidate : "+ d.candidate_name+"<br/>";
/**
* Put commas in the numbers,
* difficult to read them or else.
* @type {string}
*/
if(d.status=="TRAIL"){
str = str+"Trail by: "+(-d.lead).toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Winning Party: "+ d.winning_party_name+"<br/>";
}else if (d.status=="LEAD"){
str = str+"Lead by : "+ d.lead.toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Runner-up Party: "+ d.loosing_party_name+"<br/>";
}else if(d.status=="LOST"){
str=str+"Lost by : "+ (-d.lead).toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Winning Party: "+ d.winning_party_name+"<br/>";
}else {
str = str+"Won by :"+ d.lead.toLocaleString()+"<br/>";
str = str+"Total Votes : "+ d.votes.toLocaleString()+"<br/>";
str = str+"Runner-up Party: "+ d.loosing_party_name+"<br/>";
};
return str;
})
.attr('data-toggle', 'popover')
.attr('leads',function(d){
return d.lead;
})
.transition()
.duration(1000)
.attr('height', function (d) {
return Math.abs(yScale(d.lead) - yScale(0));
});
$(function () {
$('[data-toggle="popover"]').popover({trigger:"hover", content:"content", container:"body", html:true})
});
/**
* Adding a line to demarcate the trailing/leading line.
* also a text which says - constituency in the right place on the graph.
*/
if (i>0 & i < data.length){
// i represents the number of winning/lead seats and that's why the line is drawn there (?)
var trailLineX = xScale(data[i].constituency_id+":"+ data[i].candidate_name);
svgG.append("line")
.attr("x1",trailLineX)
.attr("y1",0)
.attr("x2",trailLineX)
.attr("y2",height)
.attr("stroke","black")
.attr("stroke-dasharray","5,5");
if(trailLineX < width/4){
svgG.append('text')
.attr('x',trailLineX+margin.left)
.attr('y',yScale(0)-margin.top)
.attr('font-style','italic')
.text('Constituency');
}else {
svgG.append('text')
.attr('x',trailLineX-(2*margin.left+margin.right))
.attr('y',yScale(0)+margin.top)
.attr('font-style','italic')
.text('Constituency');
}
}
/**
* adding axes.
*/
svgG.append("g")
.attr("class","y axis")
.call(yAxis)
.append("text")
.attr("y",-10)
.attr("x",-5)
.attr("dy",".71em")
.style("text-anchor","end")
.style("font-size","0.8em")
.text("Margin");
svgG.append("g")
.attr("class","x axis")
.append("line")
.attr("y1",yScale(0))
.attr("y2",yScale(0))
.attr("x2",width);
}
queue()
.defer(d3.json, 'cpim_bengal.json')
.defer(d3.json, 'cpim_bengal_result.json') // REPLACE REF WITH DATA
.await(drawMap);
</script>
</head>
<body>
<div class="container">
<div id="chart_div" class="row">
</div>
</div>
<!-- /.container -->
</body>
</html>
https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js
https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.16/d3.min.js
https://d3js.org/queue.v1.min.js
https://code.jquery.com/jquery-1.12.3.min.js
https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js