D3js: Add Quantitative Dimension To Hierarchical Edge Bundling
Given Mike Bostock's example on Hierarchical Edge Bundling (https://bl.ocks.org/mbostock/7607999), how would one add a quantitative dimension to the plot? What I want to achieve i
Solution 1:
If I understand your question correctly you want to start with data like:
[
{
"name": "flare.analytics.cluster.AgglomerativeCluster",
"size": 3938,
"imports": [
{
"link": "flare.animate.Transitioner",
"value": 47.194114234125514
},
{
"link": "flare.vis.data.DataList",
"value": 66.57002100495298
},
{
"link": "flare.util.math.IMatrix",
"value": 5.987508739765435
},
{
"link": "flare.analytics.cluster.MergeEdge",
"value": 31.750046370493678
},
{
"link": "flare.analytics.cluster.HierarchicalCluster",
"value": 10.186873728884827
},
{
"link": "flare.vis.data.Data",
"value": 28.60757703865271
}
]
},
...
And then color the links based on the value
property in the imports.
To do that given the linked example, modify the packageImports
function to retain the value property in the links
collection:
// Return a list of imports for the given array of nodes.functionpackageImports(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.imports) d.imports.forEach(function(i) {
var target = map[i.link]; // find the target
target.value = i.value; // retain the value
imports.push({source: map[d.name], target: target});
});
});
return imports;
}
Then add the color when you append the nodes:
var colorScale = d3.scale.quantize()
.range(["#2c7bb6", "#00a6ca","#00ccbc","#90eb9d","#ffff8c","#f9d057","#f29e2e","#e76818","#d7191c"])
.domain([0,100]);
...
link = link
.data(bundle(links))
.enter().append("path")
.each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
.attr("class", "link")
.attr("d", line)
.style("stroke", function(d){
returncolorScale(d.target.value); //<-- add color
});
Full running code:
<!DOCTYPE html><metacharset="utf-8"><style>.node {
font: 30011px"Helvetica Neue", Helvetica, Arial, sans-serif;
fill: #bbb;
}
.node:hover {
fill: #000;
}
.link {
fill: none;
pointer-events: none;
}
.node:hover,
.node--source,
.node--target {
font-weight: 700;
}
.link--source,
.link--target {
stroke-opacity: 1;
stroke-width: 2px;
}
.link--source {
stroke: #d62728;
}
.link--target {
stroke: #2ca02c;
}
</style><body><scriptsrc="//d3js.org/d3.v3.min.js"></script><script>var diameter = 960,
radius = diameter / 2,
innerRadius = radius - 120;
var cluster = d3.layout.cluster()
.size([360, innerRadius])
.sort(null)
.value(function(d) { return d.size; });
var bundle = d3.layout.bundle();
var line = d3.svg.line.radial()
.interpolate("bundle")
.tension(.85)
.radius(function(d) { return d.y; })
.angle(function(d) { return d.x / 180 * Math.PI; });
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
var link = svg.append("g").selectAll(".link"),
node = svg.append("g").selectAll(".node");
d3.json("https://jsonblob.com/api/851bd2f2-d85d-11e6-b16a-ad927fd57221", function(error, classes) {
if (error) throw error;
var nodes = cluster.nodes(packageHierarchy(classes)),
links = packageImports(nodes);
var colorScale = d3.scale.quantize()
.range(["#2c7bb6", "#00a6ca","#00ccbc","#90eb9d","#ffff8c","#f9d057","#f29e2e","#e76818","#d7191c"])
.domain([0,100]);
link = link
.data(bundle(links))
.enter().append("path")
.each(function(d) { d.source = d[0], d.target = d[d.length - 1]; })
.attr("class", "link")
.attr("d", line)
.style("stroke", function(d){
returncolorScale(d.target.value);
})
node = node
.data(nodes.filter(function(n) { return !n.children; }))
.enter().append("text")
.attr("class", "node")
.attr("dy", ".31em")
.attr("transform", function(d) { return"rotate(" + (d.x - 90) + ")translate(" + (d.y + 8) + ",0)" + (d.x < 180 ? "" : "rotate(180)"); })
.style("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.text(function(d) { return d.key; })
//.on("mouseover", mouseovered)//.on("mouseout", mouseouted);
});
/*
function mouseovered(d) {
node
.each(function(n) { n.target = n.source = false; });
link
.classed("link--target", function(l) { if (l.target === d) return l.source.source = true; })
.classed("link--source", function(l) { if (l.source === d) return l.target.target = true; })
.filter(function(l) { return l.target === d || l.source === d; })
.each(function() { this.parentNode.appendChild(this); });
node
.classed("node--target", function(n) { return n.target; })
.classed("node--source", function(n) { return n.source; });
}
function mouseouted(d) {
link
.classed("link--target", false)
.classed("link--source", false);
node
.classed("node--target", false)
.classed("node--source", false);
}
*///d3.select(self.frameElement).style("height", diameter + "px");// Lazily construct the package hierarchy from class names.functionpackageHierarchy(classes) {
var map = {};
functionfind(name, data) {
var node = map[name], i;
if (!node) {
node = map[name] = data || {name: name, children: []};
if (name.length) {
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
node.parent.children.push(node);
node.key = name.substring(i + 1);
}
}
return node;
}
classes.forEach(function(d) {
find(d.name, d);
});
return map[""];
}
// Return a list of imports for the given array of nodes.functionpackageImports(nodes) {
var map = {},
imports = [];
// Compute a map from name to node.
nodes.forEach(function(d) {
map[d.name] = d;
});
// For each import, construct a link from the source to target node.
nodes.forEach(function(d) {
if (d.imports) d.imports.forEach(function(i) {
var target = map[i.link];
target.value = i.value;
imports.push({source: map[d.name], target: target});
});
});
return imports;
}
</script>
Post a Comment for "D3js: Add Quantitative Dimension To Hierarchical Edge Bundling"