Related
so Im really new to d3 and Im just getting started with data visualizations. So im trying to use mike bostok treemap to visualize with my data. I have some understanding of parent nodes and children nodes but im at a lost when it comes to understanding or writing a code related to nodes.
bostoks JSON data has multiple levels or depths while mine has one root node and an array of objects all at the same depth.
JSON file
Javascript code
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var fader = function(color) { return d3.interpolateRgb(color, "#fff")(0.2); },
color = d3.scaleOrdinal(d3.schemeCategory20.map(fader)),
format = d3.format(",d");
var treemap = d3.treemap()
.tile(d3.treemapResquarify)
.size([width, height])
.round(true)
.paddingInner(1);
d3.json("data.json", function(data) {
var root = d3.hierarchy(data)
.eachBefore(function(d) {
d.data.id = (d.parent ? d.parent.data.id + "." : "") + d.data.name; })
.sum(sumBySize)
.sort(function(a, b) {
return b.height - a.height || b.value - a.value; });
treemap(root);
console.log(d.data.id);
var cell = svg.selectAll("g")
.data(root.leaves())
.enter().append("g")
.attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; });
cell.append("rect")
.attr("id", function(d) { return d.data.id; })
.attr("width", function(d) { return d.x1 - d.x0; })
.attr("height", function(d) { return d.y1 - d.y0; })
//.attr("fill", function(d) { return color(d.parent.data.id); });
cell.append("clipPath")
.attr("id", function(d) { return "clip-" + d.data.id; })
.append("use")
.attr("xlink:href", function(d) { return "#" + d.data.id; });
cell.append("text")
.attr("clip-path", function(d) { return "url(#clip-" + d.data.id + ")"; })
.selectAll("tspan")
.data(function(d) { return d.data.name.split(/(?=[A-Z][^A-Z])/g); })
.enter().append("tspan")
.attr("x", 4)
.attr("y", function(d, i) { return 13 + i * 10; })
.text(function(d) { return d; });
cell.append("title")
.text(function(d) { return d.data.id + "\n" + format(d.value); });
d3.selectAll("input")
.data([sumBySize, sumByCount], function(d) { return d ? d.name : this.value; })
.on("change", changed);
var timeout = d3.timeout(function() {
d3.select("input[value=\"sumByCount\"]")
.property("checked", true)
.dispatch("change");
}, 2000);
function changed(sum) {
timeout.stop();
treemap(root.sum(sum));
cell.transition()
.duration(750)
.attr("transform", function(d) { return "translate(" + d.x0 + "," + d.y0 + ")"; })
.select("rect")
.attr("width", function(d) { return d.x1 - d.x0; })
.attr("height", function(d) { return d.y1 - d.y0; });
}
});
function sumByCount(d) {
return d.children ? 0 : 1;
}
function sumBySize(d) {
return d.size;
}
could someone help me with this . I think it has to do with the hierarchy.
I think I might need to change something here
d3.json("data.json", function(data) {
var root = d3.hierarchy(data)
.eachBefore(function(d) {
d.data.id = (d.parent ? d.parent.data.id + "." : "") + d.data.name; })
I appreciate any help
I'm trying to build a Zoomable TreeMap using d3 js. I need to take an array of JSON objects from the server and pass it to tha treeMap and let the treemap handle it. But in doing so I'm not able to parse it
Here's my code for the tremap:
$rootScope.loadTreeMap = function(path_to_data,dom_element_to_append_to){
var w = $(dom_element_to_append_to).width() - 80,
h = 800 - 180,
x = d3.scale.linear().range([0, w]),
y = d3.scale.linear().range([0, h]),
color = d3.scale.category20c(),
root,
node;
console.log("W" + w);
console.log("h " + h);
var treemap = d3.layout.treemap()
.round(false)
.size([w, h])
.sticky(true)
.value(function(d) { return d.size; });
var svg = d3.select(dom_element_to_append_to).append("div")
.attr("class", "chart")
.style("width", w + "px")
.style("height", h + "px")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(.5,.5)");
d3.json(path_to_data, function(data) {
node = root = data;
var nodes = treemap.nodes(root)
.filter(function(d) { return !d.children; });
var cell = svg.selectAll("g")
.data(nodes)
.enter().append("svg:g")
.attr("class", "cell")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return zoom(node == d.parent ? root : d.parent); });
cell.append("svg:rect")
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy - 1; })
.style("fill", function(d) { return color(d.parent.name); });
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; })
.style("opacity", function(d) { d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
d3.select(window).on("click", function() { zoom(root); });
d3.select("select").on("change", function() {
treemap.value(this.value == "size" ? size : count).nodes(root);
zoom(node);
});
});
function size(d) {
return d.size;
}
function count(d) {
return 1;
}
function zoom(d) {
var kx = w / d.dx, ky = h / d.dy;
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
var t = svg.selectAll("g.cell").transition()
.duration(d3.event.altKey ? 7500 : 750)
.attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });
t.select("rect")
.attr("width", function(d) { return kx * d.dx - 1; })
.attr("height", function(d) { return ky * d.dy - 1; })
t.select("text")
.attr("x", function(d) { return kx * d.dx / 2; })
.attr("y", function(d) { return ky * d.dy / 2; })
.style("opacity", function(d) { return kx * d.dx > d.w ? 1 : 0; });
node = d;
d3.event.stopPropagation();
}
}
`
Basically it is working fine if I load the data from a csv file stored in my system, but I want to read an array of objects from the server and then build the graph on it.
Basically here's my function which reads from the file and parses the JSON objects:
d3.json(path_to_data, function(data) {
/*console.log("data");
console.log(data);
console.log("data");
data = JSON.parse(inputData);
console.log(data);*/
node = root = data;
var nodes = treemap.nodes(root)
.filter(function(d) { return !d.children; });
var cell = svg.selectAll("g")
.data(nodes)
.enter().append("svg:g")
.attr("class", "cell")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return zoom(node == d.parent ? root : d.parent); });
cell.append("svg:rect")
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy - 1; })
.style("fill", function(d) { return color(d.parent.name); });
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; })
.style("opacity", function(d) { d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
d3.select(window).on("click", function() { zoom(root); });
d3.select("select").on("change", function() {
treemap.value(this.value == "size" ? size : count).nodes(root);
zoom(node);
});
});
But I want to do something different like
node = root = inputdata ; here input data is array of json objects fetched from server
var nodes = treemap.nodes(root).filter(function(d) { return !d.children; });
var cell = svg.selectAll("g")
i figured the solution to this just remove the d3.csv function ( but don't remove the body of function as your graph will generate with the body of functions only) and pass your own variable conatining the JSON objects as required by the graphs type. This will work in approximately all graphs in which data is taken through d3.csv or d3.json here's a working code
$rootScope.loadTreeMap = function(path_to_data,dom_element_to_append_to){
var w = $(dom_element_to_append_to).width() - 80,
h = 800 - 180,
x = d3.scale.linear().range([0, w]),
y = d3.scale.linear().range([0, h]),
color = d3.scale.category20c(),
root,
node;
console.log("W" + w);
console.log("h " + h);
var treemap = d3.layout.treemap()
.round(false)
.size([w, h])
.sticky(true)
.value(function(d) { return d.size; });
var svg = d3.select(dom_element_to_append_to).append("div")
.attr("class", "chart")
.style("width", w + "px")
.style("height", h + "px")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(.5,.5)");
/* d3.json(path_to_data, function(data) {*/
remove the above line and insert your own JSON object variable like
data = inputData(your own JSON variable)
node = root = data;
var nodes = treemap.nodes(root)
.filter(function(d) { return !d.children; });
var cell = svg.selectAll("g")
.data(nodes)
.enter().append("svg:g")
.attr("class", "cell")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return zoom(node == d.parent ? root : d.parent); });
cell.append("svg:rect")
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy - 1; })
.style("fill", function(d) { return color(d.parent.name); });
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; })
.style("opacity", function(d) { d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
d3.select(window).on("click", function() { zoom(root); });
d3.select("select").on("change", function() {
treemap.value(this.value == "size" ? size : count).nodes(root);
zoom(node);
});
});
function size(d) {
return d.size;
}
function count(d) {
return 1;
}
function zoom(d) {
var kx = w / d.dx, ky = h / d.dy;
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
var t = svg.selectAll("g.cell").transition()
.duration(d3.event.altKey ? 7500 : 750)
.attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });
t.select("rect")
.attr("width", function(d) { return kx * d.dx - 1; })
.attr("height", function(d) { return ky * d.dy - 1; })
t.select("text")
.attr("x", function(d) { return kx * d.dx / 2; })
.attr("y", function(d) { return ky * d.dy / 2; })
.style("opacity", function(d) { return kx * d.dx > d.w ? 1 : 0; });
node = d;
d3.event.stopPropagation();
}
}
So I am trying to create a legend in the bottom right corner of my D3. I have written all of the code for the legend but it comes up as just a black screen with the force-directed graph not showing up as well. Any advice would help.
Legend Code:
var color = d3.scale.ordinal()
.domain(["<400", "400-549", "550-699", "700-849", "850-999", "1000-1149", "1150-1299", "1300-1449", ">1450"])
.range(["#1a9850", "#66bd63", "#a6d96a","#d9ef8b","#ffffbf","#fee08b","#fdae61","#f46d43","#d73027"]);
var legend = d3.append('svg')
.append("g")
.selectAll("g")
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize;
var x = 0;
var y = i * height;
return 'translate(' + x + ',' + y + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
D3 Code:
function start(){
var w = 1200,
h = 600,
radius = 10,
node,
link,
root;
var count = 0;
var color = d3.scale.ordinal()
.domain(["<400", "400-549", "550-699", "700-849", "850-999", "1000-1149", "1150-1299", "1300-1449", ">1450"])
.range(["#1a9850", "#66bd63", "#a6d96a","#d9ef8b","#ffffbf","#fee08b","#fdae61","#f46d43","#d73027"]);
var force = d3.layout.force()
.on("tick", tick)
.charge(function(d) { return -500; })
.linkDistance(function(d) { return d.target._children ? 100 : 50; })
.size([w, h - 160]);
`
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#000");
var legend = d3.append('svg')
.append("g")
.selectAll("g")
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize;
var x = 0;
var y = i * height;
return 'translate(' + x + ',' + y + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
root = JSON.parse(jsonObject);
console.log("root"+root);
root.fixed = true;
root.x = w / 2;
root.y = h / 2 - 80;
update();
function update() {
var nodes = root.nodes,
links = root.links;
// Restart the force layout.
force
.nodes(nodes)
.links(links)
.start();
// Update the links…
link = svg.selectAll(".link")
.data(links);
// Enter any new links.
link.enter().insert("svg:line", ".node")
.attr("class", "link")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
// Exit any old links.
link.exit().remove();
// Update the nodes…
node = svg.selectAll("circle.node")
.data(nodes, function(d) {
return d.name;
})
.style("fill", color);
node.transition()
.attr("r", radius);
// Enter any new nodes.
node.enter().append("svg:circle")
.attr("xlink:href", function(d) { return d.image;})
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", radius)
.attr("r", function(d)
{return d.size * 2 ;})
.style("fill", color)
.on("click", click)
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
// Exit any old nodes.
node.exit().remove();
title = svg.selectAll("text.title")
.data(nodes);
// Enter any new titles.
title.enter()
.append("text")
.attr("class", "title");
//.text(function(d) { return d.name; });
// Exit any old titles.
title.exit().remove();
}
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
title.attr("transform", function(d){ return "translate("+d.x+","+d.y+")"; });
}
// Color leaf nodes orange, and packages white or blue.
function color(d) {
if(d._children){
return "#95a5a6";
}else{
switch(d.group) {
case 'r': //adverb
return "#e74c3c";
break;
case 'n': //noun
return "#3498db";
break;
case 'v': //verb
return "#2ecc71";
break;
case 's': //adjective
return "#e78229";
break;
default:
return "rgb(0, 238, 238)";
}
}
}
// Toggle children on click.
function click(d) {
document.getElementById("image").src = d.image;
document.getElementById("username").innerHTML = "Username:"+d.name;
document.getElementById("id").innerHTML = "ID:" + d.id;
document.getElementById("friends").innerHTML = d.friend;
document.getElementById("nodeTitle").innerHTML = "";
document.getElementById("size").innerHTML = d.size;
//document.getElementById("id").innerHTML = "Friend Count:" + d.name;
//if (d._children)
//grabImage();
//document.getElementById("image").innerHTML = (d.image);
/*if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();*/
}
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 16);
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", 8);
}
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [], i = 0;
function recurse(node) {
if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0);
if (!node.id) node.id = ++i;
nodes.push(node);
return node.size;
}
root.size = recurse(root);
return nodes;
}};
do{
var intervalID = window.setTimeout(start, 1000)
}
while(jsonObject!=""){
}
I believe the error is in here somewhere:
var force = d3.layout.force()
.on("tick", tick)
.charge(function(d) { return -500; })
.linkDistance(function(d) { return d.target._children ? 100 : 50; })
.size([w, h - 160]);
`
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
svg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#000");
var legend = d3.append('svg')
.append("g")
.selectAll("g")
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize;
var x = 0;
var y = i * height;
return 'translate(' + x + ',' + y + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
JSFIDDLE :
jsfiddle.net/d1kp0qeL/1
This is incorrect:
var legend = d3.append('svg').append("g")
should have been (you should append, the g group which holds legend to the svg)
var legend = svg.append("g")
I am trying to dynamically update my d3 treemap when the data updates. I have two functions, one that I call initially to build the treemap and another to redraw the treemap. When I redraw the treemap, I get a thin black bar on top of the treemap, and the first element in the array that I am graphing goes black. If I click on either of the two black elements. I get the error...
Uncaught TypeError: Cannot read property 'dx' of undefined
So the data being passed to the cell is undefined.
Additionally when I call regraph I have checked and the data has changed, but the graph is unchanged from when I initially built the treemap with the exception of the two black elements.
The code for building the treemap is below. Also the createObj function takes two arrays and creates a Json object.
function drawTreeMap(array1,array2){
console.log("got to drawing");
nestedJson=createObj(array1, array2);
w = 1880 - 80,
h = 900 - 180,
x = d3.scale.linear().range([0, w]),
y = d3.scale.linear().range([0, h]),
color = d3.scale.linear()
.range(['lightgreen', 'darkgreen']) // or use hex values
.domain([computeMin(array2), computeMaxNum(array2)]);
root,
node;
treemap = d3.layout.treemap()
.round(false)
.size([w, h])
.sticky(true)
.padding([10, 0, 0, 0])
.value(function(d) { return d.size; });
svg = 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)
.append("svg:g")
.attr("transform", "translate(.5,.5)");
node = root = nestedJson;
var nodes = treemap.nodes(root)
.filter(function(d) { return !d.children; });
var cell = svg.selectAll("g")
.data(nodes)
.enter().append("svg:g")
.attr("class", "cell")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return zoom(node == d.parent ? root : d.parent); });
cell.append("svg:rect")
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy ; })
.style("fill", function(d) { return color(d.size)});
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return (d.name+",-- "+d.size); })
.style("opacity", function(d) { d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
d3.select(window).on("click", function() { zoom(root); });
d3.select("select").on("change", function() {
treemap.value(this.value == "size" ? size : count).nodes(root);
zoom(node);
});
}
The code for redrawing is below.
function redrawGraphFromJson(data) {
treemap = d3.layout.treemap()
.round(false)
.size([w,h])
.value(function(d) { return d.size; })
.sticky(true);
// Draw the graph
node = root = data;
var nodes = treemap.nodes(root)
.filter(function(d) { return !d.children; });
var cell = svg.selectAll("g")
.data(nodes)
.attr("class", "cell")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return zoom(node == d.parent ? root : d.parent); });
cell.append("svg:rect")
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy ; })
.style("fill", function(d) { return color(d.size);});
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return (d.name+",-- "+d.size); })
.style("opacity", function(d) { d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
}
Thank you.
In your redrawing of the graph, you are going to want to .exit() on the initial graph and then .enter() to update the groups with the new data. This will replace the old map with the new.
http://bost.ocks.org/mike/join/ explains it perfectly and has a really good example to look at.
I have a large dataset being visualized by a treemap. There are 2 problems I am facing:
Load and display times on localhost are very slow. Expect worse performance over ajax calls. My dataset is 400 parents, each with approximately 10-15 children. A parent only have children (children do not have children)
Because there are 400 parents on the top level the initial treemap is very crowded. What I would like to implement is the fisheye functionality. However, my code below does not work, meaning when I move the mouse over the treemap I do not see the fisheye. I see the mouse x-y being correctly outputted to the console. but not the image.
var svg = 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)
.append("svg:g")
.attr("transform", "translate(.5,.5)");'
d3.json("../data/flare.json", function(data) {
node = root = data;
var nodes = treemap.nodes(root)
.filter(function(d) { return !d.children; });
var cell = svg.selectAll("g")
.data(nodes)
.enter()
.append("svg:g")
.attr("class", "cell")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.on("click", function(d) { return zoom(node == d.parent ? root : d.parent); });
cell.append("svg:rect")
.attr("width", function(d) { return d.dx - 1; })
.attr("height", function(d) { return d.dy - 1; })
.style("fill", function(d) { return color(d.parent.name); });
cell.append("svg:text")
.attr("x", function(d) { return d.dx / 2; })
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.text(function(d) { return d.name; })
.style("opacity", function(d) { d.w = this.getComputedTextLength(); return d.dx > d.w ? 1 : 0; });
d3.select(window).on("click", function() { zoom(root); });
d3.select("select").on("change", function() {
treemap.value(this.value == "size" ? size : count).nodes(root);
zoom(node);
});
var xFisheye = d3.fisheye.scale(d3.scale.identity).domain([0, w]).focus(360),
yFisheye = d3.fisheye.scale(d3.scale.identity).domain([0, h]).focus(90);
svg.on("mousemove", function() {
var mouse = d3.mouse(this);
xFisheye.focus(mouse[0]);
yFisheye.focus(mouse[1]);
redraw();
});
function redraw() {
cell.attr("x1", xFisheye).attr("x2", xFisheye);
cell.attr("y1", xFisheye).attr("y2", xFisheye);
}
});