Skip to content

Instantly share code, notes, and snippets.

@vwochnik
Forked from mbostock/.block
Last active March 8, 2016 13:48
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 vwochnik/6063b730954be56acd7c to your computer and use it in GitHub Desktop.
Save vwochnik/6063b730954be56acd7c to your computer and use it in GitHub Desktop.
Zoomable Sunburst
license: gpl-3.0

Click on any arc to zoom in. Click on the center circle to zoom out.

A sunburst is similar to a treemap, except it uses a radial layout. The root node of the tree is at the center, with leaves on the circumference. The area (or angle, depending on implementation) of each arc corresponds to its value. Sunburst design by John Stasko. Data courtesy Jeff Heer.

var width = 960,
height = 700,
radius = (Math.min(width, height) / 2) - 10;
gap = 1/720*radius*Math.PI;
var formatNumber = d3.format(",d");
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category20c();
var partition = d3.layout.partition()
.sort(null)
.value(function(d) { return d.size; });
//NOTE: the partition layout is changing the original nodes of the object
var _arc = d3.svg.arc();
function slice(d) {
var sa = Math.max(0, Math.min(2 * Math.PI, x(d.x))),
ea = Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx))),
ir = Math.max(0, y(d.y)),
or = Math.max(0, y(d.y + d.dy)),
a, cr, pa;
a = (ea - sa) % (2 * Math.PI);
while (a < 0) a += 2*Math.PI;
ir += Math.min(ir, gap);
pa = Math.min(a, 1/2*gap/or);
cr = Math.min(a, 2*Math.PI-a, 0.02) * 0.5*(radius+radius-ir);
return _arc
.startAngle(sa)
.endAngle(ea)
.padAngle(pa)
.innerRadius(ir)
.outerRadius(or)
.cornerRadius(cr)(d);
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2) + ")");
d3.json("flare.json", function(error, data) {
if (error) throw error;
var pdata = partition(clone(data));
var depth = d3.max(pdata, function(p) { return p.depth; });
var ydom = function(d) {
var need = 1/(depth+1);
if (d.depth == depth) {
return [d.y, 1 + need];
}
return [d.y, 1];
}
svg.selectAll("path")
.data(pdata).enter()
.append("path")
.attr("d", slice)
.style("fill", function(d, i) { return color(i); })
.on("click", click);
function click(d) {
svg.transition()
.duration(750)
.tween("scale", function() {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), ydom(d)),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(t) { x.domain(xd(t)); y.domain(yd(t)).range(yr(t)); };
})
.each("end", function() { addData(d); })
.selectAll("path")
.attrTween("d", function(d) { return function() { return slice(d); }; });
}
function addData(d) {
if ((!d.children) || (d.children.length === 0)) {
more(d, function(children) {
d.self.children = children;
pdata = partition(clone(data));
depth = d3.max(pdata, function(p) { return p.depth; });
pdata.forEach(function(part) {
if (part.self === d.self) {
y.domain(ydom(part));
}
});
svg.selectAll("path").remove();
svg.selectAll("path")
.data(pdata).enter()
.append("path")
.attr("d", slice)
.style("fill", function(d, i) { return color(i); })
.on("click", click)
.filter(function(d) {
return _.contains(children, d.self);
})
.style('opacity', 0)
.transition(750)
.style('opacity', 1);
});
}
}
});
function more(d, cb) {
var data = [];
var l = Math.max(1, Math.round(Math.random() * 10));
for (var i = 0; i < l; i++) {
data.push({name: "Child "+(i+1), size: d.size / l});
}
cb(data);
}
// clones hierarchy but leaves a self property back to the original object
var clone = function(data) {
var data2 = _.omit(data, 'children');
data2.self = data;
if (data.children)
data2.children = _.map(data.children, clone);
return data2;
};
{
"name": "flare",
"children": [
{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{"name": "AgglomerativeCluster", "size": 3938},
{"name": "CommunityStructure", "size": 3812},
{"name": "HierarchicalCluster", "size": 6714},
{"name": "MergeEdge", "size": 743}
]
},
{
"name": "graph",
"size": 500
},
{
"name": "optimization",
"children": [
{"name": "AspectRatioBanker", "size": 7074}
]
}
]
},
{
"name": "animate",
"children": [
{"name": "Easing", "size": 17010},
{"name": "FunctionSequence", "size": 5842},
{
"name": "interpolate",
"children": [
{"name": "ArrayInterpolator", "size": 1983},
{"name": "ColorInterpolator", "size": 2047}
]
},
{"name": "ISchedulable", "size": 1041},
{"name": "Tween", "size": 6006}
]
},
{
"name": "data",
"children": [
{
"name": "converters",
"size": 3000
},
{"name": "DataField", "size": 1759},
{"name": "DataSchema", "size": 2165},
{"name": "DataSet", "size": 586},
{"name": "DataSource", "size": 3331},
{"name": "DataTable", "size": 772},
{"name": "DataUtil", "size": 3322}
]
},
{
"name": "display",
"children": [
{"name": "DirtySprite", "size": 8833},
{"name": "LineSprite", "size": 1732},
{"name": "RectSprite", "size": 3623},
{"name": "TextSprite", "size": 10066}
]
},
{
"name": "flex",
"children": [
{"name": "FlareVis", "size": 4116}
]
},
{
"name": "physics",
"children": [
{"name": "DragForce", "size": 1082},
{"name": "GravityForce", "size": 1336},
{"name": "IForce", "size": 319},
{"name": "NBodyForce", "size": 10498},
{"name": "Particle", "size": 2822},
{"name": "Simulation", "size": 9983},
{"name": "Spring", "size": 2213},
{"name": "SpringForce", "size": 1681}
]
},
{
"name": "query",
"children": [
{"name": "AggregateExpression", "size": 1616},
{"name": "And", "size": 1027},
{"name": "Arithmetic", "size": 3891},
{"name": "Average", "size": 891},
{"name": "BinaryExpression", "size": 2893},
{"name": "Comparison", "size": 5103},
{"name": "CompositeExpression", "size": 3677},
{"name": "Count", "size": 781},
{"name": "DateUtil", "size": 4141},
{"name": "Distinct", "size": 933},
{"name": "Expression", "size": 5130},
{"name": "ExpressionIterator", "size": 3617},
{"name": "Fn", "size": 3240},
{"name": "If", "size": 2732},
{"name": "IsA", "size": 2039},
{"name": "Literal", "size": 1214},
{"name": "Match", "size": 3748},
{"name": "Maximum", "size": 843},
{
"name": "methods",
"children": [
{"name": "add", "size": 593},
{"name": "and", "size": 330},
{"name": "average", "size": 287},
{"name": "count", "size": 277},
{"name": "distinct", "size": 292},
{"name": "div", "size": 595},
{"name": "eq", "size": 594},
{"name": "fn", "size": 460},
{"name": "gt", "size": 603},
{"name": "gte", "size": 625},
{"name": "iff", "size": 748},
{"name": "isa", "size": 461},
{"name": "lt", "size": 597},
{"name": "lte", "size": 619},
{"name": "max", "size": 283},
{"name": "min", "size": 283},
{"name": "mod", "size": 591},
{"name": "mul", "size": 603},
{"name": "neq", "size": 599},
{"name": "not", "size": 386},
{"name": "or", "size": 323},
{"name": "orderby", "size": 307},
{"name": "range", "size": 772},
{"name": "select", "size": 296},
{"name": "stddev", "size": 363},
{"name": "sub", "size": 600},
{"name": "sum", "size": 280},
{"name": "update", "size": 307},
{"name": "variance", "size": 335},
{"name": "where", "size": 299},
{"name": "xor", "size": 354},
{"name": "_", "size": 264}
]
},
{"name": "Minimum", "size": 843},
{"name": "Xor", "size": 1101}
]
},
{
"name": "scale",
"children": [
{"name": "ScaleType", "size": 1821},
{"name": "TimeScale", "size": 5833}
]
},
{
"name": "util",
"children": [
{"name": "Filter", "size": 2324},
{"name": "Geometry", "size": 10993},
{
"name": "heap",
"children": [
{"name": "FibonacciHeap", "size": 9354},
{"name": "HeapNode", "size": 1233}
]
},
{"name": "IEvaluable", "size": 335},
{"name": "IPredicate", "size": 383},
{"name": "IValueProxy", "size": 874},
{
"name": "math",
"children": [
{"name": "DenseMatrix", "size": 3165},
{"name": "IMatrix", "size": 2815},
{"name": "SparseMatrix", "size": 3366}
]
},
{"name": "Maths", "size": 17705},
{"name": "Orientation", "size": 1486},
{
"name": "palette",
"children": [
{"name": "ColorPalette", "size": 6367},
{"name": "Palette", "size": 1229},
{"name": "ShapePalette", "size": 2059},
{"name": "SizePalette", "size": 2291}
]
},
{"name": "Property", "size": 5559},
{"name": "Shapes", "size": 19118},
{"name": "Sort", "size": 6887},
{"name": "Stats", "size": 6557},
{"name": "Strings", "size": 22026}
]
},
{
"name": "vis",
"children": [
{
"name": "axis",
"children": [
{"name": "Axes", "size": 1302},
{"name": "Axis", "size": 24593},
{"name": "AxisGridLine", "size": 652},
{"name": "AxisLabel", "size": 636},
{"name": "CartesianAxes", "size": 6703}
]
},
{
"name": "controls",
"children": [
{"name": "AnchorControl", "size": 2138},
{"name": "ClickControl", "size": 3824},
{"name": "Control", "size": 1353},
{"name": "ControlList", "size": 4665},
{"name": "DragControl", "size": 2649},
{"name": "ExpandControl", "size": 2832},
{"name": "HoverControl", "size": 4896},
{"name": "IControl", "size": 763},
{"name": "PanZoomControl", "size": 5222},
{"name": "SelectionControl", "size": 7862},
{"name": "TooltipControl", "size": 8435}
]
},
{
"name": "data",
"size": 100000
},
{
"name": "events",
"children": [
{"name": "DataEvent", "size": 2313},
{"name": "SelectionEvent", "size": 1880},
{"name": "TooltipEvent", "size": 1701},
{"name": "VisualizationEvent", "size": 1117}
]
},
{
"name": "legend",
"children": [
{"name": "Legend", "size": 20859},
{"name": "LegendItem", "size": 4614},
{"name": "LegendRange", "size": 10530}
]
},
{
"name": "operator",
"children": [
{
"name": "distortion",
"children": [
{"name": "BifocalDistortion", "size": 4461},
{"name": "Distortion", "size": 6314},
{"name": "FisheyeDistortion", "size": 3444}
]
},
{
"name": "encoder",
"children": [
{"name": "ColorEncoder", "size": 3179},
{"name": "Encoder", "size": 4060},
{"name": "PropertyEncoder", "size": 4138},
{"name": "ShapeEncoder", "size": 1690},
{"name": "SizeEncoder", "size": 1830}
]
},
{
"name": "filter",
"children": [
{"name": "FisheyeTreeFilter", "size": 5219},
{"name": "GraphDistanceFilter", "size": 3165},
{"name": "VisibilityFilter", "size": 3509}
]
},
{"name": "IOperator", "size": 1286},
{
"name": "label",
"size": 1000
},
{
"name": "layout",
"size:": 50000
}
]
},
{"name": "Visualization", "size": 16540}
]
}
]
}
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="draw.js"></script>
<script>
d3.select(self.frameElement).style("height", "700px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment