how to add title to d3js bubble chart with force layout - d3.js

This is the code of the bubble chart i created. I have used force layout to create the chart.
var margin = {
top: 10,
right: 10,
bottom: 10,
left: 10
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
d3.select('#' + divId).append('div').attr('id', 'chart').attr('class', 'chart');
var n = data.vistaJson.length;
m = 1,
padding = 5,
radius = d3.scale.sqrt().range([10, 50]),
color = d3.scale.category10().domain(d3.range(m)),
x = d3.scale.ordinal().domain(d3.range(m)).rangePoints([0, width], 1);
var xscale = d3.scale.linear()
.domain([0, 500])
.range([20, 500]);
var nodes = [];
for(var i=0; i< n; i++){
var coordinates = data.vistaJson[i].SLAB.split('_');
v = data.vistaJson[i].COUNT
nodes.push({
radius: radius(v),
color: color(i),
count: v,
cx: xscale(x(i)),
cy: xscale(height / 2),
xAxis: coordinates[0],
yAxis: coordinates[1]
});
}
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.gravity(0.5)
.charge(0.5)
.on("tick", tick)
.start();
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", function (d) {
return d.radius;
})
.style("fill", function (d) {
return d.color;
})
.call(force.drag);
var labels = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.attr({"x":function(d){return d.x;},
"y":function(d){return d.y;}})
.text(function(d){return d.count;})
.call(force.drag);
circle.each(gravity(.2 * e.alpha))
.each(collide(.5))
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
});
labels.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
// Move nodes toward cluster focus.
function gravity(alpha) {
return function (d) {
d.y += (d.cy - d.y) * alpha;
d.x += (d.cx - d.x) * alpha;
};
}
// Resolve collisions between nodes.
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function (d) {
var r = d.radius + radius.domain()[1] + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function (quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
I want to add title to the node which is displayed when mouse is hovered on the node .
Earlier I used pack layout and I gave title like this :
var node = vis.selectAll("g.node")
.data(bubble.nodes(classes(json), function(d) { return d.name; })
.filter(function(d) { return !d.children; }))
.enter()
.append("svg:g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + xscale(d.x) + "," + xscale(d.y) + ")"; });
node.append("svg:title")
.text(function(d) { return d.xAxis + ": " + d.yAxis; });
How can we show title when using forceLayout . Please help .

I think it should look something like this in the force layout:
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", function(d) { return d.nodes ? "nonleaf" : "leaf"; })
.attr("r", 5)
.call(force.drag);
node.append("title")
.text(function(d) { return d.count; });
I hope this helps somehow. For me it works like that..

Related

How can I add icons to this force simulation example in d3js and avoid code duplication?

I have two questions, one about the icons and other about code duplication.
I'm using this example:
http://bl.ocks.org/rveciana/6184054/bd294b921ebf2180eccc3aca548c895367fca2d2
(Thanks Roger Veciana)
I want to add an icon to each of those cirlces but I need to put the url inside a json instead of using the script to generate those cirlces (that is inside the code). Not sure what I'm doing wrong but is not working. Any tip on how can I add the icon to each circle ?
I've also added some text, and it is working. Basically I do this:
var circlemaint = svgmaint.selectAll("circle")
.data(nodes.maint)
.enter().append("g").append("circle")
.attr("r", function(d) { return d.radius; })
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.style("fill", function(d) { return d.color; })
.call(d3.drag);
var txtmaint = svgmaint.selectAll("circle")
.select('text')
.data(nodes.maint)
.enter().append('text')
.text(node => node.id)
.attr('font-size', 18)
.attr('dx', -25)
.attr('dy',20)
The issue here, is that I have 9 different svg's, and I don't want to duplicate this per svg. How can avoid code duplication?
I finally call it within the tick function and I do something like this:
function tick(e) {
simulation.alpha(0.2)
circlemaint
.each(gravity(this.alpha()))
.each(collide(.5))
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
txtmaint
.each(gravity(this.alpha()))
.each(collide(.5))
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
circleXXXX
......
txtXXXXXX
......
}
And here is the code of the example:
//Based in
///http://bl.ocks.org/mbostock/1804919
var margin = {
top: 0,
right: 0,
bottom: 0,
left: 0
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var rect = [50, 50, width - 50, height - 50];
var n = 20,
m = 4,
padding = 6,
maxSpeed = 3,
radius = d3.scale.sqrt().range([0, 8]),
color = d3.scale.category10().domain(d3.range(m));
var nodes = [];
for (i in d3.range(n)) {
nodes.push({
radius: radius(1 + Math.floor(Math.random() * 4)),
color: color(Math.floor(Math.random() * m)),
x: rect[0] + (Math.random() * (rect[2] - rect[0])),
y: rect[1] + (Math.random() * (rect[3] - rect[1])),
speedX: (Math.random() - 0.5) * 2 * maxSpeed,
speedY: (Math.random() - 0.5) * 2 * maxSpeed
});
}
var force = d3.layout.force()
.nodes(nodes)
.size([width, height])
.gravity(0)
.charge(0)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("svg:rect")
.attr("width", rect[2] - rect[0])
.attr("height", rect[3] - rect[1])
.attr("x", rect[0])
.attr("y", rect[1])
.style("fill", "None")
.style("stroke", "#222222");
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", function(d) {
return d.radius;
})
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.style("fill", function(d) {
return d.color;
})
.call(force.drag);
var flag = false;
function tick(e) {
force.alpha(0.1)
circle
.each(gravity(e.alpha))
.each(collide(.5))
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
// Move nodes toward cluster focus.
function gravity(alpha) {
return function(d) {
if ((d.x - d.radius - 2) < rect[0]) d.speedX = Math.abs(d.speedX);
if ((d.x + d.radius + 2) > rect[2]) d.speedX = -1 * Math.abs(d.speedX);
if ((d.y - d.radius - 2) < rect[1]) d.speedY = -1 * Math.abs(d.speedY);
if ((d.y + d.radius + 2) > rect[3]) d.speedY = Math.abs(d.speedY);
d.x = d.x + (d.speedX * alpha);
d.y = d.y + (-1 * d.speedY * alpha);
};
}
// Resolve collisions between nodes.
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + radius.domain()[1] + padding,
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.color !== quad.point.color) * padding;
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 ||
x2 < nx1 ||
y1 > ny2 ||
y2 < ny1;
});
};
}
<script src="https://d3js.org/d3.v3.js"></script>
Thank you so much!!

D3 force collision detection for ellipses

All,
I know there are many examples for D3.js collision detection for circles and rectangles.
I'm trying to do force simulation of ellipse nodes.
I tried following snippet which is originally for rectangles, but it's not perfect.
var width = 960,
height = 500,
minSize = 10,
maxSize = 30;
var n = 20,
m = 10;
var color = d3.scaleOrdinal(d3.schemeCategory10)
.domain(d3.range(m));
var nodes = d3.range(n).map(function() {
var c = Math.floor(Math.random() * m),
rx = Math.sqrt((c + 1) / m * -Math.log(Math.random())) * (maxSize - minSize) + minSize,
ry = Math.sqrt((c + 1) / m * -Math.log(Math.random())) * (maxSize - minSize) + minSize,
d = {color: c, rx: rx, ry: ry};
return d;
});
var collide = function(alpha) {
var quadtree = d3.quadtree()
.x((d) => d.x)
.y((d) => d.y)
.addAll(nodes);
nodes.forEach((d) => {
quadtree.visit((quad, x0, y0, x1, y1) => {
let updated = false;
if (quad.data && (quad.data !== d)) {
let x = d.x - quad.data.x,
y = d.y - quad.data.y,
xSpacing = (quad.data.rx + d.rx),
ySpacing = (quad.data.ry + d.ry),
absX = Math.abs(x),
absY = Math.abs(y),
l, lx, ly;
if (absX < xSpacing && absY < ySpacing) {
l = Math.sqrt(x * x + y * y);
lx = (absX - xSpacing) / l * alpha;
ly = (absY - ySpacing) / l * alpha;
if (Math.abs(lx) > Math.abs(ly)) {
lx = 0;
} else {
ly = 0;
}
d.x -= x *= lx;
d.y -= y *= ly;
quad.data.x += x;
quad.data.y += y;
updated = true;
}
}
return updated;
});
});
};
var force = d3.forceSimulation()
.nodes(nodes)
.force("center", d3.forceCenter())
.force("collide", (alpha) => collide(alpha))
.force("x", d3.forceX().strength(.01))
.force("y", d3.forceY().strength(.01))
.on("tick", tick);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append('g')
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
var ellipse = svg.selectAll("ellipse")
.data(nodes)
.enter().append("ellipse")
.attr("rx", function(d) { return d.rx; })
.attr("ry", function(d) { return d.ry; })
.style("fill", function(d) { return color(d.color); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
function tick() {
ellipse
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function dragstarted(d) {
if (!d3.event.active) force.alphaTarget(0.3).restart();
d.x = d.x;
d.y = d.y;
}
function dragged(d) {
d.x = d3.event.x;
d.y = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) force.alphaTarget(0);
d.x = d3.event.x;
d.y = d3.event.y;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
There are too many gaps between nodes, and I know it's because ellipses are treated as rectangles in the collision detection.
Anybody who's got a good solution for this?
Thanks, in advance.
I've figured this out by myself.
Here's the collision detection library for d3.
ellipse-collision-detection
I've attached working example in the above repository.
Thanks!

How to change the size of dots in beeswarm plots in D3.js

I've been looking at this example of a beeswarm plot in d3.js and I'm trying to figure out how to change the size of the dots and without getting the circles to overlap. It seems if the radius of the dots change, it doesn't take this into account when running the calculations of where to place the dots.
This is a cool visualization.
I've made a plunk of it here: https://plnkr.co/edit/VwyXfbc94oXp6kXQ7JFx?p=preview and modified it to work a bit more like you're looking for (I think). The real key is changing the call to handle collision to vary based on the radius of the circles (in the original post it's hard coded to 4, which works well when r === 3 but fails as r grows). The changes:
Make the circle radius into a variable (line 7 of script.js, var r = 3;)
Change the d3.forceCollide call to use that radius and a multiplier - line 110 (.force("collide", d3.forceCollide(r * 1.333)))
Change the .enter() call to use that radius as well (line 130: .attr("r", r))
This works reasonably well for reasonable values of r - but you'll need to adjust the height, and it might even be nice to just change the whole thing so that r is based on height (e.g. var r = height * .01). You'll notice that as is now, the circles go off the bottom and top of the graph area.
This post might be of interest as well: Conflict between d3.forceCollide() and d3.forceX/Y() with high strength() value
Here's the whole of script.js for posterity:
var w = 1000, h = 280;
var padding = [0, 40, 34, 40];
var r = 5;
var xScale = d3.scaleLinear()
.range([ padding[3], w - padding[1] ]);
var xAxis = d3.axisBottom(xScale)
.ticks(10, ".0s")
.tickSizeOuter(0);
var colors = d3.scaleOrdinal()
.domain(["asia", "africa", "northAmerica", "europe", "southAmerica", "oceania"])
.range(['#e41a1c','#377eb8','#4daf4a','#984ea3','#ff7f00','#ffff33']);
d3.select("#africaColor").style("color", colors("africa"));
d3.select("#namericaColor").style("color", colors("northAmerica"));
d3.select("#samericaColor").style("color", colors("southAmerica"));
d3.select("#asiaColor").style("color", colors("asia"));
d3.select("#europeColor").style("color", colors("europe"));
d3.select("#oceaniaColor").style("color", colors("oceania"));
var formatNumber = d3.format(",");
var tt = d3.select("#svganchor").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var svg = d3.select("#svganchor")
.append("svg")
.attr("width", w)
.attr("height", h);
var xline = svg.append("line")
.attr("stroke", "gray")
.attr("stroke-dasharray", "1,2");
var chartState = {};
chartState.variable = "totalEmission";
chartState.scale = "scaleLinear";
chartState.legend = "Total emissions, in kilotonnes";
d3.csv("co2bee.csv", function(error, data) {
if (error) throw error;
var dataSet = data;
xScale.domain(d3.extent(data, function(d) { return +d.totalEmission; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (h - padding[2]) + ")")
.call(xAxis);
var legend = svg.append("text")
.attr("text-anchor", "middle")
.attr("x", w / 2)
.attr("y", h - 4)
.attr("font-family", "PT Sans")
.attr("font-size", 12)
.attr("fill", "darkslategray")
.attr("fill-opacity", 1)
.attr("class", "legend");
redraw(chartState.variable);
d3.selectAll(".button1").on("click", function(){
var thisClicked = this.value;
chartState.variable = thisClicked;
if (thisClicked == "totalEmission"){
chartState.legend = "Total emissions, in kilotonnes";
}
if (thisClicked == "emissionPerCap"){
chartState.legend = "Per Capita emissions, in metric tons";
}
redraw(chartState.variable);
});
d3.selectAll(".button2").on("click", function(){
var thisClicked = this.value;
chartState.scale = thisClicked;
redraw(chartState.variable);
});
d3.selectAll("input").on("change", filter);
function redraw(variable){
if (chartState.scale == "scaleLinear"){ xScale = d3.scaleLinear().range([ padding[3], w - padding[1] ]);}
if (chartState.scale == "scaleLog"){ xScale = d3.scaleLog().range([ padding[3], w - padding[1] ]);}
xScale.domain(d3.extent(dataSet, function(d) { return +d[variable]; }));
var xAxis = d3.axisBottom(xScale)
.ticks(10, ".0s")
.tickSizeOuter(0);
d3.transition(svg).select(".x.axis").transition().duration(1000)
.call(xAxis);
var simulation = d3.forceSimulation(dataSet)
.force("x", d3.forceX(function(d) { return xScale(+d[variable]); }).strength(2))
.force("y", d3.forceY((h / 2)-padding[2]/2))
.force("collide", d3.forceCollide(r * 1.333))
.stop();
for (var i = 0; i < dataSet.length; ++i) simulation.tick();
var countriesCircles = svg.selectAll(".countries")
.data(dataSet, function(d) { return d.countryCode});
countriesCircles.exit()
.transition()
.duration(1000)
.attr("cx", 0)
.attr("cy", (h / 2)-padding[2]/2)
.remove();
countriesCircles.enter()
.append("circle")
.attr("class", "countries")
.attr("cx", 0)
.attr("cy", (h / 2)-padding[2]/2)
.attr("r", r)
.attr("fill", function(d){ return colors(d.continent)})
.merge(countriesCircles)
.transition()
.duration(2000)
.attr("cx", function(d) { console.log(d); return d.x; })
.attr("cy", function(d) { return d.y; });
legend.text(chartState.legend);
d3.selectAll(".countries").on("mousemove", function(d) {
tt.html("Country: <strong>" + d.countryName + "</strong><br>"
+ chartState.legend.slice(0, chartState.legend.indexOf(",")) + ": <strong>" + formatNumber(d[variable]) + "</strong>" + chartState.legend.slice(chartState.legend.lastIndexOf(" ")))
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.9);
xline.attr("x1", d3.select(this).attr("cx"))
.attr("y1", d3.select(this).attr("cy"))
.attr("y2", (h - padding[2]))
.attr("x2", d3.select(this).attr("cx"))
.attr("opacity", 1);
}).on("mouseout", function(d) {
tt.style("opacity", 0);
xline.attr("opacity", 0);
});
d3.selectAll(".x.axis, .legend").on("mousemove", function(){
tt.html("This axis uses SI prefixes:<br>m: 10<sup>-3</sup><br>k: 10<sup>3</sup><br>M: 10<sup>6</sup>")
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 0.9);
}).on("mouseout", function(d) {
tt.style("opacity", 0);
});
//end of redraw
}
function filter(){
function getCheckedBoxes(chkboxName) {
var checkboxes = document.getElementsByName(chkboxName);
var checkboxesChecked = [];
for (var i=0; i<checkboxes.length; i++) {
if (checkboxes[i].checked) {
checkboxesChecked.push(checkboxes[i].defaultValue);
}
}
return checkboxesChecked.length > 0 ? checkboxesChecked : null;
}
var checkedBoxes = getCheckedBoxes("continent");
var newData = [];
if (checkedBoxes == null){
dataSet = newData;
redraw();
return;
};
for (var i = 0; i < checkedBoxes.length; i++){
var newArray = data.filter(function(d){
return d.continent == checkedBoxes[i];
});
Array.prototype.push.apply(newData, newArray);
}
dataSet = newData;
redraw(chartState.variable);
//end of filter
}
//end of d3.csv
});

How to separate different "gravitational fields" in D3?

I have a force enabled SVG visualisation where I want smaller circles to be attracted to bigger circles. This attraction works by calculating the elements' centre point and change it in iterations for every "tick" in the visualisation, to keep the items from going over the centre of the nodes I use a function to change the charge of the items depending on their size.
I used Mike's code here as a basis: http://mbostock.github.io/d3/talk/20110921/#14
My problem comes here - it seems like the bigger circles are affecting each others "gravitational fields" - is there a way I can separate them from eachother?
Force layout setup:
var w = 1280,
h = 800,
color = d3.scale.category10();
var force = d3.layout.force()
.gravity(0.0)
.charge(function(d){
return -10 * d.r;
})
.size([w, h]);
Element drawing:
var g = svg.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
;
g.append("svg:circle")
.attr("r", 40)
.attr("transform", function(d) { return "translate(" + 0 + ","+ 0 + ")"; })
.style("fill", fill)
.call(force.drag);
g.append("svg:text")
.attr("x", 0)
.attr("dy", ".31em")
.attr("text-anchor", "middle")
.text(function(d) {
return d.label;
});
Animation loop:
force.on("tick", function(e) {
var k = e.alpha * 0.5;
nodes.forEach(function(node) {
var center = nodes[node.type];
dx = center.x - node.x;
dy = center.y - node.y;
node.x += dx * k;
node.y += dy * k;
});
svg.selectAll(".circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
Adding smaller circles:
svg.on("mousemove", function() {
var p1 = d3.svg.mouse(this),
node = {
type: Math.random() * 3 | 0,
x: p1[0],
y: p1[1],
r: 1.5,
px: (p0 || (p0 = p1))[0],
py: p0[1]
};
p0 = p1;
svg.append("svg:circle")
.attr("class", "circle")
.data([node])
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 4.5)
.style("fill", fill);
nodes.push(node);
force.start();
});

D3 polyliner scale in partition tree

I am using a Partition tree as in this example to try and make a visualization for taxonomy trees. I tried to follow the code in the d3.js and the scripting in the html. It looks like the Layout and onclick event just increases the ’rect’ sizes and then clips it to fit in the same size SVG element. Would it be possible to use a polylinear scale on the x axis so I can keep each parent nodes on screen as I go deeper into the tree?
For example if the layout was 160px wide and four columns across each column is 40px wide. I’d like the tree to start with the first column 10px wide which makes the others fill up the 150px at 50px each. If the next column was clicked then the domain and range would change so that the 2 left columns would be 10px each and the others would be 70px etc.
I tried changing the range from ([0, 160]) to ([0, 10, 160]) and the domain to ([0, .25 ,1]). Is this the right approach? The positions changed but not the widths. I would probably want to change the range and domain depending on the depth.
Edit
Here is the code from the html script. I was thinking of changing the code below to something like
var parentcolumns = 30;
var numberleftcolumns = 0.25;
x = d3.scale.linear().domain([0, numberleftcolumns, 1]).range([0, parentcolumns, h]),
The numbers I used in the example were just to help discribe the problem. Here the width is 1120px.
<script type="text/javascript">
var w = 1120,
h = 600,
x = d3.scale.linear().range([0, w]),
y = d3.scale.linear().range([0, h]);
var vis = d3.select("#body").append("div")
.attr("class", "chart")
.style("width", w + "px")
.style("height", h + "px")
.append("svg:svg")
.attr("width", w)
.attr("height", h);
var partition = d3.layout.partition()
.value(function(d) { return d.size; });
d3.json("http://localhost:8080/flare.json", function(root) {
console.log("loadedJson:",root);
var g = vis.selectAll("g")
.data(partition.nodes(root))
.enter().append("svg:g")
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; })
.on("click", click);
var kx = w / root.dx,
ky = h / 1;
g.append("svg:rect")
.attr("width", root.dy * kx)
.attr("height", function(d) { return d.dx * ky; })
.attr("class", function(d) { return d.children ? "parent" : "child"; });
g.append("svg:text")
.attr("transform", transform)
.attr("dy", ".35em")
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0.15; })
.text(function(d) { return d.name; })
d3.select(window)
.on("click", function() { click(root); })
function click(d) {
if (!d.children) return;
kx = (d.y ? w - 40 : w) / (1 - d.y);
ky = h / d.dx;
x.domain([d.y, 1]).range([d.y ? 40 : 0, w]);
y.domain([d.x, d.x + d.dx]);
var t = g.transition()
.duration(d3.event.altKey ? 7500 : 750)
.attr("transform", function(d) { return "translate(" + x(d.y) + "," + y(d.x) + ")"; });
t.select("rect")
.attr("width", d.dy * kx)
.attr("height", function(d) { return d.dx * ky; });
t.select("text")
.attr("transform", transform)
.style("opacity", function(d) { return d.dx * ky > 12 ? 1 : 0; });
d3.event.stopPropagation();
}
function transform(d) {
return "translate(8," + d.dx * ky / 2 + ")";
}
});
</script>
I would have to rewrite the onclick function to deal with the domain and range changes and alter my parentcolumns each time.

Resources