How to display parent elements in a nested treemap? - javascript

This is my data : https://api.myjson.com/bins/b0m6s
I want to create a nested treemap that shows the parent element and the child elements inside it, like this example : https://bl.ocks.org/mbostock/911ad09bdead40ec0061
Here is my treemap code currently :
var defaultColors = d3.scale.ordinal().range(["#00AEEF", "#8DC63F", "#FFC20E", "#F06EAA", "#AE9986", "#009BA5", "#00A651", "#F7941D", "#B656AB", "#ABA000", "#F3716D", "#8D7B6B", "#EF413D", "#AD772B", "#878787"]);
var treemap;
var h_pad = 2, // 2 pixels vertical padding
v_pad = 4; // 4 pixels of horizontal padding (2 px at each side)
var canvas = d3
.select(id)
.append("svg")
.attr("class", "chart")
.attr("width", cfg.width + cfg.margin.left + cfg.margin.right)
.attr("height", cfg.height + cfg.margin.top + cfg.margin.bottom)
.attr("viewBox", "0 0 960 500")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("id", "canvas")
var innercanvas = canvas
.append("g")
.attr("class", "innercanvas")
.attr("transform", "translate(" + cfg.margin.left + "," + cfg.margin.top + ")");
treemap = d3.layout
.treemap()
.round(false)
.size([cfg.width, cfg.height])
.padding(.25)
.sticky(true)
.nodes(data);
var cells = innercanvas
.selectAll(".newcell")
.data(treemap)
.enter()
.append("g")
.attr("class", function (d, i) {
return 'newcell _' + i // i provides a unique identifier for each node
+ ' cell-level-' + d.depth // cell-level-0 for root, cell-level-1, cell-level-2, etc
+ (d.name ? ' ' + safe_name(d.name) : '') // if d.name exists, use the 'safe' version
+ (!d.children
? ' leaf' // d has no children => it's a leaf node
: (d.depth === 0
? ' root' // d.depth = 0 => it's the root node
: ' internal ')); // has children, depth > 0 => internal node
})
cells
.append("rect")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
})
.attr("id", "rectangle")
.attr("width", function (d) {
return d.dx;
})
.attr("height", function (d) {
return d.dy;
})
.style("fill", function (d) {
return d.children && d.parent ? defaultColors(d.name) : cfg.color ? cfg.color(d.name) : null;
})
.attr("stroke", "#000000")
.attr('pointer-events', 'all');
cells
.append("text")
.attr("x", function (d) {
return d.x + d.dx / 2;
})
.attr("y", function (d) {
return d.y + d.dy / 2;
})
.attr("text-anchor", "middle")
.text(function (d) { return d.parent ? d.name : '' })
cells
.append('title')
.text(function (d) {
if (d.parent) {
return categoryKey + " : " + d.parent.name + "\n" + groupKey + " : " + d.name + "\n" + sizeKey + " : " + toCommas(d.value.toFixed(2))
}
return d.name;
});
Wrapping the texts into multiple lines
function groupAddText(selection) {
var v_pad = 2, // vertical padding
h_pad = 4 // horizontal padding
selection.selectAll('.leaf text')
.classed('groupOversize', function (d) {
if (!d.name) {
return false;
}
var bbox = this.getBBox();
if (d.dx <= bbox.width + h_pad || d.dy <= bbox.height + v_pad) {
d3.select(this).node().textContent = "";
var lines = wordwrap2(d.name, d.dx).split('\n');
for (var i = 0; i < lines.length; i++) {
d3.select(this)
.append("tspan")
.attr("dy", 15)
.attr("x", d.x + d.dx / 2)
.text(lines[i]);
}
d3.selectAll(".groupOversize").attr("y", function (d) {
return (d.y + d.dy / 2) - 20;
})
return true;
}
return false;
});
}
function wordwrap2(str, width, brk, cut) {
brk = brk || '\n';
width = width || 75;
cut = cut || false;
if (!str) { return str; }
var regex = '.{1,' + width + '}(\\s|$)' + (cut ? '|.{' + width + '}|.+$' : '|\\S+?(\\s|$)');
return str.match(RegExp(regex, 'g')).join(brk);
}
This produces the following treemap visualization :
As you can see, I separated my cells into 3 classes, root for the root node, internal for the parents and leaf for the children. Right now, it is just showing the children rects. How do I show the parent elements with the children nested inside them?
I want something like this :
[![enter image description here][2]][2]

You can view the parent cells by adding padding to your treemap:
treemap = d3.layout
.treemap()
.round(false)
.size([cfg.width, cfg.height])
.padding(20) // 20px padding all around
.sticky(true)
.nodes(data);
or
treemap = d3.layout
.treemap()
.round(false)
.size([cfg.width, cfg.height])
.padding([20,5,5,5]) // 20px top, 5px sides and bottom
.sticky(true)
.nodes(data);
I made a little demo showing the effects of altering the padding in a d3 treemap here - although note that that is d3 v5, so the options are slightly different.
Here's a demo with your code:
var data = {"children":[{"name":"Central","children":[{"name":"Cellophane Tape","value":"419141.4728"},{"name":"File Separator","value":"327285.0157"},{"name":"Hard Cover File","value":"422707.1194"},{"name":"Highlighter","value":"488978.5362"},{"name":"Office Chair","value":"453843.621"},{"name":"Pencil","value":"416819.1027"},{"name":"Tape Dispenser","value":"393290.5862"},{"name":"File Cabinet","value":"424647.6003"},{"name":"Plastic Comb Binding","value":"230299.6657"},{"name":"White Board Markers","value":"383157.5055"},{"name":"Binder","value":"415871.6793"},{"name":"Eraser","value":"477885.9162"},{"name":"Pen","value":"444834.4362"},{"name":"Pen Set","value":"434495.1303"},{"name":"Desk","value":"247046.3919"}]},{"name":"East","children":[{"name":"Pencil","value":"441970.1055"},{"name":"White Board Markers","value":"416822.5561"},{"name":"Eraser","value":"393738.4951"},{"name":"Hard Cover File","value":"407371.1911"},{"name":"Office Chair","value":"382574.6347"},{"name":"Tape Dispenser","value":"481960.7562"},{"name":"Cellophane Tape","value":"441438.7362"},{"name":"File Cabinet","value":"333187.8858"},{"name":"Binder","value":"462926.3793"},{"name":"File Separator","value":"441311.7555"},{"name":"Plastic Comb Binding","value":"330059.7762"},{"name":"Highlighter","value":"399332.0562"},{"name":"Pen","value":"492374.2362"},{"name":"Pen Set","value":"477206.7762"},{"name":"Desk","value":"254464.9453"}]},{"name":"North","children":[{"name":"Office Chair","value":"459306.6555"},{"name":"Pencil","value":"465763.0477"},{"name":"Eraser","value":"441687.1652"},{"name":"File Cabinet","value":"463598.5893"},{"name":"File Separator","value":"430346.1162"},
{"name":"Hard Cover File","value":"346325.0175"},{"name":"Highlighter","value":"223199.4072"},{"name":"Tape Dispenser","value":"311201.7216"},{"name":"Plastic Comb Binding","value":"445513.5762"},{"name":"Binder","value":"453219.921"},{"name":"White Board Markers","value":"334737.9189"},{"name":"Cellophane Tape","value":"372554.952"},{"name":"Pen","value":"435830.2872"},{"name":"Pen Set","value":"460001.8962"},{"name":"Desk","value":"260294.2303"}]},{"name":"South","children":[{"name":"Pencil","value":"457331.6055"},{"name":"Tape Dispenser","value":"442628.4555"},{"name":"Cellophane Tape","value":"468037.3351"},{"name":"Eraser","value":"341469.2127"},{"name":"File Cabinet","value":"408198.2058"},{"name":"File Separator","value":"416543.8893"},{"name":"Office Chair","value":"466438.7227"},{"name":"Plastic Comb Binding","value":"436440.1272"},{"name":"White Board Markers","value":"437968.1344"},{"name":"Highlighter","value":"411905.4555"},{"name":"Binder","value":"456806.1151"},{"name":"Hard Cover File","value":"493053.3762"},{"name":"Pen","value":"413820.3762"},{"name":"Pen Set","value":"488299.3962"},{"name":"Desk","value":"264499.5623"}]},{"name":"West","children":[{"name":"Pencil","value":"458648.3055"},{"name":"Cellophane Tape","value":"299045.7162"},{"name":"File Cabinet","value":"386045.352"},{"name":"File Separator","value":"435098.0403"},{"name":"Highlighter","value":"457454.0701"},{"name":"Office Chair","value":"262021.1055"},{"name":"Plastic Comb Binding","value":"413222.1555"},{"name":"Eraser","value":"449997.2978"},{"name":"Hard Cover File","value":"364335.5793"},{"name":"Binder","value":"467389.3801"},{"name":"Tape Dispenser","value":"394066.5845"},{"name":"White Board Markers","value":"408833.4789"},{"name":"Pen","value":"481281.6162"},{"name":"Pen Set","value":"398652.9162"},{"name":"Desk","value":"229482.2954"}]}]};
data.name = 'root'
var defaultColors = d3.scale.ordinal().range(["#00AEEF", "#8DC63F", "#FFC20E", "#F06EAA", "#AE9986", "#009BA5", "#00A651", "#F7941D", "#B656AB", "#ABA000", "#F3716D", "#8D7B6B", "#EF413D", "#AD772B", "#878787"]);
var treemap;
var h_pad = 2, // 2 pixels vertical padding
v_pad = 4; // 4 pixels of horizontal padding (2 px at each side)
var id = 'treemap';
var cfg = { width: 960, height: 500, margin: { left: 10, right: 10, bottom: 10, top: 10 }, color: d3.scale.category20() }
var canvas = d3
.select('#' + id)
.append("svg")
.attr("class", "chart")
.attr("width", cfg.width + cfg.margin.left + cfg.margin.right)
.attr("height", cfg.height + cfg.margin.top + cfg.margin.bottom)
.attr("viewBox", "0 0 960 500")
.attr("preserveAspectRatio", "xMidYMid meet")
.attr("id", "canvas")
var innercanvas = canvas
.append("g")
.attr("class", "innercanvas")
.attr("transform", "translate(" + cfg.margin.left + "," + cfg.margin.top + ")");
treemap = d3.layout
.treemap()
.round(false)
.size([cfg.width, cfg.height])
.padding([20,5,5,5])
.sticky(true)
.nodes(data);
var cells = innercanvas
.selectAll(".newcell")
.data(treemap)
.enter()
.append("g")
.attr("class", function (d, i) {
return 'newcell _' + i // i provides a unique identifier for each node
+ ' cell-level-' + d.depth // cell-level-0 for root, cell-level-1, cell-level-2, etc
+ (d.name ? ' ' + d.name : '') // if d.name exists, use the 'safe' version
+ (!d.children
? ' leaf' // d has no children => it's a leaf node
: (d.depth === 0
? ' root' // d.depth = 0 => it's the root node
: ' internal ')); // has children, depth > 0 => internal node
})
cells
.append("rect")
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.y;
})
.attr("id", function(d,i){ return 'rect_' + i; })
.attr("width", function (d) {
return d.dx;
})
.attr("height", function (d) {
return d.dy;
})
.style("fill", function (d) {
return d.children && d.parent ? defaultColors(d.name) : cfg.color ? cfg.color(d.name) : null;
})
.attr("stroke", "#000000")
.attr('pointer-events', 'all');
cells.append("clipPath")
.attr("id", function(d,i) { return "clip_" + i ; })
.append("use")
.attr("xlink:href", function(d,i) {
return "#rect_" + i;
});
cells
.append("text")
.attr("clip-path", function(d,i) { return "url(#clip_" + i })
.attr("x", function (d) {
return d.x + d.dx / 2;
})
.attr("y", function (d) {
return d.children ? d.y + 12 : d.y + d.dy / 2 ;
})
.attr("text-anchor", "middle")
.text(function (d) { return d.name })
cells
.append('title')
.text(function (d) {
if (d.parent) {
return "categoryKey : " + d.parent.name + "\ngroupKey : " + d.name + "\nsizeKey : " + d.value.toFixed(2)
}
return d.name;
});
svg text {
font-size: 10px;
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script><div id="treemap"></div>

Related

Link Paths disappears on re-call same tree function - D3 tree

I am able to draw tree structure with json data on initial load of the application.
Now my requirement is, on click of button I should be able to add more text/css to each nodes which is working fine.
I am actually using the same function used to draw tree initially, able to interchange data on trigger of button.
When I do so, on click the first level node (to expand to second level), first level paths disappears from g tag and selected node changes the position and lies in second level data.
Tired all possible ways, can anyone help to resolve this.
thanks in advance
Below is the tree function used
//To draw tree structure for the selected data
drawTree: function() {
var driverTree = this;
var id = "#" + this.getView().byId("chartArea").sId;
// ************** Generate the tree diagram *****************
var treeData = driverTree.treeModel.oData;
var radius = 40;
var width = $(id).width();
var height = window.innerHeight;
var maxLabel = 1500;
var i = 0,
duration = 450,
root;
var tree = d3.layout.tree()
.size([height, width])
.separation(function(a, b) {
return (a.parent == b.parent ? 1 : 2);
});
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
}
var slider = d3.scale.linear()
.domain([1, 100])
.range([1, 100])
.clamp(true);
var canvasWidth = radius * 2 + margin.left + margin.right,
canvasHeight = radius * 2 + margin.top + margin.bottom;
var color = d3.scale.category10();
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var pie = d3.layout.pie()
.value(function(d) {
if (isNaN(parseFloat(d.value)))
return 0;
else
return parseFloat(d.value);
})
.sort(null);
// arc object
var arc = d3.svg.arc()
.outerRadius(40)
.innerRadius(20);
//var id = "#" + chartArea.sId;
d3.select(id).select("svg").remove();
driverTree.zoomListener = d3.behavior.zoom()
.scaleExtent([0.1, 3])
.on("zoom", function(d) {
zoomed(d);
});
var svgGroup = d3.select(id).append("svg")
.attr("width", width)
.attr("height", height)
.attr("class", "tooltip")
.call(driverTree.zoomListener);
function zoomed(d) {
if (driverTree.hoizMode)
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")rotate(90,50,50)");
else
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var svg = svgGroup.append("g")
.attr("transform", "translate(" + width + "," + (height / 2) / 2 + ") rotate(90,50,50)");
root = treeData;
root.x0 = height / 2;
root.y0 = 0;
var div = d3.select("body").append("div").attr("class", "tooltip").style("opacity", "0");
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
//update(root);
//d3.select(self.frameElement).style("height", "800px");
var circumference_r = 35;
function update(source) {
var levelWidth = [1];
var childCount = function(level, n) {
if (n.children && n.children.length > 0) {
if (levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level + 1] += n.children.length;
n.children.forEach(function(d) {
childCount(level + 1, d);
});
}
};
childCount(0, root);
var newHeight = d3.max(levelWidth) * 263.6; // 25 pixels per line
tree = tree.size([newHeight, window.innerHeight]);
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// Update the nodes…
var node = svg.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("g")
.attr("id", function(d) {
return "id" + d.title;
})
.attr("class", "node")
.attr("transform", function(d) {
if (driverTree.hoizMode)
return "translate(" + source.y0 + "," + source.x0 + ") rotate(-90)";
else
return "translate(" + source.y0 + "," + source.x0 + ") rotate(0)";
})
.on("mouseover", function(d) {
mouseover(d);
})
.on("mousemove", function(d) {
mousemove(d);
})
.on("mouseout", function(d) {
mouseout(d);
});
if (driverTree.editTree) {
nodeEnter.append("rect")
.attr("id", "hideButton")
.attr("width", function(d) {
if (d.id != "NODE1") return "40px";
})
.attr("transform", function(d) {
if (d.id != "NODE1") return "translate(-40,-70)";
})
.attr("rx", "5")
.attr("ry", "5")
.attr("height", function(d) {
if (d.id != "NODE1") return "25px";
})
.style("stroke", "#999faa")
.style("stroke-width", "2px")
.style("fill", function(d) {
if (d.isHidden) return "#000f2b";
else return "#8E94A1";
})
.on("click", function(d) {
d.isHidden = !d.isHidden;
if (d.isHidden) return this.style.fill = "#000f2b";
else return this.style.fill = "#8E94A1";
});
nodeEnter.append("text")
.attr("id", "hideText")
.attr("transform", "translate(-31,-54)")
.style("fill", "#eee")
.text(function(d) {
if (d.id != "NODE1") return "Hide";
})
.on("click", function(d) {
d.isHidden = !d.isHidden;
if (d.isHidden) return d3.select(this.parentNode).select("rect").style("fill", "#000f2b");
else return d3.select(this.parentNode).select("rect").style("fill", "#8E94A1");
});
}
nodeEnter.append("circle")
.attr("r", "40")
.on("click", click);
nodeEnter.append("text")
.attr("id", function(d) {
return "ct" + d.title;
})
.attr("x", "-8")
.attr("y", "4")
.text(function(d) {
return brevoVDT.util.Formatter.amountToMillions(d.value);
})
.on("click", click);
nodeEnter.append("text")
.attr("transform", "translate(55,-10)")
.text(function(d) {
return "Org Value - " + brevoVDT.util.Formatter.amountToMillions(d.value_org);
})
nodeEnter.append("text")
.attr("id", function(d) {
return "cv" + d.title;
})
.attr("transform", "translate(55,5)")
.text(function(d) {
return "Currenet Value - " + brevoVDT.util.Formatter.amountToMillions(d.value);
})
nodeEnter.append("text")
.attr("id", function(d) {
return "dv" + d.title;
})
.attr("transform", "translate(55,20)")
.text(function(d) {
return "Difference " + brevoVDT.util.Formatter.amountToMillions(parseFloat(d.value_org) - parseFloat(d.value));
})
.style("fill", function(d) {
var value = parseFloat(d.value_org) - parseFloat(d.value);
if (value > 0) return "green";
else if (value < 0) return "red";
else return "black";
})
/*nodeEnter.append("text")
.attr("transform", "translate(-50,58)")
.attr("text-anchor", "start")
.text(function(d) {
return d.title;
})*/
nodeEnter.append("rect")
.attr("width", "130px")
.attr("transform", "translate(48,-25)")
.attr("height", "50px")
.attr("rx", "15")
.attr("ry", "15")
.style("fill", "rgba(238, 238, 238, 0)")
.style("opacity", "0.9")
.style("stroke",
function(d) {
if (d.children == null && d._children == null)
return "black";
else
return "orange";
})
.style("stroke-width", "2");
nodeEnter.append("g").attr("class", "slicers");
nodeEnter.append("g").attr("class", "lines");
var pieNodes = nodeEnter.select(".slicers").selectAll("path")
.data(function(d, i) {
if (d.id == "NODE1") var value = 0;
else var value = d.parent.value;
value = value == 0 ? 1 : value;
return pie([d, {
value: value
}]);
})
.enter()
.append("svg:path")
.attr("class", "slice")
.attr("fill", function(d, i) {
return color(i);
})
.each(function(d) {
d;
})
.attr('d', arc)
//toolbar
function mouseover(d) {
div.transition()
.duration(500)
.style("opacity", 0.9)
.style("display", "block");
}
function mousemove(d) {
var name = d.name.length > 13 ? d.name.slice("0", "13") : d.name;
div.html("<b>" + d.title + "</b><table>" +
"<tr><td>" + name + ":</td><td>" + parseFloat(d.value).toFixed(1) + "</td></tr></table>")
.style("left", (d3.event.pageX + 50) + "px")
.style("top", (d3.event.pageY + 0) + "px");
}
function mouseout(d) {
div.transition()
.duration(500)
.style("display", "none");
}
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
if (driverTree.hoizMode)
return "translate(" + d.y + "," + d.x + ") rotate(-90)";
else
return "translate(" + d.y + "," + d.x + ") rotate(0)";
});
nodeUpdate.select("circle");
nodeUpdate.select("text");
nodeUpdate.select("rect");
nodeUpdate.select("foreignObject");
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
if (driverTree.hoizMode)
return "translate(" + source.y + "," + source.x + ") rotate(-90)";
else
return "translate(" + source.y + "," + source.x + ") rotate(0)";
})
.remove();
nodeExit.select("circle");
nodeExit.select("text");
nodeExit.select("rect");
nodeExit.select("foreignObject");
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y + 100
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Function to center node when clicked/dropped so node doesn't get lost when collapsing/moving with large amount of children.
function centerNode(source) {
var scale = driverTree.zoomListener.scale();
var x = -source.y0;
var y = -source.x0;
if (driverTree.hoizMode) {
d3.select("g").transition()
.duration(1)
.attr("transform", "translate(" + width + "," + (height / 2) / 2 + ")scale(" + scale + ")rotate(90,50,50)");
driverTree.zoomListener.translate([width, (height / 2) / 2]);
} else {
d3.select("g").transition()
.duration(1)
.attr("transform", "translate(" + height / 2 + "," + -width / 2 + ")scale(" + scale + ")");
driverTree.zoomListener.translate([height / 2, -width / 2]);
}
driverTree.zoomListener.scale(scale);
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
centerNode(d)
}
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
update(root);
centerNode(root);
Below is my edit function code
// To make the tree editable
handleEdit: function(evt) {
var that = this;
this.editTree = !this.editTree;
this.treeModel.oData.x = 0;
this.treeModel.oData.y = 0;
this.treeModel.oData.x0 = 0;
this.treeModel.oData.y0 = 0;
this.treeModel.oData.children.forEach(function(d){
d.x = 0; d.y = 0; d.x0 = 0; d.y0 = 0;
var children = (d._children == undefined ? d.children : d._children );
children.forEach(function(a){
a.x = 0; a.y = 0; a.x0 = 0; a.y0 = 0;
});
});
this.drawTree();
},

How to update a tree graph in D3

When I'm working with a tree graph in d3, I get the behavior I want up through adding a node in a tree graph. However, when I'm adding a node to the graph, the node (and path) get placed, but the nodes aren't balanced out and the previous nodes do not move.
I've posted a working version of the entire code here: https://codepen.io/auser/pen/mwwVJL
The JS code (for completeness sake is):
const pathGraph = (eleName, treeData, opts = {}) => {
var margin = { top: 40, right: 90, bottom: 50, left: 90 },
width = 660 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
const treemap = d3.tree().size([width, height]);
const svg = d3
.select(eleName)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
let g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const updateData = newData => {
// assigns the data to a hierarchy using parent-child relationships
let nodes = treemap(d3.hierarchy(newData));
const color = d3.scaleOrdinal(d3.schemeCategory10).domain(d3.range(0, 8));
// adds the links between the nodes
const link = g
.selectAll(".link")
.data(nodes.descendants().slice(1))
.enter()
.append("path")
.attr("class", "link")
.style("stroke-width", 1)
.attr("d", function(d) {
return (
"M" + d.x + "," + d.y + "C" + d.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
);
});
link.exit().remove();
// adds each node as a group
const node = g
.selectAll(".node")
.data(nodes.descendants())
.enter()
.append("g")
.attr("class", function(d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.exit().remove();
const parentTree = (d) => {
let nodeLinks = []
while(d.parent) {
nodeLinks.push(d)
d = d.parent
}
nodeLinks = nodeLinks.concat(d)
return nodeLinks
}
const activeLink = (d, o) => {
if (d === o || d.parent === o) return true;
}
node
.on('mouseover', function(d) {
const data = d3.select(this)
const linkedNodes = parentTree(d)
link
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.style('stroke', o => activeLink(d, o) ? 'red' : '#333')
.transition(500)
rect
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.transition(500)
})
.on('mouseout', d => {
const data = d3.select(this);
// console.log('d ->', d)
link
.style('stroke-width', 1)
.style('stroke', '#333')
rect
.style('stroke-width', 1)
})
// adds the circle to the node
const rect = node
.append("rect")
.attr("height", 50)
.attr("width", 50)
.style("fill", (d, i) => color(i))
.attr("x", "-0.7em");
// adds the text to the node
node
.append("text")
.attr("dy", ".52em")
.attr("y", function(d) {
return d.children ? -18 : 20;
})
.attr("dx", "-.2em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.name;
});
};
updateData(treeData);
return updateData;
};
const data = {
"name": "Root",
"children": [
{
"name": "A",
"children": [
{ "name": "B" },
{ "name": "C" }
]
},
{ "name": "D" },
{ "name": "E",
"children":[
{ "name": "F"}
] }
]
};
const mount = document.querySelector('#treea')
const updateData = pathGraph(mount, data)
setTimeout(function() {
data.children[2].children.push({ name: "H" })
updateData(data, { update: true })
}, 2000)
.tree .node rect, .tree .node circle {
fill: blue;
rounding: 5px;
}
.tree .link {
fill: none;
stroke: #222;
stroke-opacity: 1;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg id="treea" class="tree"></svg>
Any help would be greatly appreciated...
You need a proper "enter", "update" and "exit" selections. Like this:
//this is the update selection
const link = g
.selectAll(".link")
.data(nodes.descendants().slice(1));
//this is the enter selection, up to the 'merge'
link.enter()
.append("path")
.attr("class", "link")
.merge(link)//from now one, update + enter
.style("stroke-width", 1)
.attr("d", function(d) {
return (
"M" + d.x + "," + d.y + "C" + d.x + "," +
(d.y + d.parent.y) / 2 + " " + d.parent.x + "," +
(d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
);
});
//this is the exit selection
link.exit().remove();
Here is the updated Codepen: https://codepen.io/anon/pen/RggVPO?editors=0010
And here the Stack snippet:
const pathGraph = (eleName, treeData, opts = {}) => {
var margin = { top: 40, right: 90, bottom: 50, left: 90 },
width = 660 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
const treemap = d3.tree().size([width, height]);
const svg = d3
.select(eleName)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
let g = svg
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const updateData = newData => {
// assigns the data to a hierarchy using parent-child relationships
let nodes = treemap(d3.hierarchy(newData));
const color = d3.scaleOrdinal(d3.schemeCategory20).domain(d3.range(0, 8));
// adds the links between the nodes
const link = g
.selectAll(".link")
.data(nodes.descendants().slice(1));
link.enter()
.append("path")
.attr("class", "link")
.merge(link)
.style("stroke-width", 1)
.attr("d", function(d) {
return (
"M" + d.x + "," + d.y + "C" + d.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + (d.y + d.parent.y) / 2 + " " + d.parent.x + "," + d.parent.y
);
}).lower();
link.exit().remove();
const node = g
.selectAll(".node")
.data(nodes.descendants())
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
const nodeEnter = node.enter()
.append("g")
.attr("class", function(d) {
return "node " + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.exit().remove();
const parentTree = (d) => {
let nodeLinks = []
while(d.parent) {
nodeLinks.push(d)
d = d.parent
}
nodeLinks = nodeLinks.concat(d)
return nodeLinks
}
const activeLink = (d, o) => {
if (d === o || d.parent === o) return true;
}
node
.on('mouseover', function(d) {
const data = d3.select(this)
const linkedNodes = parentTree(d)
link
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.style('stroke', o => activeLink(d, o) ? 'red' : '#333')
.transition(500)
rect
.style('stroke-width', o => activeLink(d, o) ? 4 : 1)
.transition(500)
})
.on('mouseout', d => {
const data = d3.select(this);
// console.log('d ->', d)
link
.style('stroke-width', 1)
.style('stroke', '#333')
rect
.style('stroke-width', 1)
})
// adds the circle to the node
const rect = nodeEnter
.append("rect")
.attr("height", 50)
.attr("width", 50)
.style("fill", (d, i) => color(i))
.style('padding', 5)
.attr("rx", 6)
.attr("ry", 6)
.attr("x", "-0.7em");
// adds the text to the node
nodeEnter
.append("text")
.attr("dy", "0.8em")
.attr("y", function(d) {
return d.children ? -18 : 20;
})
.attr("dx", "0.8em")
.style("text-anchor", "middle")
.text(function(d) {
return d.data.name;
});
node.select("rect").attr("x", "-0.7em");
};
updateData(treeData);
return updateData;
};
const data = {
"name": "Root",
"children": [
{
"name": "A",
"children": [
{ "name": "B" },
{ "name": "C" }
]
},
{ "name": "D" },
{ "name": "E",
"children":[
{ "name": "F"}
] }
]
};
const mount = document.querySelector('#treea')
const updateData = pathGraph(mount, data)
setTimeout(function() {
data.children[2].children.push({ name: "H" })
updateData(data, { update: true })
}, 2000)
.tree .node rect, .tree .node circle {
fill: blue;
rounding: 5px;
}
.tree .link {
fill: none;
stroke: #222;
stroke-opacity: 1;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
<svg id="treea" class="tree"></svg>
PS: Things are more complicated to the nodes, because you have groups with rectangles and texts. I quickly changed the selections, but I suggest you to refactor that part of the code accordingly.

use d3 treemap to go upto child on going on clicking parent to child nodes

I have tried this code .I am new to d3.
but able to directly reach to child node directly
def city ,xyz city and abc city are displayed on treemap
data.json
{
"name":"country",
"children":
[
{
"name": "Verizona State",
"value": 100,
"children": [
{
"name": "xyz city",
"value": 30
},
{
"name": "abc city",
"value": 40
}
]
},
{
"name": "New Jersey",
"value": 50,
"children": [
{
"name": "def city",
"value": 30
}
]
}
]
}
index.html
<html>
<script src="http://d3js.org/d3.v3.min.js"></script>
<body>
<div id="heatmap">
<script>
var color = d3.scale.category10();
var canvas = d3.select("#heatmap").append("svg")
.attr("width",500)
.attr("height",500);
d3.json("data.json" ,function(data){
var treemap=d3.layout.treemap()
.size([500,500])
.nodes(data);
console.log(treemap);
var cells = canvas.selectAll(".cell")
.style("position", "relative")
.data(treemap)
.enter()
.append("g")
.attr("class","cell")
.attr("stroke","#fff");
cells.append("rect")
.attr( "x" , function(d) { console.log(d); return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) { return d.dx; })
.attr("height", function(d) { return d.dy; })
.attr("fill", function(d){return d.children ? null:color(d.parent.name); })
cells.append("text")
.attr("x",function(d) {return d.x + d.dx/2})
.attr("y",function(d) {return d.y + d.dy/2})
.text(function(d){ return d.children? null :d.name;})
})
</script>
</div>
</body>
</html>
I want a way to display first
Verizona State and New Jersey on treemap
and on clicking verizona state to get xyz city and abc city on treemap
Please suggest changes.
I solved this problem using this example of mike
http://bost.ocks.org/mike/treemap/
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script></script>
<script>
var margin = {top: 20, right: 0, bottom: 0, left: 0},
width = 960,
height = 500 - margin.top - margin.bottom,
formatNumber = d3.format(",d"),
transitioning;
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, height])
.range([0, height]);
var treemap = d3.layout.treemap()
.children(function(d, depth) { return depth ? null : d._children; })
.sort(function(a, b) { return a.value - b.value; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false);
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", -margin.left + "px")
.style("margin.right", -margin.right + "px")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.style("shape-rendering", "crispEdges");
var grandparent = svg.append("g")
.attr("class", "grandparent");
grandparent.append("rect")
.attr("y", -margin.top)
.attr("width", width)
.attr("height", margin.top);
grandparent.append("text")
.attr("x", 6)
.attr("y", 6 - margin.top)
.attr("dy", ".75em");
d3.json("flare.json", function(root) {
initialize(root);
accumulate(root);
layout(root);
display(root);
function initialize(root) {
root.x = root.y = 0;
root.dx = width;
root.dy = height;
root.depth = 0;
}
// Aggregate the values for internal nodes. This is normally done by the
// treemap layout, but not here because of our custom implementation.
// We also take a snapshot of the original children (_children) to avoid
// the children being overwritten when when layout is computed.
function accumulate(d) {
return (d._children = d.children)
? d.value = d.children.reduce(function(p, v) { return p + accumulate(v); }, 0)
: d.value;
}
// Compute the treemap layout recursively such that each group of siblings
// uses the same size (1×1) rather than the dimensions of the parent cell.
// This optimizes the layout for the current zoom state. Note that a wrapper
// object is created for the parent node for each group of siblings so that
// the parent’s dimensions are not discarded as we recurse. Since each group
// of sibling was laid out in 1×1, we must rescale to fit using absolute
// coordinates. This lets us use a viewport to zoom.
function layout(d) {
if (d._children) {
treemap.nodes({_children: d._children});
d._children.forEach(function(c) {
c.x = d.x + c.x * d.dx;
c.y = d.y + c.y * d.dy;
c.dx *= d.dx;
c.dy *= d.dy;
c.parent = d;
layout(c);
});
}
}
function display(d) {
grandparent
.datum(d.parent)
.on("click", transition)
.select("text")
.text(name(d));
var g1 = svg.insert("g", ".grandparent")
.datum(d)
.attr("class", "depth");
var g = g1.selectAll("g")
.data(d._children)
.enter().append("g");
g.filter(function(d) { return d._children; })
.classed("children", true)
.on("click", transition);
g.selectAll(".child")
.data(function(d) { return d._children || [d]; })
.enter().append("rect")
.attr("class", "child")
.call(rect);
g.append("rect")
.attr("class", "parent")
.call(rect)
.append("title")
.text(function(d) { return formatNumber(d.value); });
g.append("text")
.attr("dy", ".75em")
.text(function(d) { return d.name; })
.call(text);
function transition(d) {
if (transitioning || !d) return;
transitioning = true;
var g2 = display(d),
t1 = g1.transition().duration(750),
t2 = g2.transition().duration(750);
// Update the domain only after entering new elements.
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
// Enable anti-aliasing during the transition.
svg.style("shape-rendering", null);
// Draw child nodes on top of parent nodes.
svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; });
// Fade-in entering text.
g2.selectAll("text").style("fill-opacity", 0);
// Transition to the new view.
t1.selectAll("text").call(text).style("fill-opacity", 0);
t2.selectAll("text").call(text).style("fill-opacity", 1);
t1.selectAll("rect").call(rect);
t2.selectAll("rect").call(rect);
// Remove the old node when the transition is finished.
t1.remove().each("end", function() {
svg.style("shape-rendering", "crispEdges");
transitioning = false;
});
}
return g;
}
function text(text) {
text.attr("x", function(d) { return x(d.x) + 6; })
.attr("y", function(d) { return y(d.y) + 6; });
}
function rect(rect) {
rect.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y); });
}
function name(d) {
return d.parent
? name(d.parent) + "." + d.name
: d.name;
}
});
</script>
<script>
</script>

D3.js How do I rotate a Marimekko chart

I have a Marimekko chart where the bars are aligned vertically (with a limited number of bars).
However, the eventual chart will have so many bars that it would be better with a horizontal layout which would support a larger number of values.
I've tried to modify the chart by reversing x and y values but the result does not work properly. I want the first month in the data to appear at the top of the chart.The working code with vertical bars (no data) is below and here.
var width = 700,
height = 500,
margin = 20;
var color = d3.scale.category20();
var x = d3.scale.linear()
.range([0, width - 3 * margin]);
var y = d3.scale.linear()
.range([0, height - 2 * margin]);
var n = d3.format(",d"),
p = d3.format("%");
var svg = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 2 * margin + "," + margin + ")");
d3.json("/mydrupal/sites/default/d3_files/json/marimekko6.json",
function(error,data) {
var offset = 0;
// Nest values by month. We assume each month + cause is unique.
var months = d3.nest()
.key(function(d) {
return d.month;
})
.entries(data);
// Compute the total sum, the per-month sum, and the per-cause offset.
// You can use reduce rather than reduceRight to reverse the ordering.
// We also record a reference to the parent cause for each month.
var sum = months.reduce(function(v, p) {
return (p.offset=v) + (p.sum=p.values.reduceRight(function(v, d) {
d.parent = p;
return (d.offset = v) + d.deaths;
}, 0));
}, 0);
// Add a group for each cause.
var months = svg.selectAll(".month")
.data(months)
.enter()
.append("g")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.key;
})
.attr("transform", function(d) {
return "translate(" + x(d.offset / sum) + ")";
});
// Add a rect for each month.
var causes = months.selectAll (".cause")
.data(function(d) {
return d.values;
})
.enter()
.append("a")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.cause + " " + d.parent.key + ": " + n(d.deaths);
});
causes.append("rect")
.attr("y", function(d) {
return y(d.offset / d.parent.sum);
})
.attr("height", function(d) {
return y(d.deaths / d.parent.sum);
})
.attr("width", function(d) {
return x(d.parent.sum / sum);
})
.style("fill", function(d) {
return color(d.cause);
});
// see http://stackoverflow.com/questions/17574621/
// text-on-each-bar-of-a-stacked-bar-chart-d3-js
causes.append("text")
.text(function(d) {
return d.cause + " " + n(d.deaths);
})
.attr("x", 5)
.attr("y", function(d) {
return (y(d.offset / d.parent.sum)+20);
})
.attr("class", "label");
causes.append("text")
.text(function(d) {
return (" Total: " + d.parent.sum);
}) // total
.attr("x", 5)
.attr("y", function(d) {
return 450;
})
.attr("class", "label2");
causes.append("text")
.text(function(d) {
return d.parent.key;
}) // month
.attr("x", 5)
.attr("y", function(d) {
return 480;
})
.attr("class", "label2");
});
Here's the fix. Basically you need to change x and y as well as width and the height.
// Add a group for each cause.
var months = svg.selectAll(".month")
.data(months)
.enter().append("g")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.key; })
.attr("transform", function(d) {
return "translate(0," + y(d.offset / sum) + ")";
});
// Add a rect for each month.
var causes = months.selectAll (".cause")
.data(function(d) {
return d.values; })
.enter().append("a")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.cause + " " + d.parent.key + ": " + n(d.deaths); });
causes.append("rect")
.attr("x", function(d) {
return x(d.offset / d.parent.sum); })
.attr("width", function(d) {
return x(d.deaths / d.parent.sum); })
.attr("height", function(d) {
return y(d.parent.sum / sum); })
.style("fill", function(d) {
return color(d.cause);
});

d3.js how do I add text to Marimekko chart

I am trying to add text to a working Marimekko chart. I understand that adding text to a rect requires that the rect and text need to be in a group. But the code I used as a base already uses a group. That is my main question, but I'd also like to make the x axes display the month rather than a %value.
Is this mission impossible for a marimekko chart?
<div id="chart"> </div>
<div id="legend"> </div>
<script>
var width = 800,
height = 500,
margin = 20;
var color = d3.scale.category10();
var x = d3.scale.linear()
.range([0, width - 3 * margin]);
var y = d3.scale.linear()
.range([0, height - 2 * margin]);
var n = d3.format(",d"),
p = d3.format("%");
var svg = d3.select("#chart") .append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 2 * margin + "," + margin + ")");
d3.json("/mydrupal/sites/default/d3_files/json/marimekko6.json", function(error,data) {
var offset = 0;
// Nest values by month. We assume each month + cause is unique.
var months = d3.nest()
.key(function(d) {
return d.month;
})
.entries(data);
// Compute the total sum, the per-month sum, and the per-cause offset.
// You can use reduce rather than reduceRight to reverse the ordering.
// We also record a reference to the parent cause for each month.
var sum = months.reduce(function(v, p) {
return (p.offset = v) + (p.sum = p.values.reduceRight(function(v, d) {
d.parent = p;
return (d.offset = v) + d.deaths;
}, 0));
}, 0);
// Add x-axis ticks.
var xtick = svg.selectAll(".x")
.data(x.ticks(10))
.enter().append("g")
.attr("class", "x")
.attr("transform", function(d) {
return "translate(" + x(d) + "," + y(1) + ")";
});
xtick.append("line")
.attr("y2", 6)
.style("stroke", "#000");
xtick.append("text")
.attr("y", 8)
.attr("text-anchor", "middle")
.attr("dy", ".71em")
.text(p);
// Add y-axis ticks.
var ytick = svg.selectAll(".y")
.data(y.ticks(10))
.enter().append("g")
.attr("class", "y")
.attr("transform", function(d) {
return "translate(0," + y(1 - d) + ")";
});
ytick.append("line")
.attr("x1", -6)
.style("stroke", "#000");
ytick.append("text")
.attr("x", -8)
.attr("text-anchor", "end")
.attr("dy", ".35em")
.text(p);
// Add a group for each cause.
var months = svg.selectAll(".month")
.data(months)
.enter().append("g")
.attr("class", "month")
.attr("xlink:title", function(d) { return d.key; })
.attr("transform", function(d) {
return "translate(" + x(d.offset / sum) + ")";
});
// Add a rect for each month.
var causes = months.selectAll (".cause")
.data(function(d) { return d.values; })
.enter().append("a")
.attr("class", "month")
.attr("xlink:title", function(d) { return d.cause + " " + d.parent.key + ": " + n(d.deaths); })
.append("rect")
.attr("y", function(d) {
return y(d.offset / d.parent.sum); })
.attr("height", function(d) {
return y(d.deaths / d.parent.sum); })
.attr("width", function(d) {
return x(d.parent.sum / sum); })
.style("fill", function(d) {
return color(d.cause);
});
});
</script>
As noted above:
<!--HTML-->
<p>Marimekko Chart see http://bl.ocks.org/mbostock/1005090</p>
<div id="chart"> </div>
<!--CSS-->
<style type="text/css">body {
font: 10px sans-serif;
}
rect {
stroke: #000;
}
.label {
font-size: 12px;
fill: white;
}
.label2 {
font-size: 14px;
fill: black;
}
svg {
shape-rendering: crispEdges;
}
#chart {
margin-bottom: 20px;
}
</style>
<!--JavaScript-->
<script>
var width = 700,
height = 500,
margin = 20;
var color = d3.scale.category20();
var x = d3.scale.linear()
.range([0, width - 3 * margin]);
var y = d3.scale.linear()
.range([0, height - 2 * margin]);
var n = d3.format(",d"),
p = d3.format("%");
var svg = d3.select("#chart") .append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + 2 * margin + "," + margin + ")");
d3.json("/mydrupal/sites/default/d3_files/json/marimekko6.json", function(error,data) {
var offset = 0;
// Nest values by month. We assume each month + cause is unique.
var months = d3.nest()
.key(function(d) {
return d.month;
})
.entries(data);
// Compute the total sum, the per-month sum, and the per-cause offset.
// You can use reduce rather than reduceRight to reverse the ordering.
// We also record a reference to the parent cause for each month.
var sum = months.reduce(function(v, p) {
return (p.offset = v) + (p.sum = p.values.reduceRight(function(v, d) {
d.parent = p;
return (d.offset = v) + d.deaths;
}, 0));
}, 0);
// Add a group for each cause.
var months = svg.selectAll(".month")
.data(months)
.enter().append("g")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.key; })
.attr("transform", function(d) {
return "translate(" + x(d.offset / sum) + ")";
});
// Add a rect for each month.
var causes = months.selectAll (".cause")
.data(function(d) {
return d.values; })
.enter().append("a")
.attr("class", "month")
.attr("xlink:title", function(d) {
return d.cause + " " + d.parent.key + ": " + n(d.deaths); });
causes.append("rect")
.attr("y", function(d) {
return y(d.offset / d.parent.sum); })
.attr("height", function(d) {
return y(d.deaths / d.parent.sum); })
.attr("width", function(d) {
return x(d.parent.sum / sum); })
.style("fill", function(d) {
return color(d.cause);
});
// http://stackoverflow.com/questions/17574621/text-on-each-bar-of-a-stacked-bar-chart-d3-js
causes.append("text")
.text(function(d) {
return d.cause + " " + n(d.deaths);})
.attr("x", 5)
.attr("y", function(d) {
return (y(d.offset / d.parent.sum)+20); })
.attr("class", "label")
;
causes.append("text")
.text(function(d) {
return (" Total: " + d.parent.sum);}) // total
.attr("x", 5)
.attr("y", function(d) {
return 450 })
.attr("class", "label2")
;
causes.append("text")
.text(function(d) {
return d.parent.key;}) // month
.attr("x", 5)
.attr("y", function(d) {
return 480; })
.attr("class", "label2")
;
});
</script>

Categories

Resources