2

I have my data formatted like flare.json that's used in this example :

I am just wondering what function the d3 zoomable chart uses to get the data in this format

In flare.json it's like this

{
   name: "stuff",
   children: [
    ....
   ]

}

and it's converted to this in the example. Which line does this?

{
  children: Array[17]
  depth: 1
  dx: 0.6028744305756647
  dy: 0.25
  name: "A name would appear here"
  parent: Object
  value: 39850000.06
  x: 0
  y: 0.25
}

Chart

    var total_revenue = json.total_revenue;
    json = json.chart_data;
  var width = 840,
      height = width,
      radius = width / 2,
      x = d3.scale.linear().range([0, 2 * Math.PI]),
      y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, radius]),
      padding = 5,
      duration = 1000;

  var div = d3.select("#chart_render");

  div.select("img").remove();

  var vis = div.append("svg")
      .attr("width", width + padding * 2)
      .attr("height", height + padding * 2)
    .append("g")
      .attr("transform", "translate(" + [radius + padding, radius + padding] + ")");

  var partition = d3.layout.partition()
      .value(function(d) { return d.size });

  var arc = d3.svg.arc()
      .startAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x))); })
      .endAngle(function(d) { return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))); })
      .innerRadius(function(d) { return Math.max(0, d.y ? y(d.y) : d.y); })
      .outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });


    console.log(json);
    var nodes = partition.nodes({children: json});

    var path = vis.selectAll("path").data(nodes);
    path.enter().append("path")
        .attr("id", function(d, i) { return "path-" + i; })
        .attr("d", arc)
        .attr("fill-rule", "evenodd")
        .style("fill", colour)
        .on("click", click);

    var text = vis.selectAll("text").data(nodes);
    var textEnter = text.enter().append("text")
        .style("fill-opacity", function(d) {
            var relative_percent = 0;
            var relative_total = 0;
            //console.log(d);

            if (d.depth != 0) {
                for(var i = 0; i < d.parent.children.length; i++) {
                    relative_total += d.parent.children[i].value;
                }
                //console.log(relative_total);
                relative_percent = d.value/total_revenue*100;

                if (relative_percent > 1) {
                    return '1';
                } else {
                    return '0';
                }
            }
        })
        .style("fill", function(d) {
          return "#fff";
        })
        .attr("text-anchor", function(d) {
          return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
        })
        .attr("dy", ".2em")
        .attr("transform", function(d) {
          var multiline = (d.name || "").split(" ").length > 1,
              angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
              rotate = angle + (multiline ? -.5 : 0);
          return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
        })
        .on("click", click);
    textEnter.append("tspan")
        .attr("x", 0)
        .text(function(d) { return d.depth ? d.name.split(" ")[0] : ""; });
    textEnter.append("tspan")
        .attr("x", 0)
        .attr("dy", "1em")
        .text(function(d) { return d.depth ? d.name.split(" ")[1] || "" : ""; });

    function click(d) {
      path.transition()
        .duration(duration)
        .attrTween("d", arcTween(d));

      // Somewhat of a hack as we rely on arcTween updating the scales.
      text.style("visibility", function(e) {
            return isParentOf(d, e) && e.value > 1500000 ? null : d3.select(this).style("visibility");
          })
        .transition()
          .duration(duration)
          .attrTween("text-anchor", function(d) {
            return function() {
              return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
            };
          })
          .attrTween("transform", function(d) {
            var multiline = (d.name || "").split(" ").length > 1;
            return function() {
              var angle = x(d.x + d.dx / 2) * 180 / Math.PI - 90,
                  rotate = angle + (multiline ? -.5 : 0);
              return "rotate(" + rotate + ")translate(" + (y(d.y) + padding) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
            };
          })
          .style("fill-opacity", function(e) { return isParentOf(d, e) ? 1 : 1e-6; })
          .each("end", function(e) {
            d3.select(this).style("visibility", function (d) {

          //    var relative_total = 0;
          //    var relative_percent = 0;

                // for(var i = 0; i < d.parent.children.length; i++) {
                //  relative_total += d.parent.children[i].value;
                // }

                // console.log(relative_total);

                // relative_percent = d.value/relative_total*100;
                // console.log(relative_percent);
                return isParentOf(d, e) && e.value > 1500000 ? null : "hidden";
            })
          });
    }

  function isParentOf(p, c) {
    if (p === c) return true;
    if (p.children) {
      return p.children.some(function(d) {
        return isParentOf(d, c);
      });
    }
    return false;
  }

  function colour(d) {

    if (d.depth == 0) {
      return "rgb(250, 250, 250)";
    } else if (d.depth == 1) {
      return 'rgb(86, 135, 209)';
    } else if (d.depth == 2) {
      return 'rgb(222, 120, 59)';
    } else if (d.depth == 3) {
      return 'rgb(106, 185, 117)';
    }

    // if (d.children) {
    //   // There is a maximum of two children!
    //   var colours = d.children.map(colour),
    //       a = d3.hsl(colours[0]),
    //       b = d3.hsl(colours[1]);
    //   // L*a*b* might be better here...
    //   return d3.hsl((a.h + b.h) / 2, a.s * 1.2, a.l / 1.2);
    // }
    // return d.colour || "#fff";
  }

  // Interpolate the scales!
  function arcTween(d) {
    var my = maxY(d),
        xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
        yd = d3.interpolate(y.domain(), [d.y, my]),
        yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
    return function(d) {
      return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); return arc(d); };
    };
  }

  function maxY(d) {
    return d.children ? Math.max.apply(Math, d.children.map(maxY)) : d.y + d.dy;
  }

  // http://www.w3.org/WAI/ER/WD-AERT/#color-contrast
  function brightness(rgb) {
    return rgb.r * .299 + rgb.g * .587 + rgb.b * .114;
  }
VividD
  • 10,456
  • 6
  • 64
  • 111
user1952811
  • 2,398
  • 6
  • 29
  • 49
  • The sunburst layout computes this. – Lars Kotthoff Jun 30 '14 at 19:06
  • @LarsKotthoff what lines can I include in my code to get that same structure? I obviously don't have to output the code out into an svg – user1952811 Jun 30 '14 at 19:08
  • You haven't linked to or included any code, so I can't tell you what exactly you have to copy, but the structure you've posted is computed by the layout. Run the layout on your data. – Lars Kotthoff Jun 30 '14 at 19:09
  • @LarsKotthoff updated the question with the chart code. – user1952811 Jun 30 '14 at 19:14
  • Also I've endlessly tried to figure this out, if you're good with D3, i'd love some help on this question: http://stackoverflow.com/questions/24437634/hide-label-if-not-enough-space-d3-zoomable-sunburst-chart – user1952811 Jun 30 '14 at 19:15
  • 1
    Well like I said, you need to run the layout -- `partition.nodes({children: json});` – Lars Kotthoff Jun 30 '14 at 19:16
  • I've already tried that... after doing `partition.nodes({children:json});` I've done `console.log(json)` and it's not updated. I've also tried `var stuff = partition.nodes({children.json});` and then `console.log(stuff)` and only one object showed up. – user1952811 Jun 30 '14 at 19:23
  • Can you provide a complete example please that demonstrates the problem? – Lars Kotthoff Jun 30 '14 at 19:36
  • Variable `json` is irrelevant after the call: `nodes = partition.nodes({children: json});` - variable `nodes` is what is used after that point! :) – VividD Jun 30 '14 at 19:48

1 Answers1

2

This line:

var nodes = partition.nodes({children: json});

Explanation of code that sets up sunburst diagram

In D3 parlance, sunburst diagram is based on D3 "partition layout". Actually, D3 "partition layout" is in a way more general term, since it can be used for displaying not only sunburst diagram, but also others based on the idea of "partitioning" parents (hence the name "partition"). This is also a useful example for noticing difference between "layout" and "diagram" (in D3 mindset), but this is another story.

Following 2 lines are first steps in initializing partition layout:

  var partition = d3.layout.partition()
      .value(function(d) { return d.size });

This line does all calculations:

var nodes = partition.nodes({children: json});

Then variable nodes can be used for defining actual visual appearance of svg elements (arcs and labels):

var path = vis.selectAll("path").data(nodes);

and

var text = vis.selectAll("text").data(nodes);

These two lines represent something which is called "data binding" often. They enable programmers to use data to drive visual elements, like in the following line:

.text(function(d) { return d.depth ? d.name.split(" ")[0] : ""; });

Here, d.name originates from data, and d.depth is added by partition layout. They are both actually part of nodes.

I tried to explain in simple terms, but probably there are some confusing points to you - don't worry, it will be crystal clear to you soon, if you read the right docs and tutorials. ;)

VividD
  • 10,456
  • 6
  • 64
  • 111
  • if i did `console.log(nodes)` I would get my data in the required form? – user1952811 Jun 30 '14 at 19:45
  • You would, definitely! – VividD Jun 30 '14 at 19:57
  • This is working, thanks. Could you look at this question for me? http://stackoverflow.com/questions/24437634/hide-label-if-not-enough-space-d3-zoomable-sunburst-chart It'd be greatly appreciated if you could provide any sort of help! – user1952811 Jun 30 '14 at 20:07