Skip to content

Instantly share code, notes, and snippets.

@micahstubbs
Last active February 28, 2024 12:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save micahstubbs/f8937584103ab929c314e479a5a457af to your computer and use it in GitHub Desktop.
Save micahstubbs/f8937584103ab929c314e479a5a457af to your computer and use it in GitHub Desktop.
Pyramid Pie
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment