Skip to content

Instantly share code, notes, and snippets.

@mbostock
Forked from mbostock/.block
Last active May 21, 2019 23:26
  • Star 6 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save mbostock/3048740 to your computer and use it in GitHub Desktop.
Stacked Radial Area
license: gpl-3.0

This plot might be suitable for showing cyclical trends, though I'm not sure it’s a great idea as the radial display has a number of limitations:

  • The underlying data goes from Sunday to Saturday, but the chart shows continuity from Saturday through to the previous Sunday. Time does not flow backwards, so you might instead prefer to plot two values for Sunday; this would show a discontinuity on opposite sides of the Sunday axis.

  • Displaying the discontinuity requires an open interpolator, rather than cardinal-closed as used here. However, this causes the tangents of the incoming and outgoing lines to no longer be orthogonal to the axis. To display the discontinuity properly, you’d need to write a custom interpolator to generate the correct tangents.

  • Due to the interpolation taking place in Cartesian (rather than polar) coordinates, the intermediate values of the lines do not have the correct radial values: if you tried to measure the intermediate values using the radius, they would be wrong. The only way to fix this is to interpolate in polar coordinates, which requires plotting the intermediate values as Archimedian spirals. There is no native representation for spirals in SVG, so you must resample the spirals as piecewise Bézier curves. Needless to say, this is a fair amount of work, but it might be something D3 supports in the future.

Polar charts are pretty. But when in doubt, it’s probably best to stick to Cartesian coordinates.

key value time
a 37 0
b 12 0
c 46 0
a 32 1
b 19 1
c 42 1
a 45 2
b 16 2
c 44 2
a 24 3
b 52 3
c 64 3
a 25 4
b 39 4
c 29 4
a 34 5
b 59 5
c 44 5
a 40 6
b 28 6
c 21 6
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis line {
stroke: #000;
}
.axis path {
fill: none;
stroke: #000;
}
.axis + .axis g text {
display: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var formatDate = d3.time.format("%a"),
formatDay = function(d) { return formatDate(new Date(2007, 0, d)); };
var width = 960,
height = 500,
outerRadius = height / 2 - 10,
innerRadius = 120;
var angle = d3.time.scale()
.range([0, 2 * Math.PI]);
var radius = d3.scale.linear()
.range([innerRadius, outerRadius]);
var z = d3.scale.category20c();
var stack = d3.layout.stack()
.offset("zero")
.values(function(d) { return d.values; })
.x(function(d) { return d.time; })
.y(function(d) { return d.value; });
var nest = d3.nest()
.key(function(d) { return d.key; });
var line = d3.svg.line.radial()
.interpolate("cardinal-closed")
.angle(function(d) { return angle(d.time); })
.radius(function(d) { return radius(d.y0 + d.y); });
var area = d3.svg.area.radial()
.interpolate("cardinal-closed")
.angle(function(d) { return angle(d.time); })
.innerRadius(function(d) { return radius(d.y0); })
.outerRadius(function(d) { return radius(d.y0 + d.y); });
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
d3.csv("data.csv", type, function(error, data) {
if (error) throw error;
var layers = stack(nest.entries(data));
// Extend the domain slightly to match the range of [0, 2π].
angle.domain([0, d3.max(data, function(d) { return d.time + 1; })]);
radius.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);
svg.selectAll(".layer")
.data(layers)
.enter().append("path")
.attr("class", "layer")
.attr("d", function(d) { return area(d.values); })
.style("fill", function(d, i) { return z(i); });
svg.selectAll(".axis")
.data(d3.range(angle.domain()[1]))
.enter().append("g")
.attr("class", "axis")
.attr("transform", function(d) { return "rotate(" + angle(d) * 180 / Math.PI + ")"; })
.call(d3.svg.axis()
.scale(radius.copy().range([-innerRadius, -outerRadius]))
.orient("left"))
.append("text")
.attr("y", -innerRadius + 6)
.attr("dy", ".71em")
.attr("text-anchor", "middle")
.text(function(d) { return formatDay(d); });
});
function type(d) {
d.time = +d.time;
d.value = +d.value;
return d;
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment