Joel
Joel

Reputation: 2404

Composing D3 charts (pie) in Tree

I am trying to compose a D3 pie component in each node of a tree.

I am able to build separately the tree and one pie, but I couldn't figure out how to compose them.

Basically, I have the following json data:

window.json = {
    "health": [{
    "value": 60
    }, {
    "value": 10
    }, {
    "value": 30
    }],
    "color": orange,
    "children": [{
    "health": [{
        "value": 60
    }, {
        "value": 20
    }, {
        "value": 20
    }],
    "color": green
    }, {
    "health": [{
        "value": 40
        }, {
        "value": 30
        }, {
        "value": 30
        }],
    "color": orange
    }]
};

It represents the tree. Each node contains data for a pie: it's the "health" properties.

I've build the tree here: http://jsfiddle.net/4srt30pj/4/

I can build a single pie: http://jsfiddle.net/4srt30pj/5/

But I can't see how to mix them together, so that each node shows a pie. I've tried to create a function that draws a pie component:

function drawPie(selection, node) {
    selection.data(node, function(d, i) {
        console.log(node);
        console.log(d);
        console.log(i);
        return pie(d.health);
    })
    .enter()
    .append('path')
    .attr('d', arc)
    .attr('fill', function (d, i) {
        return color(i);
    });
}

Then call it for each tree nodes:

drawPie(vis.selectAll("g.node"), nodes);

(the code is there: http://jsfiddle.net/4srt30pj/6/ )

But it doesn't show the pies.

Is it possible to achieve this composition?

Upvotes: 0

Views: 661

Answers (1)

Mark
Mark

Reputation: 108517

You are close. Try:

function drawPie(d) {      
  d3.select(this)
    .selectAll('path')
    .data(pie(d.health))
    .enter()
    .append('path')
    .attr('d', arc)
    .attr('fill', function(d, i) {
       return color(i);
    });
}

nodeEnter.each(drawPie);

Full working sample:

<!DOCTYPE html>
<html>

<head>
  <script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
  <style>
    path.link {
      fill: none;
      stroke-width: 5px;
    }
    
    svg text {
      font-family: Roboto, Arial;
    }
    
    .selected {
      display: none;
    }
  </style>
</head>

<body>
  <script>
    var red = "#f5696d";
    var green = "#40bc96";
    var orange = "#fabd57";
    window.json = {
      "health": [{
        "value": 60
      }, {
        "value": 10
      }, {
        "value": 30
      }],
      "color": orange,
      "children": [{
        "health": [{
          "value": 60
        }, {
          "value": 20
        }, {
          "value": 20
        }],
        "color": green
      }, {
        "health": [{
          "value": 40
        }, {
          "value": 30
        }, {
          "value": 30
        }],
        "color": orange
      }]
    };

    var w = 100;
    var h = 60;
    var i = 0;
    var root;

    var tree = d3.layout.tree()
      .nodeSize([w + 10, h + 20])
      .separation(function(a, b) {
        return (a.parent == b.parent ? 1 : 1.5);
      });

    var diagonal = d3.svg.diagonal()
      .projection(function(d) {
        return [d.x, d.y];
      });

    var vis = d3.select("body").append("svg:svg")
      .attr("width", 500)
      .attr("height", 500)
      .append("svg:g")
      .attr("transform", "translate(" + 250 + "," + 30 + ")");

    root = window.json;
    root.x0 = 0;
    root.y0 = 0;

    function toggleAll(d) {
      if (d.children) {
        d.children.forEach(toggleAll);
        toggle(d);
      }
    }

    var arc = d3.svg.arc()
      .outerRadius(30)
      .innerRadius(0);

    var pie = d3.layout.pie()
      .value(function(d) {
        return d.value;
      })
      .sort(null);

    var color = d3.scale.ordinal()
      .range(['#40bc96', '#fabd57', '#f5696d']);


    function drawPie(d) {
      
      d3.select(this)
        .selectAll('path')
        .data(pie(d.health))
        .enter()
        .append('path')
        .attr('d', arc)
        .attr('fill', function(d, i) {
          return color(i);
        });
    }

    update(root);

    function update(source) {
      var duration = d3.event && d3.event.altKey ? 5000 : 500;

      // Compute the new tree layout.
      var nodes = tree.nodes(root).reverse();

      // Update the nodes…
      var node = vis.selectAll("g.node")
        .data(nodes, function(d) {
          return d.id || (d.id = ++i);
        });

      // Enter any new nodes at the parent's previous position.
      var nodeEnter = node.enter().append("svg:g")
        .attr("class", "node")
        .attr("transform", function(d) {
          return "translate(" + source.x0 + "," + source.y0 + ")";
        });

      nodeEnter
        .each(drawPie);

      // Transition nodes to their new position.
      var nodeUpdate = node.transition()
        .duration(duration)
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        });

      // Update the links…
      var link = vis.selectAll("path.link")
        .data(tree.links(nodes), function(d) {
          return d.target.id;
        });

      // Enter any new links at the parent's previous position.
      link.enter().insert("svg:path", "g")
        .attr("class", "link")
        .style("stroke-opacity", 0.4)
        .style("stroke", function(d) {
          return d.target.color;
        })
        .attr("d", function(d) {
          var o = {
            x: source.x0,
            y: source.y0
          };
          return diagonal({
            source: o,
            target: o
          });
        })
        .transition()
        .duration(duration)
        .attr("d", diagonal);

      // Stash the old positions for transition.
      nodes.forEach(function(d) {
        d.x0 = d.x;
        d.y0 = d.y;
      });
    }
  </script>
</body>

</html>

Upvotes: 1

Related Questions