Sankey diagram with D3 v7 - mouseover node highlights links - javascript

I am making a Sankey diagram with D3 v7 where I hope that on mouseover of the node all connected paths will be highlighted and the other nodes will lower in opacity.
I’ve tried to follow this example: D3.js Sankey Chart - How can I highlight the set of links coming from a node? but I am new to JS so am not sure what this part is doing
function (l) {return l.source === d || l.target === d ? 0.5 : 0.2;});
I am finding that there are many examples of this for v4 of d3 but I can’t find one that works on v7.
In addition, I would like fade out all nodes that are not connected to the selected node. Is this possible?
Any advice would be very much appreciated!
Screen shot of current layout:
Would like it to be like this on mouseover of node:
// set the dimensions and margins of the graph
var margin = { top: 10, right: 50, bottom: 10, left: 50 },
width = 1920 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
// format variables
var formatNumber = d3.format(",.0f"), // zero decimal places
format = function (d) { return formatNumber(d); },
color = d3.scaleOrdinal().range(["#002060ff", "#164490ff", "#4d75bcff", "#98b3e6ff", "#d5e2feff", "#008cb0ff"]);
// append the svg object to the body of the page
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 + ")");
// Set the sankey diagram properties
var sankey = d3.sankey()
.nodeWidth(100)
.nodePadding(40)
.size([width, height]);
var path = sankey.links();
// load the data
d3.json("sankey.json").then(function (sankeydata) {
graph = sankey(sankeydata);
// add in the links
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function (d) { return d.width; });
// add the link titles
link.append("title")
.text(function (d) {
return d.source.name + " → " +
d.target.name;
});
// add in the nodes
var node = svg.append("g").selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
// add the rectangles for the nodes
node.append("rect")
.attr("x", function (d) { return d.x0; })
.attr("y", function (d) { return d.y0; })
.attr("height", function (d) { return d.y1 - d.y0; })
.attr("width", sankey.nodeWidth())
.style("fill", function (d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
// Attempt at getting whole length of link to highlight
.on("mouseover", function (d) {
link
.transition()
.duration(300)
.style("stroke-opacity", function (l) {
return l.source === d || l.target === d ? 0.5 : 0.2;
});
})
.on("mouseleave", function (d) {
link
.transition()
.duration(300)
.style("stroke-opacity", 0.2);
})
// Node hover titles
.append("title")
.text(function (d) {
return d.name + "\n" + format(d.value);
});
// add in the title for the nodes
node.append("text")
.style("fill", "#3f3f3f")
.attr("x", function (d) { return d.x0 - 6; })
.attr("y", function (d) { return (d.y1 + d.y0) / 2; })
.attr("dy", "0.35em")
.attr("text-anchor", "end")
.text(function (d) { return d.name; })
.filter(function (d) { return d.x0 < width / 2; })
.attr("x", function (d) { return d.x1 + 6; })
.attr("text-anchor", "start")
;
});

Related

Nodes in the collapsible tree get overlapped. D3.js

I am implementing a d3 code in power bi for a collapsible tree. Each node in my tree is a rectangular box. But for some reason the nodes get overlapped if the node size is large. Here is my code:
// ADD: translate function for the data
function toJSON(data) {
var flare = { name: "All Products", children: [] },
levels = ["productcategory","productsubcategory"];
// For each data row, loop through the expected levels traversing the output tree
data.forEach(function(d){
// Keep this as a reference to the current level
var depthCursor = flare.children;
// Go down one level at a time
levels.forEach(function( property, depth ){
// Look to see if a branch has already been created
var index;
depthCursor.forEach(function(child,i){
if ( d[property] == child.name ) index = i;
});
// Add a branch if it isn't there
if ( isNaN(index) ) {
depthCursor.push({ name : d[property], children : []});
index = depthCursor.length - 1;
}
// Now reference the new child array as we go deeper into the tree
depthCursor = depthCursor[index].children;
// This is a leaf, so add the last element to the specified branch
if ( depth === levels.length - 1 ) depthCursor.push({ name : d.product, size : d.revenue, revenue : d.revenue});
});
});
// End of conversion
return flare;
}
// Aggregate the revenue at each level
function aggregateRevenue(node) {
if(node.children) {
node.children.forEach(function(child) {
aggregateRevenue(child);
node.revenue = d3.sum(node.children, function(d) { return d.revenue; });
});
}
}
// Set the margins
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = pbi.width - margin.left - margin.right, // ALTER: Changed fixed width with the 'pbi.width' variable
height = pbi.height - margin.top - margin.bottom; // ALTER: Changed fixed height with the 'pbi.height' variable
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
// Zoom functionality:
var zoom = d3.behavior.zoom()
.scaleExtent([0.1, 10])
.on("zoom", zoomed);
function zoomed() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
// SVG creation
var svg = d3.select("#chart") // ALTER: Select SVG object; no need to create it
.attr("width", width + margin.left + margin.right) // ALTER: Add complete width
.attr("height", height + margin.top + margin.bottom) // ALTER: Add complete height
.call(zoom) // Add zoom behavior here
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// ALTER: Replaced the d3.json function with the pbi variant: pbi.dsv
pbi.dsv(function(data) {
var flare = toJSON(data); // ALTER: add extra convertion step to parent/child JSON
root = flare;
aggregateRevenue(flare);
root.x0 = height / 2;
root.y0 = 0;
// collapse the tree
function collapse(d) {
if (d.children) {
d._children = d.children;
d._children.forEach(collapse);
d.children = null;
}
}
root.children.forEach(collapse);
update(root);
});
d3.select(self.frameElement).style("height", height + margin.top + margin.bottom);
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click);
// Total revenue ( to calculate individual perncentages )
var totalRevenue = d3.sum(data, function(d) { return d.revenue; });
// Append rectangular node
nodeEnter.append("rect")
.attr("width", 150)
.attr("height", 80)
.attr("x", -75)
.attr("y", -40)
.style("fill", function(d) { return d._children ? pbi.colors[0] : pbi.colors[1]; });
// Append name of the node
nodeEnter.append("text")
.text(function(d) { return d.name; })
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("fill", "white")
.style("font-weight", "bold")
.attr("x", 0)
.attr("y", -15);
// Append the green stacked bar line
nodeEnter.append("rect")
.attr("class", "percentage")
.attr("width", function(d) { return ((d.revenue / totalRevenue) * 100).toFixed(2); })
.attr("height", 10)
.attr("x", -50)
.attr("y", -5)
.attr("fill", "green");
// Append the blue stacked bar line
nodeEnter.append("rect")
.attr("class", "non-percentage")
.attr("width", function(d) { return 100 - ((d.revenue / totalRevenue) * 100).toFixed(2); })
.attr("height", 10)
.attr("x", function(d) { return ((d.revenue / totalRevenue) * 100).toFixed(2) - 50; })
.attr("y", -5)
.attr("fill", "blue");
// Append the text for green bar
nodeEnter.append("text")
.attr("x", -70)
.attr("y", 0)
.attr("text-anchor", "start")
.attr("dominant-baseline", "central")
.text(function(d) { return ((d.revenue / totalRevenue) * 100).toFixed(2) + "%"; })
.style("fill", "white")
.style("font-weight", "bold");
// Append the text for blue bar
nodeEnter.append("text")
.attr("x", 35)
.attr("y", 0)
.attr("text-anchor", "start")
.attr("dominant-baseline", "central")
.text(function(d) { return (100 - ((d.revenue / totalRevenue) * 100)).toFixed(2) + "%"; })
.style("fill", "white")
.style("font-weight", "bold");
// Append node's revenue inside the node
nodeEnter.append("text")
.text(function(d) { return "Revenue = $" + d.revenue; })
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.style("fill", "white")
.style("font-weight", "bold")
.attr("x", 0)
.attr("y", 15);
// Append % of total share shared by the node
//nodeEnter.append("text")
//.text(function(d) { return "% Total Share = " + ((d.revenue / totalRevenue) * 100).toFixed(2) + "%"; })
//.attr("text-anchor", "middle")
//.attr("dominant-baseline", "central")
//.style("fill", "white")
//.style("font-weight", "bold")
//.attr("x", 0)
//.attr("y", 15);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
I also tried of following code but this does not seem to work also:
var treemap = d3.tree()
.size([10*height, width])
.separation(function separation(a, b) { return a.parent == b.parent ? 2 : 2; });
A help in this regard shall be appreciated.
You need to change the node size to your box(rectangle size) that is 80
var treemap = d3.tree()
.size([80, width])
.separation(function separation(a, b) { return a.parent == b.parent ? 2 : 2; });

Prevent loop in D3js Sankey chart

I'm trying to create a sankey chart using the d3 sankey plugin with dynamic shipping data. It works great most of the time except when I get a data set like this:
[{"DeparturePort":"CHARLESTON","ArrivalPort":"BREMERHAVEN","volume":5625.74},{"DeparturePort":"CHARLESTON","ArrivalPort":"ITAPOA","volume":2340},{"DeparturePort":"PT EVERGLADES","ArrivalPort":"PT AU PRINCE","volume":41.02},{"DeparturePort":"BREMERHAVEN","ArrivalPort":"CHARLESTON","volume":28}]
The key to my issue is the first and last entry in the data set. It seems that having opposite directions in the same sankey chart sends the javascript into an infinite loop and kills the browser. Any ideas on how to prevent this from happening?
Here's my chart code where raw would be the object above:
var data = raw;
var units = "Volume";
var margin = { top: 100, right: 0, bottom: 30, left: 0 },
width = $("#"+divID).width() - margin.left - margin.right,
height = divID == "enlargeChart" ? 800 - margin.top - margin.bottom : 600 - margin.top - margin.bottom;
var formatNumber = d3.format(",.0f"), // zero decimal places
format = function (d) { return ""; },
color = d3.scale.ordinal()
.range(["#0077c0", "#FF6600"]);
// append the svg canvas to the page
var svg = d3.select("#"+divID).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.style("font-size", "12px")
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// Set the sankey diagram properties
var sankey = d3.sankey(width)
.nodeWidth(10)
.nodePadding(10)
.size([width, height]);
var path = sankey.link();
// load the data (using the timelyportfolio csv method)
//d3.csv("sankey.csv", function (error, data) {
//set up graph in same style as original example but empty
graph = { "nodes": [], "links": [] };
var checklist = [];
data.forEach(function (d) {
if ($.inArray(d.DeparturePort, checklist) == -1) {
checklist.push(d.DeparturePort)
graph.nodes.push({ "name": d.DeparturePort });
}
if ($.inArray(d.ArrivalPort, checklist) == -1) {
checklist.push(d.ArrivalPort)
graph.nodes.push({ "name": d.ArrivalPort });
}
graph.links.push({
"source": d.DeparturePort,
"target": d.ArrivalPort,
"value": +d.volume
});
});
// return only the distinct / unique nodes
graph.nodes = d3.keys(d3.nest()
.key(function (d) { return d.name; })
.map(graph.nodes));
// loop through each link replacing the text with its index from node
graph.links.forEach(function (d, i) {
graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);
graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);
});
//now loop through each nodes to make nodes an array of objects
// rather than an array of strings
graph.nodes.forEach(function (d, i) {
graph.nodes[i] = { "name": d };
});
sankey
.nodes(graph.nodes)
.links(graph.links)
.layout(32);
// add in the links
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function (d) { return Math.max(1, d.dy); })
.sort(function (a, b) { setTimeout(function () { return b.dy - a.dy; }, 10);});
// add the link titles
link.append("title")
.text(function (d) {
return d.source.name + " → " +
d.target.name + "\n" + d.value.toFixed(0) + " TEU";
});
$("#" + divID + " .loading").addClass("hide");
// add in the nodes
var node = svg.append("g").selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
//setTimeout(function () {
return "translate(" + d.x + "," + d.y + ")";
//}, 10);
})
.call(d3.behavior.drag()
.origin(function (d) { return d; })
.on("dragstart", function () {
this.parentNode.appendChild(this);
})
.on("drag", dragmove));
// add the rectangles for the nodes
node.append("rect")
.attr("height", function (d) { if (d.dy < 0) { d.dy = (d.dy * -1); } return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function (d) {
return d.color = color(d.name);
})
.style("stroke", function (d) {
return d3.rgb(d.color);
})
.append("title")
.text(function (d) {
return d.name + "\n" + format(d.value);
});
// add in the title for the nodes
node.append("text")
.attr("x", -6)
.attr("y", function (d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.style("stroke", function (d) { return "#000000" })
.attr("transform", null)
.text(function (d) { return d.name; })
.filter(function (d) { return d.x < width / 2; })
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
// the function for moving the nodes
function dragmove(d) {
d3.select(this).attr("transform",
"translate(" + d.x + "," + (
d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", path);
}
}, 0)
It's an issue with the sankey.js script.
See this commit (on a fork of sankey.js) which fixed it:
https://github.com/soxofaan/d3-plugin-captain-sankey/commit/0edba18918aac3e9afadffd4a169c47f88a98f81
while (remainingNodes.length) {
becomes:
while (remainingNodes.length && x < nodes.length) {
That should prevent the endless loop.

How to force y position of one branch in d3 sankey plugin?

I would like to force one branch of sankey diagram to be on top.
Instead of diagram like this:
would like to generate diagram where nodes 1, 2, 7, 15, 10 and 14 are always on top:
Link to fiddle with current code: http://jsfiddle.net/w5jfp9t0/1/
var margin = {top: 1, right: 1, bottom: 6, left: 1};
var width = 1052 - margin.left - margin.right;
var height = 500 - margin.top - margin.bottom;
var formatNumber = d3.format(",.0f"),
format = function(d) { return formatNumber(d); },
color = d3.scale.category20();
var svg = d3.select("#chart_sankey").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 sankey = d3.sankey()
.nodeWidth(35)
.nodePadding(10)
.size([width, height]);
var path = sankey.link();
raw = '{"nodes":[{"name":"Node 1"},{"name":"Node 2"},{"name":"Node 3"},{"name":"Node 4"},{"name":"Node 5"},{"name":"Node 6"},{"name":"Node 7"},{"name":"Node 8"},{"name":"Node 9"},{"name":"Node 10"},{"name":"Node 11"},{"name":"Node 12"},{"name":"Node 13"},{"name":"Node 14"},{"name":"Node 15"}],"links":[{"source":9,"target":13,"value":25},{"source":14,"target":9,"value":37},{"source":14,"target":11,"value":16},{"source":14,"target":12,"value":8},{"source":14,"target":10,"value":68},{"source":6,"target":14,"value":154},{"source":6,"target":8,"value":40},{"source":1,"target":6,"value":345},{"source":1,"target":7,"value":66},{"source":1,"target":3,"value":17},{"source":1,"target":4,"value":25},{"source":1,"target":5,"value":117},{"source":0,"target":1,"value":692},{"source":0,"target":2,"value":19}]}';
data = JSON.parse(raw);
sankey.nodes(data.nodes)
.links(data.links)
.layout(32);
var link = svg.append("g")
.selectAll(".link")
.data(data.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
var nodes = data.nodes;
var node = svg.append("g").selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; })
.call(d3.behavior.drag()
.origin(function(d) { return d; })
.on("dragstart", function() { this.parentNode.appendChild(this); })
.on("drag", dragmove));
sankey.relayout();
node.filter(function(d) { return d.value != 0; }) // append text only if node value is not zero
.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
.append("title")
.text(function(d) { return d.name + "\n" + format(d.value); });
node.filter(function(d) { return d.value != 0; }) // append text only if node value is not zero
.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function(d) { return d.name; })
.filter(function(d) { return d.x == 0; }) // at first column append text after column
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
function dragmove(d) {
d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")");
sankey.relayout();
link.attr("d", path);
}
What do I need to change to accomplish this?
Note
To be honest, I never used that plugin. I don't see an option to get the desired behaviour directly - thus I looked at the source of sankey.js to make the adjustments. Below I show how I'd modify - you might want to do it more thoroughly :)
Idea
Looking at the code of sankey.js, you see that the nodes are placed (y-direction) using the center function:
function center(node) {
return node.y + node.dy / 2;
}
As I don't see a parameter to change that behaviour, you can change it to:
function center(node) {
return 0;
}
If you then also revert the sorting order:
function ascendingDepth(a, b) {
return b.y - a.y;
}
you get the following picture:

dynamically update d3.js treemap

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.

D3 Treemap with fisheye

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);
}
});

Categories