block by renecnielsen 5ff9bbbcd17623c59f85992751e6bea1

Pyramid Pie

Full Screen

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

forked from micahstubbs‘s block: Pyramid Pie

index.html

<!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>

data.csv

feature,label,proportion
sky,Sky,0.75
shadySide,Shady side of the pyramid,0.06
sunnySide,Sunny side of the pyramid,0.19