Related
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>
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.
So, I have been trying to visualize the popular tree of life data using dendogram layout of d3.js exactly similar to that of http://bl.ocks.org/mbostock/4063570.
I have problem with the links in the diagram as you can see in screenshot. I have also posted the code don't know where exactly I am going wrong.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(40,0)");
var tree = d3.cluster()
.size([height, width - 160]);
d3.text("test.txt", function(error, data) {
if (error) throw error;
var dat = parseNewick(data);
console.log(dat);
var root = d3.hierarchy(dat)
.sort(function(a,b){return b.height - a.height });
tree(root);
var link = g.selectAll(".link")
.data(root.descendants().slice(1))
.enter().append("path")
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.y + "," + d.x
+ "C" + (d.parent.y + 100) + "," + d.x
+ " " + (d.parent.y + 100) + "," + d.parent.x
+ " " + d.parent.y + "," + d.parent.x;
});
var node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
node.append("circle")
.attr("r", 2.5);
});
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);
});
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>