An example of the d3.horizon plugin.
-
-
Save bewest/4316717 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
d3.horizon = function() { | |
var bands = 1, // between 1 and 5, typically | |
mode = "offset", // or mirror | |
interpolate = "linear", // or basis, monotone, step-before, etc. | |
x = d3_horizonX, | |
y = d3_horizonY, | |
w = 960, | |
h = 40, | |
duration = 0; | |
var color = d3.scale.linear() | |
.domain([-1, 0, 0, 1]) | |
.range(["#08519c", "#bdd7e7", "#bae4b3", "#006d2c"]); | |
// For each small multiple… | |
function horizon(g) { | |
g.each(function(d, i) { | |
var g = d3.select(this), | |
n = 2 * bands + 1, | |
xMin = Infinity, | |
xMax = -Infinity, | |
yMax = -Infinity, | |
x0, // old x-scale | |
y0, // old y-scale | |
id; // unique id for paths | |
// Compute x- and y-values along with extents. | |
var data = d.map(function(d, i) { | |
var xv = x.call(this, d, i), | |
yv = y.call(this, d, i); | |
if (xv < xMin) xMin = xv; | |
if (xv > xMax) xMax = xv; | |
if (-yv > yMax) yMax = -yv; | |
if (yv > yMax) yMax = yv; | |
return [xv, yv]; | |
}); | |
// Compute the new x- and y-scales, and transform. | |
var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]), | |
y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]), | |
t1 = d3_horizonTransform(bands, h, mode); | |
// Retrieve the old scales, if this is an update. | |
if (this.__chart__) { | |
x0 = this.__chart__.x; | |
y0 = this.__chart__.y; | |
t0 = this.__chart__.t; | |
id = this.__chart__.id; | |
} else { | |
x0 = x1.copy(); | |
y0 = y1.copy(); | |
t0 = t1; | |
id = ++d3_horizonId; | |
} | |
// We'll use a defs to store the area path and the clip path. | |
var defs = g.selectAll("defs") | |
.data([null]); | |
// The clip path is a simple rect. | |
defs.enter().append("defs").append("clipPath") | |
.attr("id", "d3_horizon_clip" + id) | |
.append("rect") | |
.attr("width", w) | |
.attr("height", h); | |
defs.select("rect").transition() | |
.duration(duration) | |
.attr("width", w) | |
.attr("height", h); | |
// We'll use a container to clip all horizon layers at once. | |
g.selectAll("g") | |
.data([null]) | |
.enter().append("g") | |
.attr("clip-path", "url(#d3_horizon_clip" + id + ")"); | |
// Instantiate each copy of the path with different transforms. | |
var path = g.select("g").selectAll("path") | |
.data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number); | |
var d0 = d3_horizonArea | |
.interpolate(interpolate) | |
.x(function(d) { return x0(d[0]); }) | |
.y0(h * bands) | |
.y1(function(d) { return h * bands - y0(d[1]); }) | |
(data); | |
var d1 = d3_horizonArea | |
.x(function(d) { return x1(d[0]); }) | |
.y1(function(d) { return h * bands - y1(d[1]); }) | |
(data); | |
path.enter().append("path") | |
.style("fill", color) | |
.attr("transform", t0) | |
.attr("d", d0); | |
path.transition() | |
.duration(duration) | |
.style("fill", color) | |
.attr("transform", t1) | |
.attr("d", d1); | |
path.exit().transition() | |
.duration(duration) | |
.attr("transform", t1) | |
.attr("d", d1) | |
.remove(); | |
// Stash the new scales. | |
this.__chart__ = {x: x1, y: y1, t: t1, id: id}; | |
}); | |
d3.timer.flush(); | |
} | |
horizon.duration = function(x) { | |
if (!arguments.length) return duration; | |
duration = +x; | |
return horizon; | |
}; | |
horizon.bands = function(x) { | |
if (!arguments.length) return bands; | |
bands = +x; | |
color.domain([-bands, 0, 0, bands]); | |
return horizon; | |
}; | |
horizon.mode = function(x) { | |
if (!arguments.length) return mode; | |
mode = x + ""; | |
return horizon; | |
}; | |
horizon.colors = function(x) { | |
if (!arguments.length) return color.range(); | |
color.range(x); | |
return horizon; | |
}; | |
horizon.interpolate = function(x) { | |
if (!arguments.length) return interpolate; | |
interpolate = x + ""; | |
return horizon; | |
}; | |
horizon.x = function(z) { | |
if (!arguments.length) return x; | |
x = z; | |
return horizon; | |
}; | |
horizon.y = function(z) { | |
if (!arguments.length) return y; | |
y = z; | |
return horizon; | |
}; | |
horizon.width = function(x) { | |
if (!arguments.length) return w; | |
w = +x; | |
return horizon; | |
}; | |
horizon.height = function(x) { | |
if (!arguments.length) return h; | |
h = +x; | |
return horizon; | |
}; | |
return horizon; | |
}; | |
var d3_horizonArea = d3.svg.area(), | |
d3_horizonId = 0; | |
function d3_horizonX(d) { | |
return d[0]; | |
} | |
function d3_horizonY(d) { | |
return d[1]; | |
} | |
function d3_horizonTransform(bands, h, mode) { | |
return mode == "offset" | |
? function(d) { return "translate(0," + (d + (d < 0) - bands) * h + ")"; } | |
: function(d) { return (d < 0 ? "scale(1,-1)" : "") + "translate(0," + (d - bands) * h + ")"; }; | |
} | |
})(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<style> | |
#horizon-controls { | |
position: relative; | |
width: 100%; | |
font-size: 16px; | |
margin-bottom: 6px; | |
} | |
#horizon-bands { | |
position: absolute; | |
right: 0; | |
} | |
</style> | |
<div id="horizon-controls"> | |
<input name="mode" type="radio" value="mirror" id="horizon-mode-mirror" checked><label for="horizon-mode-mirror"> Mirror</label> | |
<input name="mode" type="radio" value="offset" id="horizon-mode-offset"><label for="horizon-mode-offset"> Offset</label> | |
<span id="horizon-bands"><span id="horizon-bands-value">1</span> <button class="first">−</button><button class="last">+</button></span> | |
</div> | |
<div id="horizon-chart"></div> | |
<script src="http://d3js.org/d3.v2.min.js?2.9.5"></script> | |
<script src="horizon.js?0.0.1"></script> | |
<script> | |
var width = 960, | |
height = 360; | |
var chart = d3.horizon() | |
.width(width) | |
.height(height) | |
.bands(1) | |
.mode("mirror") | |
.interpolate("basis"); | |
var svg = d3.select("body").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
d3.json("unemployment.json", function(data) { | |
// Offset so that positive is above-average and negative is below-average. | |
var mean = data.rate.reduce(function(p, v) { return p + v; }, 0) / data.rate.length; | |
// Transpose column values to rows. | |
data = data.rate.map(function(rate, i) { | |
return [Date.UTC(data.year[i], data.month[i] - 1), rate - mean]; | |
}); | |
// Render the chart. | |
svg.data([data]).call(chart); | |
// Enable mode buttons. | |
d3.selectAll("#horizon-controls input[name=mode]").on("change", function() { | |
svg.call(chart.duration(0).mode(this.value)); | |
}); | |
// Enable bands buttons. | |
d3.selectAll("#horizon-bands button").data([-1, 1]).on("click", function(d) { | |
var n = Math.max(1, chart.bands() + d); | |
d3.select("#horizon-bands-value").text(n); | |
svg.call(chart.duration(1000).bands(n).height(height / n)); | |
}); | |
}); | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"year":[2000,2000,2000,2000,2000,2000,2000,2000,2000,2000,2000,2000,2001,2001,2001,2001,2001,2001,2001,2001,2001,2001,2001,2001,2002,2002,2002,2002,2002,2002,2002,2002,2002,2002,2002,2002,2003,2003,2003,2003,2003,2003,2003,2003,2003,2003,2003,2003,2004,2004,2004,2004,2004,2004,2004,2004,2004,2004,2004,2004,2005,2005,2005,2005,2005,2005,2005,2005,2005,2005,2005,2005,2006,2006,2006,2006,2006,2006,2006,2006,2006,2006,2006,2006,2007,2007,2007,2007,2007,2007,2007,2007,2007,2007,2007,2007,2008,2008,2008,2008,2008,2008,2008,2008,2008,2008,2008,2008,2009,2009,2009,2009,2009,2009,2009,2009,2009,2009,2009,2009,2010,2010],"month":[1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8,9,10,11,12,1,2],"rate":[4.5,4.4,4.3,3.7,3.8,4.1,4.2,4.1,3.8,3.6,3.7,3.7,4.7,4.6,4.5,4.2,4.1,4.7,4.7,4.9,4.7,5,5.3,5.4,6.3,6.1,6.1,5.7,5.5,6,5.9,5.7,5.4,5.3,5.6,5.7,6.5,6.4,6.2,5.8,5.8,6.5,6.3,6,5.8,5.6,5.6,5.4,6.3,6,6,5.4,5.3,5.8,5.7,5.4,5.1,5.1,5.2,5.1,5.7,5.8,5.4,4.9,4.9,5.2,5.2,4.9,4.8,4.6,4.8,4.6,5.1,5.1,4.8,4.5,4.4,4.8,5,4.6,4.4,4.1,4.3,4.3,5,4.9,4.5,4.3,4.3,4.7,4.9,4.6,4.5,4.4,4.5,4.8,5.4,5.2,5.2,4.8,5.2,5.7,6,6.1,6,6.1,6.5,7.1,8.5,8.9,9,8.6,9.1,9.7,9.7,9.6,9.5,9.5,9.4,9.7,10.6,10.4]} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment