proportional pie charts
[{"type":"region","region_id":"E12000001","electorate":2093713,"turnout_abs":1118356,"leave_abs":570812,"remain_abs":547544,"turnout_pct":53.414961840519695,"leave_pct":51.04027697799269,"remain_pct":48.9597230220073},{"type":"region","region_id":"E12000002","electorate":5618117,"turnout_abs":2792986,"leave_abs":1519530,"remain_abs":1273456,"turnout_pct":49.713916602306426,"leave_pct":54.40521363157566,"remain_pct":45.59478636842434},{"type":"region","region_id":"E12000003","electorate":4219056,"turnout_abs":2086635,"leave_abs":1023050,"remain_abs":1063585,"turnout_pct":49.45739046838914,"leave_pct":49.02869931732191,"remain_pct":50.97130068267809},{"type":"region","region_id":"E12000004","electorate":3671679,"turnout_abs":1852882,"leave_abs":931743,"remain_abs":921139,"turnout_pct":50.46416094653154,"leave_pct":50.28614882113378,"remain_pct":49.71385117886622},{"type":"region","region_id":"E12000005","electorate":4476290,"turnout_abs":2253921,"leave_abs":1123990,"remain_abs":1129931,"turnout_pct":50.35243471714299,"leave_pct":49.8682074482646,"remain_pct":50.1317925517354},{"type":"region","region_id":"E12000006","electorate":4730846,"turnout_abs":2341720,"leave_abs":1264030,"remain_abs":1077690,"turnout_pct":49.49896910615987,"leave_pct":53.97869941752216,"remain_pct":46.021300582477835},{"type":"region","region_id":"E12000007","electorate":6618717,"turnout_abs":3323458,"leave_abs":1741819,"remain_abs":1581639,"turnout_pct":50.21302466928258,"leave_pct":52.40983939017734,"remain_pct":47.59016060982266},{"type":"region","region_id":"E12000008","electorate":6969602,"turnout_abs":3483603,"leave_abs":1764413,"remain_abs":1719190,"turnout_pct":49.982811070129976,"leave_pct":50.64908372165255,"remain_pct":49.35091627834745},{"type":"region","region_id":"E12000009","electorate":4346897,"turnout_abs":2218818,"leave_abs":1053545,"remain_abs":1165273,"turnout_pct":51.043721532854356,"leave_pct":47.482263078810426,"remain_pct":52.517736921189574},{"type":"region","region_id":"N07000001","electorate":1407337,"turnout_abs":831299,"leave_abs":449716,"remain_abs":381583,"turnout_pct":59.06893658022207,"leave_pct":54.097983998537224,"remain_pct":45.90201600146277},{"type":"region","region_id":"S12000001","electorate":5285228,"turnout_abs":2664537,"leave_abs":1364605,"remain_abs":1299932,"turnout_pct":50.41479762084058,"leave_pct":51.21358795167791,"remain_pct":48.78641204832209},{"type":"region","region_id":"W08000001","electorate":1491616,"turnout_abs":754660,"leave_abs":365495,"remain_abs":389165,"turnout_pct":50.59345032501663,"leave_pct":48.431744096679296,"remain_pct":51.568255903320704}]
An idea for showing vote distribution, turnout and electorate across a different areas. The outer circle's area represents the total electorate the pie chart area is proportional to the numebr of votes cast so turnout can be infered . Notwithstanding problems of comparing areas and a general distaste for piecharts.
function proportionalPie(){
var pie = d3.layout.pie().sort(null);
var radiusScale = d3.scale.sqrt()
.domain([0, 3000000])
.range([0, 50]);
var tickInterval = 1000000;
var ticks = null;
var classer = function(d,i){
return 'segment-' + i;
}
var extentMarker = false; // how big could this pie have been?
var extentAccessor = function(d){ return d.extent; }
function chart(parent){
parent.each(function(d){
var arc = d3.svg.arc()
.innerRadius(0)
.outerRadius(radiusScale(d.total)) //based on d.total
var anchor = d3.select(this);
var pieData = pie(d.values);
var labelData = pieData.map(function(p){
return {
angle: ((p.endAngle + p.startAngle)/2) - Math.PI/2,
value: Math.round( (p.data/d.total)*100 )
}
});
anchor.selectAll('path')
.data(pieData)
.enter()
.append('path')
.attr('d', arc)
.attr('class', classer)
if( tickInterval === null || ticks === null){
var tickMarks = [];
}else{
tickMarks = d3.range(tickInterval, d.total + tickInterval ,tickInterval );
}
if(extentMarker){
anchor.append('circle')
.attr('class','extent')
.attr('r',function(d){
return radiusScale( extentAccessor(d) );
})
}
anchor.selectAll('circle.tick') //the axes circle
.data(tickMarks)
.enter()
.append('circle')
.attr('class','tick')
.attr('r',radiusScale);
})
}
chart.extentAccessor = function(f){
extentAccessor = f;
return chart;
}
chart.extentMarker = function(x){
extentMarker = x;
return chart;
}
chart.totalAccessor = function(f){
if(f==undefined){
totalAccessor = function(d){ return d.total }
}else{
totalAccessor = f;
}
return chart;
}
chart.valuesAccessor = function(f){
if(f==undefined){
valuesAccessor = function(d){ return d.values }
}else{
valuesAccessor = f;
}
return chart;
}
chart.ticks = function(a){ //expects an array
if(a==undefined) return ticks;
ticks = a;
tickInterval = null;
return chart;
};
chart.tickInterval = function(x){
if(x==undefined) return tickInterval;
tickInterval = x;
ticks = null;
return chart;
};
chart.maxValue = function(x){
if(x==undefined) return radiusScale.domain()[1];
var dom = radiusScale.domain();
dom[1] = x;
radiusScale.domain(dom);
return chart;
};
chart.maxRadius = function(x){
if(x==undefined) return radiusScale.range()[1];
var range = radiusScale.range();
range[1] = x;
radiusScale.range(range);
return chart;
};
chart.radiusScale = function(x){
return radiusScale(x);
};
chart.classer = function(f){ //takes a function
if(!f) return classer;
classer = f;
return chart;
};
return chart;
}
<!doctype html>
<html lang="en-GB" class="core">
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="proportional-pie.js"></script>
</head>
<body>
<main class="article" role="main">
<h1 class="article__headline--mega">Proportional Pies!</h1>
</main>
<div class="chart"></div>
</body>
<style>
body{
font-family: sans-serif;
}
.segment-0{
fill:#FF9999;
}
.segment-1{
fill:#9999FF;
}
.extent{
fill:none;
stroke:#000;
stroke-width:3;
stroke-dasharray: 5 2;
}
.pie-container{
display: inline-block;
margin: 10px;
}
</style>
<script>
d3.json('regional.json',function(regionaldata){
var pie = proportionalPie()
.maxRadius(45)
.maxValue(d3.max(regionaldata, function(d){ return d.electorate; }))
.extentMarker(true)
.extentAccessor(function(d){
return d.electorate;
});
var regionaldata = regionaldata.map(function(d){
d.values = [d.remain_pct,d.leave_pct];
d.total = d.turnout_abs;
d.name = '-'+d.region_id+'-'; //put a proper name in here
return d;
});
console.log(regionaldata);
d3.select('.chart')
.selectAll('div.pie-container')
.data(regionaldata)
.enter()
.append('div')
.attr('class','pie-container')
.call(function(parent){
parent.append('h3')
.text(function(d){ return d.name });
parent.append('svg')
.attr('class','pie-chart')
.attr('width', 100)
.attr('height', 100)
.attr('viewBox','0 0 100 100')
.append('g')
.attr('transform','translate(50,50)')
.call(pie);
parent.append('div')
.html(function(d){
return 'L <span class="leave-label">'+Math.round(d.leave_pct)+'%</span> R <span class="remain-label">'+Math.round(d.remain_pct)+'%</span>. Turnout ' + Math.round(d.turnout_pct) +'%';
})
})
})
</script>
</html>