Skip to content

Instantly share code, notes, and snippets.

@cjhin cjhin/.block forked from mbostock/.block
Last active Jun 24, 2017

Embed
What would you like to do?
Org Chart (Radial -> Dendrogram Transition)
license: gpl-3.0
height: 950
border: no

Forked from Mike Bostock's dendrogram example, with a couple modifications:

  • Transition between cartesian and radial charts
  • slightly diff data file, classic employee model
  • straight line from node out
    • nvm, doesn't work with transition
  • legend, colors
name department manager
Homer Simpson CEO
Ned Stark HR Homer Simpson
Ash Ketchum Marketing Don Draper
Tony Stark Engineering Homer Simpson
Don Draper Marketing Leslie Knope
Borat Engineering Tony Stark
Master Chief IT Ned Stark
Shrek HR Ned Stark
Winnie the Pooh IT Master Chief
Wonder Woman Sales Homer Simpson
Stewie Griffin Sales Homer Simpson
Gollum Sales Stewie Griffin
Jack Sparrow Sales Gollum
Hermione Granger R&D Homer Simpson
Forrest Gump R&D Hermione Granger
Sherlock Holmes R&D Hermione Granger
Atticus Finch Sales Gollum
Scarlett O'Hara Marketing Don Draper
Mulan Engineering Tony Stark
Minnie Mouse Engineering Tony Stark
Liz Lemon Sales Wonder Woman
Leslie Knope Marketing Homer Simpson
The Little Prince R&D Sherlock Holmes
Lucille Bluth R&D Sherlock Holmes
Cookie Monster Sales Wonder Woman
Bob the Builder Engineering Mulan
Captain Ahab HR Ned Stark
Nancy Drew HR Shrek
Gilgamesh R&D Hermione Granger
Lady Macbeth Marketing Leslie Knope
Ebenezer Scrooge Sales Gollum
Tim Riggins Marketing Don Draper
Frasier Engineering Mulan
Edward Scissorhands HR Shrek
Buffy Sales Gollum
Carrie Bradshaw Marketing Leslie Knope
HAL 9000 R&D Sherlock Holmes
R2D2 Sales Wonder Woman
Astro Boy Sales Gollum
Godzilla IT Master Chief
Sir Lancelot HR Nancy Drew
The Cat in The Hat Sales Stewie Griffin
James Bond R&D Sherlock Holmes
Snow White Engineering Minnie Mouse
Vault Dweller Marketing Lady Macbeth
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: sans-serif;
}
.chart-title {
font-size: 1.5em;
}
.node text {
font: 10px sans-serif;
}
.node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
#toggle-button{
position: absolute;
bottom: 10px;
right: 10px;
padding: 10px 20px;
font-size: 2em;
text-align: center;
background: #FFF;
border-radius: 5px;
border: 1px solid #DDD;
}
#toggle-button:hover {
background: #CCC;
cursor: pointer;
}
</style>
<div id='toggle-button'>Toggle</div>
<svg width="900" height="830"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<!--<script src="./d3.v4.min.js"></script>-->
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + 80 + "," + 0 + ")");
var stratify = d3.stratify()
.id(function(d) { return d.name; })
.parentId(function(d) { return d.manager; });
function project(x, y) {
var angle = (x - 90) / 180 * Math.PI, radius = y;
return [radius * Math.cos(angle) + width/2.0, radius * Math.sin(angle) + height/2.0];
}
d3.csv("data.csv", function(error, data) {
if (error) throw error;
var color = d3.scaleOrdinal(d3.schemeCategory10);
// Title
svg.append("text")
.attr("x", "20")
.attr("y", "30")
.attr("class", "chart-title")
.text("Acme Org Chart");
// Setup legend
var legendDotSize = 30;
var legendWrapper = svg.append("g")
.attr("class", "legend")
.attr("transform", function(d) { return "translate(20,60)"; })
var colorData = Array.from(new Set(data.map(function(d) { return d.department; })))
.sort(function(a, b) { return a.localeCompare(b); });
// The text of the legend
var legendText = legendWrapper.selectAll("text")
.data(colorData);
legendText.enter().append("text")
.attr("y", function(d, i) { return i * legendDotSize + 12; })
.attr("x", 20)
.merge(legendText)
.text(function(d) {
return d;
});
// The dots of the legend
var legendDot = legendWrapper.selectAll("rect")
.data(colorData);
legendDot.enter().append("rect")
.attr("y", function(d, i) { return i * legendDotSize; })
.attr("rx", legendDotSize * 0.5)
.attr("ry", legendDotSize * 0.5)
.attr("width", legendDotSize * 0.5)
.attr("height", legendDotSize * 0.5)
.merge(legendDot)
.style("fill", function(d) { return color(d); });
// Interaction with button
var radialMode = true;
document.getElementById("toggle-button").onclick = function() { toggleMode() };
var toggleMode = function() {
var tree;
if(radialMode) {
tree = d3.cluster()
.size([360, 320]) // degrees, radius
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
} else {
tree = d3.cluster()
.size([820, 700]); // height, width
}
var root = stratify(data)
.sort(function(a, b) {
return (a.data.department.localeCompare(b.data.department) || b.height - a.height);
});
tree(root);
var node = g.selectAll(".node").data(root.descendants());
node.group = node.enter().append("g")
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); });
var link = g.selectAll(".link")
.data(root.descendants().slice(1));
if(radialMode) {
node.group.merge(node).transition().duration(1000)
.attr("transform", function(d) { return "translate(" + project(d.x, d.y) + ")"; });
node.group.append("text")
.text(function(d) { return d.data.name; })
.merge(node.select("text")).transition().duration(1000)
.attr("x", function(d) { return d.x < 180 === !d.children ? 6 : -1 * (this.getBBox().width + 6); })
.attr("transform", function(d) { return "rotate(" + (d.x < 180 ? d.x - 90 : d.x + 90) + ")"; });
link.enter().append("path")
.attr("class", "link")
.merge(link).transition().duration(1000)
.attr("d", function(d) {
// straight line from the root to first level, looks a little cleaner
// But, looks terrible on the transition between radial/tree, b/c diff kind of svg path
/*
if(d.depth == 1) {
return "M" + project(d.x, d.y)
+ "L" + project(d.parent.x, d.parent.y);
}
*/
return "M" + project(d.x, d.y)
+ "C" + project(d.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, d.parent.y);
});
} else {
node.group.merge(node).transition().duration(1000)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
node.group.append("text")
.text(function(d) { return d.data.name; })
.merge(node.select("text")).transition().duration(1000)
.attr("dy", 3)
.attr("x", function(d) { return d.children ? -1 * (this.getBBox().width + 6) : 6; })
.attr("transform", function(d) { return "rotate(0)"; });
link.enter().append("path")
.attr("class", "link")
.merge(link).transition().duration(1000)
.attr("d", function(d) {
return "M" + d.y + "," + d.x
+ "C" + (d.parent.y + 100) + "," + d.x
+ " " + (d.parent.y + 100) + "," + d.parent.x
+ " " + d.parent.y + "," + d.parent.x;
});
}
node.group.append("circle")
.attr("r", 3.5)
.attr("fill", function(d) {
return color(d.data.department)
});
// Toggle state
radialMode = !radialMode;
};
// run radial mode first!
toggleMode();
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.