Skip to content

Instantly share code, notes, and snippets.

@bartaelterman
Last active August 29, 2015 14:14
Show Gist options
  • Save bartaelterman/52478dbded1372d21c2d to your computer and use it in GitHub Desktop.
Save bartaelterman/52478dbded1372d21c2d to your computer and use it in GitHub Desktop.
Bilevel sunburst example
{"name": "Coccinellidae", "children": [{"name": "Adalia", "children": [{"name": "Adalia bipunctata", "size": "3350"}, {"name": "Adalia decempunctata", "size": "1901"}]}, {"name": "Anatis", "children": [{"name": "Anatis ocellata", "size": "482"}]}, {"name": "Anisosticta", "children": [{"name": "Anisosticta novemdecimpunctata", "size": "784"}]}, {"name": "Aphidecta", "children": [{"name": "Aphidecta obliterata", "size": "500"}]}, {"name": "Calvia", "children": [{"name": "Calvia decemguttata", "size": "664"}, {"name": "Calvia quatuordecimguttata", "size": "1069"}]}, {"name": "Chilocorus", "children": [{"name": "Chilocorus bipustulatus", "size": "173"}, {"name": "Chilocorus renipustulatus", "size": "252"}]}, {"name": "Clitostethus", "children": [{"name": "Clitostethus arcuatus", "size": "7"}]}, {"name": "Coccidula", "children": [{"name": "Coccidula rufa", "size": "433"}, {"name": "Coccidula scutellata", "size": "271"}]}, {"name": "Coccinella", "children": [{"name": "Coccinella hieroglyphica", "size": "163"}, {"name": "Coccinella magnifica", "size": "162"}, {"name": "Coccinella quinquepunctata", "size": "763"}, {"name": "Coccinella septempunctata", "size": "5819"}, {"name": "Coccinella undecimpunctata", "size": "845"}]}, {"name": "Coccinula", "children": [{"name": "Coccinula quatuordecimpustulata", "size": "280"}]}, {"name": "Cryptolaemus", "children": [{"name": "Cryptolaemus montrouzieri", "size": "1"}]}, {"name": "Cynegetis", "children": [{"name": "Cynegetis impunctata", "size": "68"}]}, {"name": "Exochomus", "children": [{"name": "Exochomus nigromaculatus", "size": "282"}, {"name": "Exochomus quadripustulatus", "size": "1118"}]}, {"name": "Halyzia", "children": [{"name": "Halyzia sedecimguttata", "size": "1120"}]}, {"name": "Harmonia", "children": [{"name": "Harmonia axyridis", "size": "4882"}, {"name": "Harmonia quadripunctata", "size": "645"}]}, {"name": "Henosepilachna", "children": [{"name": "Henosepilachna argus", "size": "381"}]}, {"name": "Hippodamia", "children": [{"name": "Hippodamia septemmaculata", "size": "19"}, {"name": "Hippodamia tredecimpunctata", "size": "339"}, {"name": "Hippodamia variegata", "size": "840"}]}, {"name": "Hyperaspis", "children": [{"name": "Hyperaspis campestris", "size": "15"}]}, {"name": "Myrrha", "children": [{"name": "Myrrha octodecimguttata", "size": "261"}]}, {"name": "Myzia", "children": [{"name": "Myzia oblongoguttata", "size": "189"}]}, {"name": "Nephus", "children": [{"name": "Nephus bipunctatus", "size": "18"}, {"name": "Nephus redtenbacheri", "size": "5"}]}, {"name": "Oenopia", "children": [{"name": "Oenopia conglobata", "size": "638"}, {"name": "Oenopia impustulata", "size": "24"}]}, {"name": "Platynaspis", "children": [{"name": "Platynaspis luteorubra", "size": "86"}]}, {"name": "Propylea", "children": [{"name": "Propylea quatuordecimpunctata", "size": "3951"}]}, {"name": "Psyllobora", "children": [{"name": "Psyllobora vigintiduopunctata", "size": "2171"}]}, {"name": "Rhyzobius", "children": [{"name": "", "size": "10"}, {"name": "Rhyzobius chrysomeloides", "size": "321"}, {"name": "Rhyzobius forestieri", "size": "4"}, {"name": "Rhyzobius litura", "size": "211"}]}, {"name": "Scymnus", "children": [{"name": "Scymnus abietis", "size": "6"}, {"name": "Scymnus apetzi", "size": "19"}, {"name": "Scymnus ater", "size": "6"}, {"name": "Scymnus auritus", "size": "113"}, {"name": "Scymnus frontalis", "size": "116"}, {"name": "Scymnus haemorrhoidalis", "size": "59"}, {"name": "Scymnus interruptus", "size": "6"}, {"name": "Scymnus limbatus", "size": "2"}, {"name": "Scymnus mimulus", "size": "6"}, {"name": "Scymnus nigrinus", "size": "71"}, {"name": "Scymnus rubromaculatus", "size": "73"}, {"name": "Scymnus suturalis", "size": "138"}]}, {"name": "Sospita", "children": [{"name": "Sospita vigintiguttata", "size": "4"}]}, {"name": "Stethorus", "children": [{"name": "Stethorus punctillum", "size": "79"}]}, {"name": "Subcoccinella", "children": [{"name": "Subcoccinella vigintiquatuorpunctata", "size": "533"}]}, {"name": "Tytthaspis", "children": [{"name": "Tytthaspis sedecimpunctata", "size": "1068"}]}, {"name": "Vibidia", "children": [{"name": "Vibidia duodecimguttata", "size": "4"}]}]}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
circle,
path {
cursor: pointer;
}
circle {
fill: none;
pointer-events: all;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 350, right: 480, bottom: 350, left: 480},
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 10;
var hue = d3.scale.category10();
var luminance = d3.scale.sqrt()
.domain([0, 1e6])
.clamp(true)
.range([90, 20]);
var svg = d3.select("body").append("svg")
.attr("width", margin.left + margin.right)
.attr("height", margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var partition = d3.layout.partition()
.sort(function(a, b) { return d3.ascending(a.name, b.name); })
.size([2 * Math.PI, radius]);
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx - .01 / (d.depth + .5); })
.innerRadius(function(d) { return radius / 3 * d.depth; })
.outerRadius(function(d) { return radius / 3 * (d.depth + 1) - 1; });
d3.json("/bartaelterman/raw/52478dbded1372d21c2d/cocc_taxonomy.json", function(error, root) {
// Compute the initial layout on the entire tree to sum sizes.
// Also compute the full name and fill color for each node,
// and stash the children so they can be restored as we descend.
partition
.value(function(d) { return d.size; })
.nodes(root)
.forEach(function(d) {
d._children = d.children;
d.sum = d.value;
d.key = key(d);
d.fill = fill(d);
});
// Now redefine the value function to use the previously-computed sum.
partition
.children(function(d, depth) { return depth < 2 ? d._children : null; })
.value(function(d) { return d.sum; });
var center = svg.append("circle")
.attr("r", radius / 3)
.on("click", zoomOut);
center.append("title")
.text("zoom out");
var path = svg.selectAll("path")
.data(partition.nodes(root).slice(1))
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn)
.on("mouseover", function(d) {console.log(d.name)})
;
function zoomIn(p) {
if (p.depth > 1) p = p.parent;
if (!p.children) return;
zoom(p, p);
}
function zoomOut(p) {
if (!p.parent) return;
zoom(p.parent, p);
}
// Zoom to the specified new root.
function zoom(root, p) {
if (document.documentElement.__transition__) return;
// Rescale outside angles to match the new layout.
var enterArc,
exitArc,
outsideAngle = d3.scale.linear().domain([0, 2 * Math.PI]);
function insideArc(d) {
return p.key > d.key
? {depth: d.depth - 1, x: 0, dx: 0} : p.key < d.key
? {depth: d.depth - 1, x: 2 * Math.PI, dx: 0}
: {depth: 0, x: 0, dx: 2 * Math.PI};
}
function outsideArc(d) {
return {depth: d.depth + 1, x: outsideAngle(d.x), dx: outsideAngle(d.x + d.dx) - outsideAngle(d.x)};
}
center.datum(root);
// When zooming in, arcs enter from the outside and exit to the inside.
// Entering outside arcs start from the old layout.
if (root === p) enterArc = outsideArc, exitArc = insideArc, outsideAngle.range([p.x, p.x + p.dx]);
path = path.data(partition.nodes(root).slice(1), function(d) { return d.key; });
// When zooming out, arcs enter from the inside and exit to the outside.
// Exiting outside arcs transition to the new layout.
if (root !== p) enterArc = insideArc, exitArc = outsideArc, outsideAngle.range([p.x, p.x + p.dx]);
d3.transition().duration(d3.event.altKey ? 7500 : 750).each(function() {
path.exit().transition()
.style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
.attrTween("d", function(d) { return arcTween.call(this, exitArc(d)); })
.remove();
path.enter().append("path")
.style("fill-opacity", function(d) { return d.depth === 2 - (root === p) ? 1 : 0; })
.style("fill", function(d) { return d.fill; })
.on("click", zoomIn)
.each(function(d) { this._current = enterArc(d); });
path.transition()
.style("fill-opacity", 1)
.attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });
});
}
});
function key(d) {
var k = [], p = d;
while (p.depth) k.push(p.name), p = p.parent;
return k.reverse().join(".");
}
function fill(d) {
var p = d;
while (p.depth > 1) p = p.parent;
var c = d3.lab(hue(p.name));
c.l = luminance(d.sum);
return c;
}
function arcTween(b) {
var i = d3.interpolate(this._current, b);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function updateArc(d) {
return {depth: d.depth, x: d.x, dx: d.dx};
}
d3.select(self.frameElement).style("height", margin.top + margin.bottom + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment