Hover shows employee details, dims more recent hires, and tallies office distribution of similarly senior staff. Adapted from Radial Tidy Tree by M. Bostock. block
xxxxxxxxxx
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.js"></script>
<link rel="icon" href="/favicon.png">
<style>
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
th {
text-align: right;
padding-right: 10px;
}
#person_detail {
margin-bottom: 16px;
}
#corner_tip {
position: fixed;
background-color: white;
opacity: 0.9;
padding: 10px;
}
</style>
<div id="d" style="position: relative; border: 0px solid green; width: <%= @diameter %>px; height: 900px; margin: 0px auto !important;">
<div id="corner_tip">
<div id="person_detail"></div>
<table id="people_count_table"></table>
</div>
</div>
<script>
var diameter = 1200
var tree = d3.layout.tree()
.size([360, diameter / 2 - 120])
.separation(function(a, b) { return (a.parent == b.parent ? 1 : 2) / a.depth; });
var colorOrdinal = d3.scale.category20();
var diagonal = d3.svg.diagonal.radial()
.projection(function(d) { return [d.y, d.x / 180 * Math.PI]; });
var svg = d3.select("#d").append("svg")
.attr("width", diameter)
.attr("height", diameter - 10)
.append("g")
.attr("transform", "translate(" + ((diameter/2)-60) + "," + ((diameter / 2) - 65) + ")");
// python -m SimpleHTTPServer 8000
d3.json( "organization.json" , function(error, people) {
var this_start = null
function lastName (name) { return name.replace(/\b[A-Z]\. /g,'') }
function startsFirst (a,b) { return string_to_date(a.start) - string_to_date(b.start) }
people.sort(startsFirst)
var root
function isManager (who) { return who.email == person.manager }
for (var person of people) {
var manager = people.find(isManager)
if (!manager || person.email == manager.email) {
root = person
} else {
manager.children = manager.children || []
manager.children.push(person)
}
}
var nodes = tree.nodes(root),
links = tree.links(nodes)
var link = svg.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("d", diagonal);
var node = svg.selectAll(".node")
.data(nodes)
.attr("opacity",1)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
node.append("circle")
.attr("r", 4)
.attr("opacity",1)
.style("fill", function(d) { return colorOrdinal(d.office) } )
.on('click', find)
function update_person (d) {
d3.select("#person_detail")
.html(
"<table>"+
"<tr><th>Name<td>"+d.name+
"<tr><th>Office<td>"+d.office+
"<tr><th>Start<td>"+d.start+
"</table>")
}
function update_counts () {
var peopleCount = {}
var total = 0
for (var person of people) {
if (!this_start || string_to_date(person.start) <= this_start) {
var office = person.office
peopleCount[office] = peopleCount[office] || 0
peopleCount[office]++
total++
}
}
var officeTuples = [];
for (var office in peopleCount) {
officeTuples.push([office, peopleCount[office]])
}
officeTuples.sort(function(a,b) {
return a[0] > b[0] ? 1 : -1
})
var table = d3.select("#people_count_table")
function row(tuple) {
var tr = table.append("tr")
var th = tr.append("th")
var td = tr.append("td")
var span = td.append("span")
th.text(tuple[0]);
span.html(tuple[1])
}
table.selectAll("*").remove()
for (var tuple of officeTuples) {
row(tuple)
}
row(['Total', total])
}
update_counts()
node.append("text")
.attr("dy", ".31em")
.attr("opacity",1)
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
.attr("transform", function(d) { return d.x < 180 ? "translate(8)" : "rotate(180)translate(-8)"; })
.text(function(d) { return lastName(d.name) });
node.selectAll("circle")
.on("mouseover", function(d, i) {
this_start = string_to_date(d.start);
d3.transition().duration(300).selectAll(".node").attr("opacity",
function(d){
return string_to_date(d.start) > this_start ? .2 : 1;
}
)
d3.transition().duration(300).selectAll(".circle").attr("opacity",
function(d){
if(string_to_date(d.start) > this_start){
return .2
} else {
// Count people here
return 1;
}
}
)
d3.select(this).attr("r", 7)
update_person(d)
update_counts()
})
node.selectAll("circle")
.on("mouseout", function(d, i) {
this_start = null
d3.select(this).attr("r", 4)
var d3Scale = d3.time.scale().domain([string_to_date(d.start), new Date() ]).range([0,2000]);
d3.selectAll(".node").transition().attr("opacity",1).duration(0).delay(function(d,i) {
return d3Scale(string_to_date(d.start));
})
d3.selectAll(".circle").transition().attr("opacity",1).duration(0).delay(function(d,i) {
return d3Scale(string_to_date(d.start));
})
update_counts()
})
});
function string_to_date(string_date){
return new Date(string_date);
}
function find(person) {
window.location = '//google.com?q=' + person.name
}
</script>
https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.13/d3.js