Skip to content

Instantly share code, notes, and snippets.

@biovisualize
Created April 15, 2013 22:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save biovisualize/20437c547a3ab051d93d to your computer and use it in GitHub Desktop.
Save biovisualize/20437c547a3ab051d93d to your computer and use it in GitHub Desktop.
Binary tree

Binary tree with zoom/pan, percent leaves, link labels and path highlights

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<style>
</style>
</head>
<body>
<script>
var test = {
"sourceSheet":"mushroom.csv",
"visualization":{
"members":{
"edges":[[1,2],[1,3],[2,4],[2,5],[5,10],[5,11],[10,20],[10,21],[11,22],[11,23]],
"nodes":[
{"id":1,"isLeaf":false,"leftChild":3,"leftProbability":0.5622,"type":"categorical","values":["a","n","l"]},
{"id":2,"isLeaf":false,"leftChild":5,"leftProbability":0.018498754891497687,"type":"categorical","values":["r"]},
{"class":"p","id":4,"isLeaf":true,"splitPoint":0},
{"id":5,"isLeaf":false,"leftChild":11,"leftProbability":0.012323305545487495,"type":"categorical","values":["k"]},
{"id":10,"isLeaf":false,"leftChild":21,"leftProbability":0.29411764705882354,"type":"categorical","values":["w"]},
{"class":"e","id":20,"isLeaf":true,"splitPoint":0},{"class":"p","id":21,"isLeaf":true,"splitPoint":0},
{"id":11,"isLeaf":false,"leftChild":23,"leftProbability":0.0011009174311926607,"type":"categorical","values":["g"]},
{"class":"p","id":22,"isLeaf":true,"splitPoint":0},{"class":"e","id":23,"isLeaf":true,"splitPoint":0},
{"class":"p","id":3,"isLeaf":true,"splitPoint":0}
]
}
},
"workbookId":3
};
var dataEdges = test.visualization.members.edges;
var dataNodes = test.visualization.members.nodes;
var links = dataEdges.map(function(d, i){return {source: d[0], target: d[1]};})
var nodesByName = {};
links.forEach(function(link) {
var parent = link.source = nodeByName(link.source),
child = link.target = nodeByName(link.target);
if (parent.children) parent.children.push(child);
else parent.children = [child];
});
function nodeByName(name) {
var node = dataNodes.filter(function(d, i){return d.id == name;})[0];
console.log();
return nodesByName[name]
|| (nodesByName[name] = {
name: (node.isLeaf) ? name + ': ' + node.class : name + ': ' + node.values.join(','),
leftChild: node.leftChild,
id: name
});
}
var width = 960,
height = 500;
var tree = d3.layout.tree()
.size([width, height]);
var nodes = tree.nodes(links[0].source);
nodes.forEach(function(d, i){
if(!d.children) d.percentTrue = ~~(Math.random()*100);
d.linkName = (d.parent && d.parent.leftChild == d.id) ? 'No' : 'Yes';
});
// SVG skeleton
/////////////////////////////////
var svg = d3.select('body').append('svg')
.attr({width: width,
height: height+100
})
.append('g')
.call(d3.behavior.zoom()
.on("zoom", function() {
d3.select(this).select('g.pan').attr("transform","translate(" + d3.event.translate + ") scale(" + d3.event.scale + ")");
})
);
var chartGroup = svg.append('g')
.attr({'class': 'pan', width: width, height: height});
chartGroup.append('rect')
.attr({'class': 'background', x: -width, y: -height, width: width*3, height: height*3})
.style({stroke: 'silver', fill: 'white'});
// Marker
/////////////////////////////////
var markerSize = 6;
var marker = svg.append('defs')
.append('marker')
.attr({
id: 'Triangle',
refX: markerSize*2,
refY: markerSize,
markerUnits: 'userSpaceOnUse',
markerWidth: markerSize + markerSize,
markerHeight: markerSize * 2 + markerSize,
orient: '90deg'
})
.append('path')
.attr({d: 'M 0 0 '+markerSize*2+' '+markerSize+' 0 '+markerSize*2+' '+markerSize/2+' '+markerSize});
// Labels
/////////////////////////////////
var selectedPath = [];
var nodeWidth = 50;
var nodeHeight = 20;
var nodeGroup = chartGroup.selectAll('g.node')
.data(tree.nodes(links[0].source), function(d){ return d.id; })
.enter().append('g')
.attr({
'class': 'node',
transform: function(d, i){ return 'translate('+(d.px = d.x)+','+(d.py = d.y)+')'; }
})
.on('mouseover', function(d, i){
var parentTmp = nodes[i];
selectedPath = [];
d3.range(nodes[i].depth + 1).forEach(function(d, i){
selectedPath.push(parentTmp.name);
parentTmp = parentTmp.parent
});
nodeGroup.selectAll('rect.label-frame')
.filter(function(d, i){return selectedPath.indexOf(d.name) != -1;})
.style({stroke: 'red', 'stroke-width': 3});
chartGroup.selectAll('path.link').filter(function(d, i){
return selectedPath.indexOf(d.source.name) != -1
&& selectedPath.indexOf(d.target.name) != -1;
})
.style({stroke: 'red', 'stroke-width': 3});
})
.on('mouseout', function(d, i){
nodeGroup.selectAll('rect.label-frame')
.style({stroke: 'none'});
svg.selectAll('path.link')
.style({stroke: 'grey'});
});
nodeGroup.append('rect')
.attr({
'class': 'label-frame',
x: -nodeWidth/2,
y: 0,
width: nodeWidth,
height: nodeHeight
})
.style({fill: function(d, i){ return (d.children) ? '#336699' : 'yellowgreen'; }});
nodeGroup.filter(function(d, i){ return (!d.children); })
.append('rect')
.attr({
'class': 'percent',
x: function(d, i){ return -nodeWidth/2 + nodeWidth - nodeWidth / 100 * d.percentTrue; },
y: 0,
width: function(d, i){ return nodeWidth / 100 * d.percentTrue; },
height: nodeHeight
})
.style({fill: 'red'});
nodeGroup.append('text')
.attr({
'class': 'label-text',
x: -nodeWidth/2,
y: 0,
dx: '0.4em',
dy: '1em',
'pointer-events': 'none'
})
.style({fill: 'white'})
.text(function(d, i){ return d.name; });
// Links
/////////////////////////////////
var diagonal = d3.svg.diagonal();
var commonTextAttr = {
'class': 'edge-text',
x: -nodeWidth/2,
y: 0,
dx: '1em',
dy: '-1.5em'
};
nodeGroup.append('text')
.attr(commonTextAttr)
.style({fill: 'white', stroke: 'white', 'font-size': '14px', 'stroke-width': '3px', 'stroke-opacity': 1})
.text(function(d, i){ return d.linkName; });
nodeGroup.append('text')
.attr(commonTextAttr)
.style({fill: 'black', 'font-size': '14px'})
.text(function(d, i){return d.linkName;});
chartGroup.selectAll('path.link')
.data(tree.links(nodes), function(d){ return d.source.name + '-' + d.target.name; })
.enter().insert('path', '.node')
.attr({
'class': 'link',
d: diagonal,
'marker-end': 'url(#Triangle)'
})
.style({fill: 'none', stroke: 'grey', 'stroke-width': 2});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment