A perceptually accurate pie chart.
inspired by this photo, whose provenance has been lost in the sands of time
the legend is drawn with the handy d3-legend component from @DataToViz
a descendant of this bl.ock from the prolific currankelleher
border: no | |
license: CCO-1.0 |
A perceptually accurate pie chart.
inspired by this photo, whose provenance has been lost in the sands of time
the legend is drawn with the handy d3-legend component from @DataToViz
a descendant of this bl.ock from the prolific currankelleher
feature | label | proportion | |
---|---|---|---|
sky | Sky | 0.75 | |
shadySide | Shady side of the pyramid | 0.06 | |
sunnySide | Sunny side of the pyramid | 0.19 |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Pyramid Pie</title> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3-legend/1.1.0/d3-legend.js"></script> | |
<script src='https://npmcdn.com/babel-core@5.8.34/browser.min.js'></script> | |
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'> | |
<style> | |
.color-legend text { | |
font-family: 'Open Sans', sans-serif; | |
font-size: 12pt; | |
font-weight: 600 | |
} | |
</style> | |
</head> | |
<body> | |
<script lang='babel' type='text/babel'> | |
const outerWidth = 960; | |
const outerHeight = 500; | |
const margin = { left: 0, top: 0, right: 0, bottom: 0 }; | |
const radiusMax = 180; | |
const xColumn = 'name'; | |
const sliceSizeColumn = 'proportion'; | |
const colorColumn = 'feature'; | |
const skyBlue = '#3a5d8f'; | |
const sunnySide = '#d8a34d'; | |
const shadySide = '#633c27'; | |
const pyramidColors = [ | |
skyBlue, | |
shadySide, | |
sunnySide, | |
]; | |
const legendColors = [ | |
skyBlue, | |
sunnySide, | |
shadySide | |
]; | |
const innerWidth = outerWidth - margin.left - margin.right; | |
const innerHeight = outerHeight - margin.top - margin.bottom; | |
const svg = d3.select('body').append('svg') | |
.attr('width', outerWidth) | |
.attr('height', outerHeight); | |
const g = svg.append('g') | |
.attr('transform', `translate(${margin.left}, ${margin.top})`); | |
const xAxisG = g.append('g') | |
.attr('class', 'x axis') | |
.attr('transform', `translate(0, ${innerHeight})`); | |
const pieG = g.append('g'); | |
const colorLegendG = g.append('g') | |
.attr('class', 'color-legend') | |
.attr('transform', 'translate(620, 145)'); | |
const xScale = d3.scale.ordinal().rangePoints([0, innerWidth]); | |
const colorScale = d3.scale.ordinal() | |
.range(pyramidColors); | |
const labels = [ | |
'Sky', | |
'Sunny side of the pyramid', | |
'Shady side of the pyramid' | |
]; | |
// a hack to get the legend elements to appear | |
// in the desired order | |
const legendColorScale = d3.scale.ordinal() | |
.domain(labels) | |
.range(legendColors); | |
const pie = d3.layout.pie() | |
.sort(null); | |
const arc = d3.svg.arc(); | |
arc.outerRadius(radiusMax); | |
arc.innerRadius(0); | |
const colorLegend = d3.legend.color() | |
.scale(legendColorScale) | |
.shape('circle') | |
.shapePadding(50) | |
.shapeRadius(25) | |
.labelOffset(12); | |
function render(data) { | |
colorScale.domain(data.map(d => d[colorColumn])); | |
pie.value(d => d[sliceSizeColumn]); | |
const pieData = pie(data); | |
pieG.attr('transform', `translate(${innerWidth / 3}, ${innerHeight / 2})`); | |
const slices = pieG.selectAll('path').data(pieData); | |
slices.enter().append('path'); | |
slices | |
.attr('d', arc) | |
.attr('fill', d => colorScale(d.data[colorColumn])); | |
slices.exit().remove(); | |
slices | |
.attr('transform', `rotate(220)`); | |
colorLegend.labels(labels); | |
colorLegendG.call(colorLegend); | |
d3.selectAll('text.label') | |
.attr('dy', '0em'); | |
} | |
function type(d) { | |
d.name = 'World'; | |
d.population = +d.population; | |
return d; | |
} | |
d3.csv('data.csv', type, render); | |
</script> | |
</body> | |
</html> |