// requires a global 'flare' tree data // this adds a central root with straight links (MikeB's original used flared from a point), // and adds jitter to node positions to pack nodes more tightly. // Added labels back in, from http://bl.ocks.org/mbostock/2e12b0bd732e7fe4000e2d11ecab0268 var diameter = 880; var radius_unspec = 5; var radius_minspec = 3; var radius_maxspec = 10; var label_expected = 100; // could measure and adjust, but function of a css setting var max_value = 100000; // survey from flare var min_value = 0; var sizeFeature = "size"; // for radius of node circles var titleFeature = "name"; var sortFeature = "size"; var attrFeature = "size"; var treeXYMargin = 0; var treeArcDegrees = 360; var treeStartDegrees = 90; var piOver180 = Math.PI/180.0; var radius_scale = d3.scale.sqrt() .domain([min_value, max_value]) .range([radius_minspec, radius_maxspec]); var rho_scale = d3.scale.pow() .exponent(1.0) //0.8 .domain([0, diameter/2]) .range([0, diameter/2]); var tree = d3.layout.tree() .size([treeArcDegrees, diameter / 2 - radius_maxspec - label_expected]) .separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; }); var diagonal = d3.svg.diagonal.radial() .projection(function(d) { return [d.y, (d.x + (90-treeStartDegrees)) * piOver180]; }); var g = d3.select("#d3svgdiv").append("svg:svg") .attr("width", diameter + 2*treeXYMargin) .attr("height", diameter + 2*treeXYMargin) .append("svg:g") .attr("transform", "translate(" + (diameter / 2 + treeXYMargin) + "," + (diameter / 2 + treeXYMargin) + ")"); function rollup(node) { if (node.children === undefined) return node[sizeFeature] === undefined ? 0 : node[sizeFeature]; var sum=0; node.children.forEach( function(n, i) { sum = sum + rollup(n); }); if (node[sizeFeature] === undefined) node[sizeFeature] = sum; return sum; } rollup(flare); tree.sort(comparator); // before further ops var nodes = tree.nodes(flare), links = tree.links(nodes); function comparator(a, b) { return b[sortFeature] - a[sortFeature]; } function jitter(node) { if(node.children === undefined) return; var jCoef = 1.0; // start with +, as with original node.children.forEach( function(n, i) { if(n[sizeFeature] !== undefined) { if (n.children !== undefined) // no need for jitter, but a sibling w/o children should be inward jCoef = 1.0; else jCoef = -1.0 * jCoef; // invert each cycle thus, until next w/children n.y = n.y + jCoef * radius_scale(n[sizeFeature]); } n.y = rho_scale(n.y); jitter(n); }); } jitter(flare); function fliptext(a) { var tA = ((a + (90-treeStartDegrees))%360); if (tA<0) tA = tA + 360; return tA < 180; } var link = g.selectAll(".link") .data(links.filter(function(l) { return l.source.depth > 0; })); link .enter().append("path") .attr("class", "link") .attr("d", diagonal); var root_link = g.selectAll(".root_link") .data(links.filter(function(l) { return l.source.depth === 0; })); root_link .enter().append("path") .attr("class", "root_link") .attr("d", function(d){ // add link line(s) from root to first level nodes var theta = (d.target.x-treeStartDegrees)*piOver180; var x1 = Math.cos(theta)*d.source.y; var y1 = Math.sin(theta)*d.source.y; var x2 = Math.cos(theta)*d.target.y; var y2 = Math.sin(theta)*d.target.y; return 'M'+x1+' '+y1+' L'+x2+' '+y2; }); var node = g.selectAll(".node") .data(nodes) .enter().append("g") //g fixes text .attr("class", "node") .attr("transform", function(d){ return "rotate(" + (d.x - treeStartDegrees) + ")translate(" + d.y + ")"; }) .classed('internal', function(d){ return d.children && d.depth>0; }) .classed('root', function(d) { return d.depth===0; }) .classed('missing', function(d) { return d[attrFeature] === undefined; }) // this(these) should be externally applied .classed('beta', function(d) { var s = (""+d.name).toLowerCase().indexOf("list")>=0; return s; }) .classed('alpha', function(d) { var s = (""+d.name).toLowerCase().indexOf("legend")>=0; return s; }) ; node.append("circle") .attr("r", function(d){ if(d[attrFeature] === undefined) return radius_unspec; return radius_scale(d[attrFeature]); }); node.append("title") .text(function(d) { if(d[attrFeature] === undefined) return d[titleFeature]; else return d[titleFeature] + ' (' + d3.format(',')(d[attrFeature]) + ')'; }); node.append("text") .attr("dy", ".31em") .attr("x", function(d) { return d.depth===0 ? 0 : d.children? fliptext(d.x) ? -20 : 20 : fliptext(d.x) ? 6 : -6; }) .attr("text-anchor", function(d) { return fliptext(d.x) === !d.children ? "start" : "end"; }) .attr("transform", function(d) { return fliptext(d.x) ? "translate(8)" : "rotate(180)translate(-8)"; }) .text(function(d) { return d[titleFeature]; }); //d3.select(self.frameElement).style('height', diameter+'px');