Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3js - Creating Asterplot-like Charts (example included)

I'm new to d3js, but am familiar with javascript and the principles of data-visualisation.

I've tried to achieve an effects to visualize 2 dimensions of data using the aster plot diagram, but can't really get this thing to work like expected.

Attached you'll find the diagram I'm trying to recreate and my example code. It would be huge if you could tell me how to get it working - or how to optimize it! :)

D3JS - Aster Plot Preview

This is how I think the data and the code should look like.. kinda.. (Pseudo-Code incoming..)

Data Example:

    var testData = {
      maxPoints: 10,
      color: '#bababa',
      border: {
        width: 1,
        color: '#ffffff'
      },
      items: [
        {
          name: 'Looks',
          color: '#2976dd',
          weight: 0.37,
          points: 8
        },{
          name: 'Charm',
          color: '#87bd24',
          weight: 0.03,
          points: 5
        },{
          name: 'Honesty',
          color: '#406900',
          weight: 0.16,
          points: 7
        },{
          name: 'Humour',
          color: '#ffb200',
          weight: 0.31,
          points: 9
        },{
          name: 'Intelligence',
          color: '#f78200',
          weight: 0.12,
          points: 0
        }
      ]
    };

Code Example:

  var archs = [];
  // Loop through each item
  var circleRadius = 400;
  var innerRadius = 100;

  var startAngle = 0;
  var endAngle = 0;
  for (var i = 0; i < testData.items.length; i++) {
    // Draw each arch
    var maxPoints = testData.maxPoints;
    var archHeight = (circleRadius - innerRadius) / maxPoints;
    var startRadius = innerRadius;

    endAngle += testData.items[i].weight;

    for (var j = 0; j < maxPoints; j++) {
      var color = testData.color;
      // draw arch - don't know how to colorize accordingly..
      if (testData.items[i].points < j) {
        // color this arc somehow..
        color = testData.items[i].color;
      }

        d3.svg.arc()
        .startAngle(startAngle)
        .endAngle(endAngle)
        .innerRadius(startRadius)
        .outerRadius(startRadius+archHeight);

      // Increase startRadius
      startRadius += archHeight;
    }

    // Increase startAngle
    startAngle = endAngle;
  }

Somehow my code looks way more complicated.. Though it's still pseudo-code.. I'm still struggling.. If someone could give me a hint or some working code to start from I would be very thankful!

Thanks in advance - Chris

like image 942
Christian Wisniewski Avatar asked Oct 18 '22 15:10

Christian Wisniewski


1 Answers

Here's a quick implementation which replicates your diagram. It essentially builds concentric donut charts based on weight and then colors the slices based on points:

<!DOCTYPE html>
<meta charset="utf-8">
<style> 
  .arc path {
    stroke: #fff;
  }
</style>

<body>
  <script src="//d3js.org/d3.v3.min.js"></script>
  <script>
    var testData = {
      maxPoints: 10,
      color: '#bababa',
      border: {
        width: 1,
        color: '#ffffff'
      },
      items: [{
        name: 'Looks',
        color: '#2976dd',
        weight: 0.37,
        points: 8
      }, {
        name: 'Charm',
        color: '#87bd24',
        weight: 0.03,
        points: 5
      }, {
        name: 'Honesty',
        color: '#406900',
        weight: 0.16,
        points: 7
      }, {
        name: 'Humour',
        color: '#ffb200',
        weight: 0.31,
        points: 9
      }, {
        name: 'Intelligence',
        color: '#f78200',
        weight: 0.12,
        points: 0
      }]
    };

    var width = 500,
      height = 500;
      color = d3.scale.category20();
    
    // inner radius
    var iR = 75,
    // radius of each concentric arc
        r = ((Math.min(width, height) / 2) - iR)  / testData.maxPoints;
        
    var pie = d3.layout.pie()
      .sort(null)
      .value(function(d) {
        return d.weight;
      })
      .padAngle(.01);

  var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height)
    .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    var g = svg.selectAll(".arc")
      .data(pie(testData.items))
      .enter().append("g")
      .attr("class", "arc");

    // iterate our number of rings
    d3.range(testData.maxPoints)
      .forEach(function(i){
        
        // generate an arc
        var arc = d3.svg.arc()
            .outerRadius(r * (i + 1) + iR)
            .innerRadius(r * i + iR);
        
        // fill it, if appropriate
        g.append("path")
          .attr("d", arc)
          .style("fill", function(d) {
            if (i < d.data.points)
              return color(d.data.name);
            else
              return "#eee"
          });
    });

  </script>
like image 144
Mark Avatar answered Oct 30 '22 15:10

Mark