Circle pack layout using nest() and rollup - d3.js

I'm trying to create a circle pack graph using nest() and .rollup. I'm getting the following errors:
Error: Invalid value for <g> attribute transform="translate(undefined,undefined)"
Error: Invalid value for <circle> attribute r="NaN"
I want the circles to be sized according to the number of companies in each country. I'm attempting to adapt Mike Bostock's Flare circle-pack example.
If anyone could point me in the direction of any information, I'd be very grateful.
JS code:
var diameter = 960,
format = d3.format(",d");
var pack = d3.layout.pack()
.size([diameter - 4, diameter - 4])
.value(function(d) { return d.size; });
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(2,2)");
//Get data
d3.json("data/countriesNested.php", function(error, data){
var submissionsByCountry = d3.nest()
.key(function(d) { return d.Country; })
.key(function(d) { return d.Organisation; })
.rollup(function(leaves) { return leaves.length; })
.entries(data);
var node = svg.datum(data).selectAll(".node")
.data(pack.nodes)
.enter().append("g")
.attr("class", function(d) { return d.children ? "node" : "leaf node"; })
.attr("transform", function(d) { return "translate(" + d.cx + "," + d.cy + ")"; });
node.append("title")
.text(function(d) { return d.name + (d.children ? "" : ": " + format(d.size)); });
node.append("circle")
.attr("r", function(d) { return d.r; });
});
d3.select(self.frameElement).style("height", diameter + "px");
</script>
Data file (from MySQL using PHP script):
[
{
"Country":"USA",
"ID":4,
"Organisation":"Company 1"
},
{
"Country":"USA",
"ID":5,
"Organisation":"Company 2"
},
{
"Country":"USA",
"ID":6,
"Organisation":"Company 3"
},
{
"Country":"FRANCE",
"ID":19,
"Organisation":"Company 4"
},
{
"Country":"FRANCE",
"ID":24,
"Organisation":"Company 5"
},
{
"Country":"GERMANY",
"ID":10,
"Organisation":"Company 6"
},
{
"Country":"ITALY",
"ID":7,
"Organisation":"Company 7"
},
.....
Thanks for reading.

There are a few errors in your code that need to be fixed:
You need to set the accessor functions for children and values on your pack layout:
var pack = d3.layout.pack()
.size([diameter - 4, diameter - 4])
.children(function(d) {
return d.values; // accessor for children
})
.value(function(d) {
return d.values; // accessor for values
});
Your d3.nest() returns an array but d3.pack() requires you to supply a root object containing the hierarchy. You have to create a root object and put your nested array inside:
var countryRoot = {
key: "root",
values: submissionsByCountry
};
In your code you nest your data into submissionsByCountry but you are not using this variable anywhere else. So you obviously have to refer to it when binding data to your svg. This is accomplished by the above mentioned root object which is later on bound to the svg.
var node = svg.datum(countryRoot).selectAll(".node")
The attributes the pack layout is adding to your data nodes include values x and y, whereas you refered to them as cx and cy which are attributes to <svg:circle> but are not present in your data. Hence, you got your transform="translate(undefined,undefined)" error messages. You should use these attributes as such:
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
I put together a working plunk.

So, you want a parent circle (let's call it the world), with child circles representing each country sized with a count of entries in your JSON array?
d3.json("data/countriesNested.php", function(error, data) {
var submissionsByCountry = d3.nest()
.key(function(d) {
return d.Country;
})
.rollup(function(leaves) {
return leaves.length;
})
.entries(data);
var root = {
"key": "world",
"children": submissionsByCountry
};
...
This will give you something closely resembling the flare.json.
Next, you need to give d3 the right accessor for your circle size.
var pack = d3.layout.pack()
.size([diameter - 4, diameter - 4])
.value(function(d) {
return d.values; //<-- this comes from your roll-up and is the count.
});
Finally it looks like you changed the example code to access non-exist cx and cy attributes in the resulting nodes data:
var node = svg.datum(root).selectAll(".node")
.data(pack.nodes)
.enter().append("g")
.attr("class", function(d) {
return d.children ? "node" : "leaf node";
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; //<-- this is .x, .y
});
Here's an example.

Related

Updating each iteration using d3.js

I am working on a line graph in d3.js and am unsure how to to iterate through each country and update my graph's points. I want to draw each country on my map. In my code I have only hard coded the first country and the output shown in the following images. Have attached my csv file to show the column names. I am unsure whether I need to alter my csv file to do so.
any help is appreciated
function init(){
var w = 600;
var h = 600;
var barPadding = 20;
var dataset;
var rowConverter = function(d){
return {
year: parseFloat(d.year),
Afghanistan: (d.Afghanistan),
Albania: (d.Albania),
Algeria: (d.Algeria),
Andorra: (d.Andorra),
Angola: (d.Angola)
};
}
d3.csv("hello.csv", rowConverter, function(data){
dataset = data;
if (data==null){
alert("Error, data has not been loaded!");
}
else{
draw(dataset);
console.log(dataset);
}
});
function draw(){
var xScale = d3.scaleLinear()
.domain([d3.min(dataset,function(d){
return d.year;
}),
d3.max(dataset,function(d){
return d.year;
})])
.range([barPadding,w-barPadding]);
var yScale = d3.scaleLinear()
.domain([0,100])
.range([h-barPadding,barPadding*2]);
var xAxis = d3.axisBottom()
.scale(xScale)
.ticks(5);
var yAxis = d3.axisLeft()
.scale(yScale)
.ticks(5);
var valueline = d3.line()
.x(function(d) { return xScale(d.year); })
.y(function(d) { return yScale(d.Afghanistan); });
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return xScale(d.year);
})
.attr("cy", function(d) {
return yScale(d.Afghanistan);
})
.attr("r", 5)
.attr("fill","slategrey")
svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d.year + "," + d.Afghanistan;
})
.attr("x", function(d) {
return xScale(d.year);
})
.attr("y", function(d) {
return yScale(d.Afghanistan);
})
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "blue");
svg.append("path")
.data([dataset])
.attr("class", "line")
.attr("d", valueline);
svg.append("g")
.attr("class","axis")
.attr("transform", "translate(0," + (h - barPadding) + ")")
.call(xAxis);
svg.append("g")
.attr("class","axis")
.attr("transform", "translate(" + barPadding + ",0)")
.call(yAxis);
}
}
window.onload=init;
As a selectAll(null).data(dataArray).enter() uses a data array to enter an element for each item in the data array, we need to create an array for each line we wish to enter. Currently you have an array for each year, but we want to enter a path for each data series/country. So we need to create an array where each item in that array represents a path.
This requires altering the structure of our data from:
[
{year: 2000, series1: number, series2: number... },
{year: 2001, series1: number, series2: number... },
....
]
To an array with an item for each line:
[
{ year: 2000, series1: number },
{ year: 2001, series1: number },
...
{ year: 2000, series2: number },
{ year: 2001, series2: number },
...
]
I'm using this approach because it is commonly seen in d3 cannonical examples such as this.
This is relatively easy to do. After we parse in our csv/tsv/dsv with d3, we can access the columns of the dataset with dataset.columns. The first column isn't a series we want to plot, it represents the x axis, so we can slice it off with dataset.columns.slice(1). Ok, with the remaining columns we can iterate through each series and create the data array above:
I'm using csvParse in the snippet, which replicates d3.csv except that it doesn't use a callback function for the returned data, letting me define the dataset with var dataset = d3.csvParse(... rather than d3.csv("file.csv", function(error, dataset) {...})
var csv = "year,series1,series2,series3\n"+
"2000,5,2,8\n"+
"2001,6,4,7\n"+
"2002,9,3,5\n"+
"2003,10,6,7\n"+
"2004,9,7,8"
var data = d3.csvParse(csv);
var series = data.columns // get the columns
.slice(1) // drop the first column(years)
.map(function(series) { // for each series:
return { // return a new object:
series: series, // name it
values: data.map(function(d) { // get the data:
return { year: d.year, value: d[series] };
})
}
});
console.log(series);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>
Now we have an item in the data array for each series we want to draw a line for. Now we're cooking with gas. So we can now use selectAll().data(series) to enter a line for each item in the data array, creating a line for each series.
In keeping with Mike Bostock's example I linked to above, I've created an property which identifies which series each item represents, as well as a property which holds the arrays of year/value pairings.
Here's a quick demo:
var csv = "year,series1,series2,series3\n"+
"2000,5,2,8\n"+
"2001,6,4,7\n"+
"2002,9,3,5\n"+
"2003,10,6,7\n"+
"2004,9,7,8"
var data = d3.csvParse(csv);
var series = data.columns
.slice(1)
.map(function(series) {
return {
series: series,
values: data.map(function(d) {
return { year: d.year, value: d[series] };
})
}
});
var x = d3.scaleLinear().domain([2000,2004]).range([0,500]);
var y = d3.scaleLinear().domain([0,10]).range([300,0]);
var color = d3.scaleOrdinal().range(d3.schemeCategory20);
var line = d3.line()
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.value); });
d3.select("svg")
.selectAll("path")
.data(series)
.enter()
.append("path")
.attr("fill","none")
.attr("stroke", function(d,i) { return color(i) })
.attr("d",function(d) { return line(d.values) });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
<svg width="500" height="300"></svg>

Pie is not a function in Ember D3 Pie Chart

I am currently struggling to create a pie chart in Ember using D3 as installed by the add-on ember-d3. To create the chart I am working backwords from a great example posted on git here.
With my current code I receiving an error in the console that 'pie' is not a function. The code is as follows:
import Ember from 'ember';
import Component from 'ember-component';
import {arc, pie} from 'd3-shape';
import { select } from 'd3-selection';
import { scaleOrdinal } from 'd3-scale';
export default Component.extend({
radius() {
let width = this.get('width'),
height = this.get('height');
return Math.min(width, height) / 2;
},
color() {
return scaleOrdinal().range(['#A60F2B', '#648C85', '#B3F2C9', '#528C18', '#C3F25C'])
},
arc() {
let radius = this.radius();
return arc().outerRadius(radius - 10).innerRadius(0);
},
labelArc() {
let radius = this.radius();
return arc().outerRadius(radius - 40).innerRadius(radius - 40);
},
didInsertElement() {
let data = this.get('data');
this.pieChart(data);
},
pieChart(dataIn, dataIndexIn) {
let width = this.get('width'),
height = this.get('height'),
arc = this.arc(),
labelArc = this.labelArc(),
color = this.color(),
that = this;
let data = dataIn;
let pie = pie()
.value(function(d) { return d.count; })(data[dataIndexIn]);
let svg = select("#pie-chart")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + ", " + height/2 + ")");
let g = svg.selectAll("arc")
.data(pie)
.enter().append("g")
.attr("class", "arc");
g.append("path")
.attr("d", arc)
.style("fill", function(d, i) { return color(i); })
.style("stroke", "#222")
.each(function(d) { this._current = d; that.set('chartContext', this._current); });
//Labels
g.append("text")
.attr("transform", function(d) { return "translate(" + labelArc.centroid(d) + ")"; })
.text(function(d) { return d.data.color; })
.attr("text-anchor", "middle")
.style("fill", "#FFF")
.each(function(d) { this._current = d; that.set('chartContextLable', this._current); });
},
});
As far as I can tell d3-shape is being imported correctly as I do not receive an error about 'arc'. If I do remove 'arc' from the import statement I receive an error to say that 'arc' is not defined. This suggests the module is importing correctly.
The shape module also imports correctly on other component charts that do not use the pie function.
I think this suggests a syntax error but I can't see one.
Mock data is passed to the component via the controller and template helper:
data: [
{ label: 'Abulia', count: 10 },
{ label: 'Betelgeuse', count: 20 },
{ label: 'Cantaloupe', count: 30 },
{ label: 'Dijkstra', count: 40 }
],
{{pie-chart-example data=data dataIndex=dataIndex}}
You can't declare variables with the same name as the imports
working version: https://ember-twiddle.com/a658207aa8800816329f95b4e1b4860f?openFiles=components.pie-chart.js%2Ctemplates.components.pie-chart.hbs
creating functions that don't do much also makes the code harder to understand which in turn causes more problems.

"TypeError: root.children is undefined" on Collapsible Tree d3 v3

I am trying one of Mike Bostock's collapsible tree examples with d3 v3. However, I always receive the following error:
TypeError: root_03.children is undefined
I have referred to Stack Overflow for troubleshooting but the solutions do not work.
Does anyone have the answer to this? Or could it be because this code does not work at v3 anymore?
Here is my JS code:
var w_canvas_03 = 960;
var h_canvas_03 = 800;
var margin_03 = {
top: 50,
right: 20,
bottom: 30,
left: 20
};
var w_chart_03 = w_canvas_03 - margin_03.left - margin_03.right;
var h_chart_03 = h_canvas_03 - margin_03.top - margin_03.bottom;
var i_03 = 0;
var duration_03 = 750;
var root_03;
var tree_03 = d3.layout.tree()
.size([h_chart_03, w_chart_03]);
var diagonal_03 = d3.svg.diagonal()
.projection(function(data_){
return [data_.y, data_.x];
});
var svg_03 = d3.select("body")
.append("svg")
.attr("width", w_canvas_03)
.attr("height", h_canvas_03)
.append("g")
.attr("transform", "translate(" + margin_03.left + "," + margin_03.top + ")");
d3.json("resource/json_remark.json", function(error_, source_){
if (error_) throw error_;
root_03 = source_;
root_03.x0 = h_chart_03 / 2;
root_03.y0 = 0;
console.log(root_03)
function Collapse_03(data_){
if (data_.children){
data_._children = data_.children;
data_._children.forEach(Collapse_03);
data_.children = null;
}
}
root_03.children.forEach(Collapse_03);
Update_03(root_03);
})
d3.select(self.frameElement).style("height", h_canvas_03);
function Update_03(source_){
// Computing the flattened node list
var nodeAll_03 = tree_03.nodes(root_03).reverse()
var linkAll_03 = tree_03.links(nodeAll_03);
// Normalizing for fixed-depth
nodeAll_03.forEach(function(data_){
data_.y = 180 * data_.depth;
});
// Updating the node
var node_03 = svg_03.selectAll("g.node")
.data(nodeAll_03, function(data_){
return data_.id || (data_.id = ++i_03);
});
var nodeEnter_03 = node_03.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(data_){
return "translate(" + source_.y0 + "," + source_.x0 + ")";
})
.on("click", Click_03);
// Entering any new nodes at the parent's previous position
nodeEnter_03.append("circle")
.attr("r", 1e-6)
.style("fill", function(data_){
data_._children ? "lightsteelblue" : "#fff"
; });
nodeEnter_03.append("text")
.attr("x", function(data_){
data_.children || data_.children ? -10:10;
})
.attr("dy", "0.35em")
.attr("text-anchor", function(data_){
return data_.children || data_.children ? "end" : "start";
})
.text(function(data_){
return data_.name;
})
.style("fill-opacity", 1e-6);
// Transitioning nodes to their new position
var nodeUpdate_03 = node_03.transition()
.duration(duration_03)
.attr("transform", function(data_){
return "translate(" + data_.y + "," + data_.x + ")";
});
nodeUpdate_03.select("circle")
.attr("r", 4.5)
.style("fill", function(data_){
return data_._children ? "lightsteelblue" : "#fff;"
});
nodeUpdate_03.select("text")
.style("fill-opacity", 1);
// Transitioning exiting nodes to the parent's new position
var nodeExit_03 = node_03.exit()
.transition()
.duration(duration_03)
.attr("transform", function(data_){
return "translate(" + source_.y + "," + source_.x + ")";
})
.remove();
nodeExit_03.select("circle")
.attr("r", 1e-6);
nodeExit_03.select("text")
.style("fill-opacity", 1e-6);
// Updating the links
var link_03 = svg_03.selectAll("path.link")
.data(linkAll_03, function(data_){
return data_.target.id;
});
// Entering any new links at the parent's previous position
link_03.enter()
.insert("path", "g")
.attr("class", "link")
.attr("d", function(data_){
var o_03 = {x: source_.x0, y: source_.y0};
return diagonal_03({source: o_03, target: o_03});
});
// Transitioning links to their new position
link_03.transition()
.duration(duration_03)
.attr("d", diagonal_03);
// Transitioning exiting nodes to the parent's new position
link_03.exit()
.transition()
.duration(duration_03)
.attr("d", function(data_){
var o_03 = {x: source_.x, y: source_.y};
return diagonal_03({source: o_03, target: o_03});
})
.remove();
// Stashing the old positions for transition
nodeAll_03.forEach(function(data_){
data_.x0 = data_.x;
data_.y0 = data_.y;
});
}
// Toggling children on click
function Click_03(data_){
if (data_.children){
data_._children = data_.children;
data_.children = null;
} else{
data_.children = data_._children;
data_._children = null;
}
Update_03(data_)
}
And here is the simplified version of my JSON file:
[
{
"name": "Neutral",
"children": [
{
"name": "Fruit",
"children": [
{
"name": "Apple"
},
{
"name": "Orange"
}
]
}
]
}
]
If you look at the original Bostock's bl.ocks you'll see that its JSON ("flare.json") file has an object, not an array. However, your JSON file has an array (with a single object inside it).
Therefore, the solution is simple:
root_03 = source_[0];
//1st element-----^
This get's the first element of that array, which is the root object with the children property.
Here is a bl.ocks with your code: https://bl.ocks.org/anonymous/ae2bcac26194865317d0ec6b55361221/070902f5daaf9d90f061455d11d04769439a8b4c

grouped category bar chart with different groups in d3?

My requirement is to draw a category-grouped bar chart in which each category has a different number of groups, using pure d3. I have no idea how to take domain and range to meet my requirement.
I tried in the way given in the answer to d3 nested grouped bar chart, but it did not work in my case.
Here my graph structure is like:
The issue with the plunker of the answer that you mention is that it will just work for values that have the same children. In order to handle the dynamic children values I took this approach
Lets create the color mapping for our groups:
var color = {
Mechanical: '#4A7B9D',
Electrical: '#54577C',
Hydraulic: '#ED6A5A'
};
We also need a structure with nested values that will be the inner groups:
// Simulated data structure
var data = [{
key: 'Mechanical',
values: [{
key: 'Gear',
value: 11
}, {
key: 'Bearing',
value: 8
}, {
key: 'Motor',
value: 3
}]
}];
I created a barPadding value which will dictate the separation between bars:
var barPadding = 120;
We are going to need a dummy scale to get the rangeBand of the bars, lets do that:
// dummy array
var rangeBands = [];
// cummulative value to position our bars
var cummulative = 0;
data.forEach(function(val, i) {
val.cummulative = cummulative;
cummulative += val.values.length;
val.values.forEach(function(values) {
rangeBands.push(i);
})
});
// set scale to cover whole svg
var x_category = d3.scale.linear()
.range([0, width]);
// create dummy scale to get rangeBands (width/childrenValues)
var x_defect = d3.scale.ordinal().domain(rangeBands)
.rangeRoundBands([0, width], .1);
var x_category_domain = x_defect.rangeBand() * rangeBands.length;
x_category.domain([0, x_category_domain]);
Then lets add all our category groups g elements:
var category_g = svg.selectAll(".category")
.data(data)
.enter().append("g")
.attr("class", function(d) {
return 'category category-' + d.key;
})
.attr("transform", function(d) { // offset by inner group size
var x_group = x_category((d.cummulative * x_defect.rangeBand()));
return "translate(" + x_group + ",0)";
})
.attr("fill", function(d) { // make child elements of group "inherit" this fill
return color[d.key];
});
Adding our inner groups g elements:
var defect_g = category_g.selectAll(".defect")
.data(function(d) {
return d.values;
})
.enter().append("g")
.attr("class", function(d) {
return 'defect defect-' + d.key;
})
.attr("transform", function(d, i) { // offset by index
return "translate(" + x_category((i * x_defect.rangeBand())) + ",0)";
});
Having our g elements lets add the labels:
var category_label = category_g.selectAll(".category-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
console.log(d)
return 'category-label category-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((d.values.length * x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 30;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
var defect_label = defect_g.selectAll(".defect-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
console.log(d)
return 'defect-label defect-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 10;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
And finally our rects:
var rects = defect_g.selectAll('.rect')
.data(function(d) {
return [d];
})
.enter().append("rect")
.attr("class", "rect")
.attr("width", x_category(x_defect.rangeBand() - barPadding))
.attr("x", function(d) {
return x_category(barPadding);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
Here's the above code in plnkr: https://plnkr.co/edit/L0eQwtEMQ413CpoS5nvo?p=preview

Dynamically Updating A D3 Treemap Datasource

I think I'm missing something very obvious here. Basically what I am trying to do is create a treemap that on button click will go to the server and retrieve the next level into the treemap...This is necessary because the treemap structure is too large and takes too long to calculate so jumping one level at a time is the only option we have.
[Note to IE users, in this example the treemap node names don't appear to be working. Try using Chrome]
http://plnkr.co/edit/simVGU
This code is taken almost exactly from
http://bost.ocks.org/mike/treemap/
I'm using vizData1.json for the "first" level and on mouse click I'm using vizData2.json as the "second" level. You can see that the two end up overlapping. I've tried to do svg.exit() as well as svg.clear() without any luck.
I should also note that I have already tried the sticky(false) suggestion from this post
Does the d3 treemap layout get cached when a root node is passed to it?
UPDATE:
To continue my hunt I have found an example that successfully adds new nodes to an existing treemap. However I am having trouble adapting this logic as the treemap I am attempting to fit this logic into has been heavily customized by Michael Bostock - #mbostock to allow for the nice breadcrumb trail bar at the top.
Code snippet that proves appending to existing treemap nodes is possible:
http://jsfiddle.net/WB5jh/3/
Also, Stackoverflow is forcing me to post code because I'm linking to plnkr so I have dumped my script.js here for those who would rather not interact with plunker
$(function() {
var margin = { top: 20, right: 0, bottom: 0, left: 0 },
width = 960,
height = 500,
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)
.sticky(false);
var svg = d3.select("#treemap")
.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("vizData1.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) {
console.log(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("foreignObject")
.call(rect)
.attr("class", "foreignobj")
.append("xhtml:div")
.attr("dy", ".75em")
.html(function (d) { return d.name; })
.attr("class", "textdiv");
function transition(d) {
if (transitioning || !d) return;
transitioning = true;
d3.json("vizData2.json", function (root) {
initialize(root);
accumulate(root);
layout(root);
display(root);
});
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);
g2.selectAll("foreignObject div").style("display", "none"); /*added*/
// 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);
t1.selectAll(".textdiv").style("display", "none"); /* added */
t1.selectAll(".foreignobj").call(foreign);
t2.selectAll(".textdiv").style("display", "block"); /* added */
t2.selectAll(".foreignobj").call(foreign); /* added */
// 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 foreign(foreign) { /* added */
foreign.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;
}
});

Resources