2011-04-07 20:54:03 +04:00
|
|
|
(function(){d3.chart = {};
|
2011-04-13 23:13:58 +04:00
|
|
|
// Inspired by http://informationandvisualization.de/blog/box-plot
|
2011-04-14 01:31:53 +04:00
|
|
|
d3.chart.box = function() {
|
2011-04-13 21:53:52 +04:00
|
|
|
var width = 1,
|
|
|
|
height = 1,
|
|
|
|
duration = 0,
|
|
|
|
domain = null,
|
2011-04-13 23:39:12 +04:00
|
|
|
value = Number,
|
2011-04-14 01:31:53 +04:00
|
|
|
whiskers = d3_chart_boxWhiskers,
|
|
|
|
quartiles = d3_chart_boxQuartiles,
|
2011-04-13 21:53:52 +04:00
|
|
|
tickFormat = null;
|
|
|
|
|
|
|
|
// For each small multiple…
|
2011-04-14 01:31:53 +04:00
|
|
|
function box(g) {
|
2011-04-13 21:53:52 +04:00
|
|
|
g.each(function(d, i) {
|
2011-04-13 23:39:12 +04:00
|
|
|
d = d.map(value).sort(d3.ascending);
|
2011-04-14 04:31:16 +04:00
|
|
|
var g = d3.select(this),
|
|
|
|
n = d.length,
|
2011-04-14 00:59:37 +04:00
|
|
|
min = d[0],
|
2011-04-14 04:31:16 +04:00
|
|
|
max = d[n - 1];
|
|
|
|
|
|
|
|
// Compute quartiles. Must return exactly 3 elements.
|
|
|
|
var quartileData = d.quartiles = quartiles(d);
|
|
|
|
|
|
|
|
// Compute whiskers. Must return exactly 2 elements, or null.
|
|
|
|
var whiskerIndices = whiskers && whiskers.call(this, d, i),
|
|
|
|
whiskerData = whiskerIndices && whiskerIndices.map(function(i) { return d[i]; });
|
|
|
|
|
|
|
|
// Compute outliers. If no whiskers are specified, all data are "outliers".
|
|
|
|
// We compute the outliers as indices, so that we can join across transitions!
|
|
|
|
var outlierIndices = whiskerIndices
|
|
|
|
? d3.range(0, whiskerIndices[0]).concat(d3.range(whiskerIndices[1] + 1, n))
|
|
|
|
: d3.range(n);
|
2011-04-13 21:53:52 +04:00
|
|
|
|
|
|
|
// Compute the new x-scale.
|
|
|
|
var x1 = d3.scale.linear()
|
2011-04-14 04:31:16 +04:00
|
|
|
.domain(domain && domain.call(this, d, i) || [min, max])
|
2011-04-13 21:53:52 +04:00
|
|
|
.range([height, 0]);
|
|
|
|
|
|
|
|
// Retrieve the old x-scale, if this is an update.
|
|
|
|
var x0 = this.__chart__ || d3.scale.linear()
|
|
|
|
.domain([0, Infinity])
|
|
|
|
.range(x1.range());
|
|
|
|
|
|
|
|
// Stash the new scale.
|
|
|
|
this.__chart__ = x1;
|
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
// Note: the box, median, and box tick elements are fixed in number,
|
|
|
|
// so we only have to handle enter and update. In contrast, the outliers
|
|
|
|
// and other elements are variable, so we need to exit them! Variable
|
|
|
|
// elements also fade in and out.
|
|
|
|
|
|
|
|
// Update center line: the vertical line spanning the whiskers.
|
2011-04-13 21:53:52 +04:00
|
|
|
var center = g.selectAll("line.center")
|
2011-04-14 04:31:16 +04:00
|
|
|
.data(whiskerData ? [whiskerData] : []);
|
2011-04-13 21:53:52 +04:00
|
|
|
|
2011-04-14 10:28:04 +04:00
|
|
|
center.enter().insert("svg:line", "rect")
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("class", "center")
|
|
|
|
.attr("x1", width / 2)
|
|
|
|
.attr("y1", function(d) { return x0(d[0]); })
|
|
|
|
.attr("x2", width / 2)
|
|
|
|
.attr("y2", function(d) { return x0(d[1]); })
|
2011-04-14 04:31:16 +04:00
|
|
|
.style("opacity", 1e-6)
|
2011-04-13 21:53:52 +04:00
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
2011-04-14 04:31:16 +04:00
|
|
|
.style("opacity", 1)
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("y1", function(d) { return x1(d[0]); })
|
|
|
|
.attr("y2", function(d) { return x1(d[1]); });
|
|
|
|
|
|
|
|
center.transition()
|
|
|
|
.duration(duration)
|
2011-04-14 04:31:16 +04:00
|
|
|
.style("opacity", 1)
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("y1", function(d) { return x1(d[0]); })
|
|
|
|
.attr("y2", function(d) { return x1(d[1]); });
|
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
center.exit().transition()
|
|
|
|
.duration(duration)
|
|
|
|
.style("opacity", 1e-6)
|
|
|
|
.attr("y1", function(d) { return x1(d[0]); })
|
|
|
|
.attr("y2", function(d) { return x1(d[1]); })
|
|
|
|
.remove();
|
2011-04-13 21:53:52 +04:00
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
// Update innerquartile box.
|
2011-04-13 21:53:52 +04:00
|
|
|
var box = g.selectAll("rect.box")
|
2011-04-14 04:31:16 +04:00
|
|
|
.data([quartileData]);
|
2011-04-13 21:53:52 +04:00
|
|
|
|
|
|
|
box.enter().append("svg:rect")
|
|
|
|
.attr("class", "box")
|
|
|
|
.attr("x", 0)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("y", function(d) { return x0(d[2]); })
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("width", width)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("height", function(d) { return x0(d[0]) - x0(d[2]); })
|
2011-04-13 21:53:52 +04:00
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("y", function(d) { return x1(d[2]); })
|
|
|
|
.attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
|
2011-04-13 21:53:52 +04:00
|
|
|
|
|
|
|
box.transition()
|
|
|
|
.duration(duration)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("y", function(d) { return x1(d[2]); })
|
|
|
|
.attr("height", function(d) { return x1(d[0]) - x1(d[2]); });
|
2011-04-13 21:53:52 +04:00
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
// Update median line.
|
2011-04-14 00:09:31 +04:00
|
|
|
var medianLine = g.selectAll("line.median")
|
2011-04-14 01:21:29 +04:00
|
|
|
.data([quartileData[1]]);
|
2011-04-13 21:53:52 +04:00
|
|
|
|
2011-04-14 00:09:31 +04:00
|
|
|
medianLine.enter().append("svg:line")
|
|
|
|
.attr("class", "median")
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("x1", 0)
|
|
|
|
.attr("y1", x0)
|
|
|
|
.attr("x2", width)
|
|
|
|
.attr("y2", x0)
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("y1", x1)
|
|
|
|
.attr("y2", x1);
|
|
|
|
|
2011-04-14 00:09:31 +04:00
|
|
|
medianLine.transition()
|
2011-04-13 21:53:52 +04:00
|
|
|
.duration(duration)
|
|
|
|
.attr("y1", x1)
|
|
|
|
.attr("y2", x1);
|
|
|
|
|
2011-04-14 00:09:31 +04:00
|
|
|
// Update whiskers.
|
|
|
|
var whisker = g.selectAll("line.whisker")
|
2011-04-14 04:31:16 +04:00
|
|
|
.data(whiskerData || []);
|
2011-04-14 00:09:31 +04:00
|
|
|
|
2011-04-14 10:28:04 +04:00
|
|
|
whisker.enter().insert("svg:line", "circle, text")
|
2011-04-14 00:09:31 +04:00
|
|
|
.attr("class", "whisker")
|
|
|
|
.attr("x1", 0)
|
|
|
|
.attr("y1", x0)
|
|
|
|
.attr("x2", width)
|
|
|
|
.attr("y2", x0)
|
2011-04-14 04:31:16 +04:00
|
|
|
.style("opacity", 1e-6)
|
2011-04-14 00:09:31 +04:00
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("y1", x1)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("y2", x1)
|
|
|
|
.style("opacity", 1);
|
2011-04-14 00:09:31 +04:00
|
|
|
|
|
|
|
whisker.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("y1", x1)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("y2", x1)
|
|
|
|
.style("opacity", 1);
|
2011-04-14 00:09:31 +04:00
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
whisker.exit().transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("y1", x1)
|
|
|
|
.attr("y2", x1)
|
|
|
|
.style("opacity", 1e-6)
|
|
|
|
.remove();
|
2011-04-13 21:53:52 +04:00
|
|
|
|
2011-04-14 00:59:37 +04:00
|
|
|
// Update outliers.
|
2011-04-14 01:27:25 +04:00
|
|
|
var outlier = g.selectAll("circle.outlier")
|
2011-04-14 04:31:16 +04:00
|
|
|
.data(outlierIndices, Number);
|
2011-04-14 00:59:37 +04:00
|
|
|
|
2011-04-14 10:28:04 +04:00
|
|
|
outlier.enter().insert("svg:circle", "text")
|
2011-04-14 00:59:37 +04:00
|
|
|
.attr("class", "outlier")
|
2011-04-14 01:27:25 +04:00
|
|
|
.attr("r", 5)
|
|
|
|
.attr("cx", width / 2)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("cy", function(i) { return x0(d[i]); })
|
|
|
|
.style("opacity", 1e-6)
|
2011-04-14 00:59:37 +04:00
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("cy", function(i) { return x1(d[i]); })
|
|
|
|
.style("opacity", 1);
|
2011-04-14 00:59:37 +04:00
|
|
|
|
|
|
|
outlier.transition()
|
|
|
|
.duration(duration)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("cy", function(i) { return x1(d[i]); })
|
|
|
|
.style("opacity", 1);
|
2011-04-14 00:59:37 +04:00
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
outlier.exit().transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("cy", function(i) { return x1(d[i]); })
|
|
|
|
.style("opacity", 1e-6)
|
|
|
|
.remove();
|
2011-04-14 00:59:37 +04:00
|
|
|
|
2011-04-13 21:53:52 +04:00
|
|
|
// Compute the tick format.
|
|
|
|
var format = tickFormat || x1.tickFormat(8);
|
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
// Update box ticks.
|
|
|
|
var boxTick = g.selectAll("text.box")
|
|
|
|
.data(quartileData);
|
2011-04-13 21:53:52 +04:00
|
|
|
|
2011-04-14 04:31:16 +04:00
|
|
|
boxTick.enter().append("svg:text")
|
|
|
|
.attr("class", "box")
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("dy", ".3em")
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("dx", function(d, i) { return i & 1 ? 6 : -6 })
|
|
|
|
.attr("x", function(d, i) { return i & 1 ? width : 0 })
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("y", x0)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("text-anchor", function(d, i) { return i & 1 ? "start" : "end"; })
|
2011-04-13 21:53:52 +04:00
|
|
|
.text(format)
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("y", x1);
|
|
|
|
|
2011-04-14 05:50:26 +04:00
|
|
|
boxTick.transition()
|
2011-04-13 21:53:52 +04:00
|
|
|
.duration(duration)
|
2011-04-14 05:50:26 +04:00
|
|
|
.text(format)
|
2011-04-13 21:53:52 +04:00
|
|
|
.attr("y", x1);
|
2011-04-14 04:31:16 +04:00
|
|
|
|
|
|
|
// Update whisker ticks. These are handled separately from the box
|
|
|
|
// ticks because they may or may not exist, and we want don't want
|
|
|
|
// to join box ticks pre-transition with whisker ticks post-.
|
|
|
|
var whiskerTick = g.selectAll("text.whisker")
|
|
|
|
.data(whiskerData || []);
|
|
|
|
|
|
|
|
whiskerTick.enter().append("svg:text")
|
|
|
|
.attr("class", "whisker")
|
|
|
|
.attr("dy", ".3em")
|
|
|
|
.attr("dx", 6)
|
|
|
|
.attr("x", width)
|
|
|
|
.attr("y", x0)
|
|
|
|
.text(format)
|
|
|
|
.style("opacity", 1e-6)
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("y", x1)
|
|
|
|
.style("opacity", 1);
|
|
|
|
|
2011-04-14 05:50:26 +04:00
|
|
|
whiskerTick.transition()
|
2011-04-14 04:31:16 +04:00
|
|
|
.duration(duration)
|
2011-04-14 05:50:26 +04:00
|
|
|
.text(format)
|
2011-04-14 04:31:16 +04:00
|
|
|
.attr("y", x1)
|
|
|
|
.style("opacity", 1);
|
|
|
|
|
|
|
|
whiskerTick.exit().transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("y", x1)
|
|
|
|
.style("opacity", 1e-6)
|
|
|
|
.remove();
|
2011-04-13 21:53:52 +04:00
|
|
|
});
|
2011-05-01 08:52:14 +04:00
|
|
|
d3.timer.flush();
|
2011-04-13 21:53:52 +04:00
|
|
|
}
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.width = function(x) {
|
2011-04-13 21:53:52 +04:00
|
|
|
if (!arguments.length) return width;
|
|
|
|
width = x;
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-13 21:53:52 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.height = function(x) {
|
2011-04-13 21:53:52 +04:00
|
|
|
if (!arguments.length) return height;
|
|
|
|
height = x;
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-13 21:53:52 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.tickFormat = function(x) {
|
2011-04-13 21:53:52 +04:00
|
|
|
if (!arguments.length) return tickFormat;
|
|
|
|
tickFormat = x;
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-13 21:53:52 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.duration = function(x) {
|
2011-04-13 21:53:52 +04:00
|
|
|
if (!arguments.length) return duration;
|
|
|
|
duration = x;
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-13 21:53:52 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.domain = function(x) {
|
2011-04-13 21:53:52 +04:00
|
|
|
if (!arguments.length) return domain;
|
2011-04-14 04:31:16 +04:00
|
|
|
domain = x == null ? x : d3.functor(x);
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-13 21:53:52 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.value = function(x) {
|
2011-04-13 23:39:12 +04:00
|
|
|
if (!arguments.length) return value;
|
|
|
|
value = x;
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-13 23:13:58 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.whiskers = function(x) {
|
2011-04-14 00:59:37 +04:00
|
|
|
if (!arguments.length) return whiskers;
|
2011-04-14 00:09:31 +04:00
|
|
|
whiskers = x;
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-14 00:09:31 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
box.quartiles = function(x) {
|
2011-04-14 01:21:29 +04:00
|
|
|
if (!arguments.length) return quartiles;
|
|
|
|
quartiles = x;
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-14 01:21:29 +04:00
|
|
|
};
|
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
return box;
|
2011-04-13 21:53:52 +04:00
|
|
|
};
|
2011-04-14 00:09:31 +04:00
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
function d3_chart_boxWhiskers(d) {
|
2011-04-14 04:31:16 +04:00
|
|
|
return [0, d.length - 1];
|
2011-04-14 00:09:31 +04:00
|
|
|
}
|
2011-04-14 01:21:29 +04:00
|
|
|
|
2011-04-14 01:31:53 +04:00
|
|
|
function d3_chart_boxQuartiles(d) {
|
2011-07-10 03:03:07 +04:00
|
|
|
return [
|
|
|
|
d3.quantile(d, .25),
|
|
|
|
d3.quantile(d, .5),
|
|
|
|
d3.quantile(d, .75)
|
|
|
|
];
|
2011-04-14 01:21:29 +04:00
|
|
|
}
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
// Chart design based on the recommendations of Stephen Few. Implementation
|
|
|
|
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
|
|
|
|
// http://projects.instantcognition.com/protovis/bulletchart/
|
2011-04-07 20:54:03 +04:00
|
|
|
d3.chart.bullet = function() {
|
2011-04-09 02:35:26 +04:00
|
|
|
var orient = "left", // TODO top & bottom
|
2011-04-09 21:43:09 +04:00
|
|
|
reverse = false,
|
2011-04-08 01:36:25 +04:00
|
|
|
duration = 0,
|
2011-04-08 03:57:33 +04:00
|
|
|
ranges = d3_chart_bulletRanges,
|
|
|
|
markers = d3_chart_bulletMarkers,
|
|
|
|
measures = d3_chart_bulletMeasures,
|
2011-04-08 14:24:51 +04:00
|
|
|
width = 380,
|
2011-04-06 23:49:15 +04:00
|
|
|
height = 30,
|
2011-04-09 02:35:26 +04:00
|
|
|
tickFormat = null;
|
2011-04-08 03:57:33 +04:00
|
|
|
|
2011-04-09 21:43:09 +04:00
|
|
|
// For each small multiple…
|
2011-04-08 14:24:51 +04:00
|
|
|
function bullet(g) {
|
2011-04-09 21:43:09 +04:00
|
|
|
g.each(function(d, i) {
|
|
|
|
var rangez = ranges.call(this, d, i).slice().sort(d3.descending),
|
|
|
|
markerz = markers.call(this, d, i).slice().sort(d3.descending),
|
|
|
|
measurez = measures.call(this, d, i).slice().sort(d3.descending),
|
|
|
|
g = d3.select(this);
|
|
|
|
|
|
|
|
// Compute the new x-scale.
|
|
|
|
var x1 = d3.scale.linear()
|
|
|
|
.domain([0, Math.max(rangez[0], markerz[0], measurez[0])])
|
2011-04-09 19:41:26 +04:00
|
|
|
.range(reverse ? [width, 0] : [0, width]);
|
2011-04-09 01:40:29 +04:00
|
|
|
|
2011-04-09 21:43:09 +04:00
|
|
|
// Retrieve the old x-scale, if this is an update.
|
|
|
|
var x0 = this.__chart__ || d3.scale.linear()
|
|
|
|
.domain([0, Infinity])
|
|
|
|
.range(x1.range());
|
|
|
|
|
|
|
|
// Stash the new scale.
|
|
|
|
this.__chart__ = x1;
|
|
|
|
|
|
|
|
// Derive width-scales from the x-scales.
|
|
|
|
var w0 = d3_chart_bulletWidth(x0),
|
|
|
|
w1 = d3_chart_bulletWidth(x1);
|
|
|
|
|
|
|
|
// Update the range rects.
|
|
|
|
var range = g.selectAll("rect.range")
|
|
|
|
.data(rangez);
|
|
|
|
|
|
|
|
range.enter().append("svg:rect")
|
|
|
|
.attr("class", function(d, i) { return "range s" + i; })
|
|
|
|
.attr("width", w0)
|
|
|
|
.attr("height", height)
|
|
|
|
.attr("x", reverse ? x0 : 0)
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("width", w1)
|
|
|
|
.attr("x", reverse ? x1 : 0);
|
|
|
|
|
|
|
|
range.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("x", reverse ? x1 : 0)
|
|
|
|
.attr("width", w1)
|
|
|
|
.attr("height", height);
|
|
|
|
|
|
|
|
// Update the measure rects.
|
|
|
|
var measure = g.selectAll("rect.measure")
|
|
|
|
.data(measurez);
|
|
|
|
|
|
|
|
measure.enter().append("svg:rect")
|
|
|
|
.attr("class", function(d, i) { return "measure s" + i; })
|
|
|
|
.attr("width", w0)
|
|
|
|
.attr("height", height / 3)
|
|
|
|
.attr("x", reverse ? x0 : 0)
|
|
|
|
.attr("y", height / 3)
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("width", w1)
|
|
|
|
.attr("x", reverse ? x1 : 0);
|
|
|
|
|
|
|
|
measure.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("width", w1)
|
|
|
|
.attr("height", height / 3)
|
|
|
|
.attr("x", reverse ? x1 : 0)
|
|
|
|
.attr("y", height / 3);
|
|
|
|
|
|
|
|
// Update the marker lines.
|
|
|
|
var marker = g.selectAll("line.marker")
|
|
|
|
.data(markerz);
|
|
|
|
|
|
|
|
marker.enter().append("svg:line")
|
|
|
|
.attr("class", "marker")
|
|
|
|
.attr("x1", x0)
|
|
|
|
.attr("x2", x0)
|
|
|
|
.attr("y1", height / 6)
|
|
|
|
.attr("y2", height * 5 / 6)
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("x1", x1)
|
|
|
|
.attr("x2", x1);
|
|
|
|
|
|
|
|
marker.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("x1", x1)
|
|
|
|
.attr("x2", x1)
|
|
|
|
.attr("y1", height / 6)
|
|
|
|
.attr("y2", height * 5 / 6);
|
|
|
|
|
|
|
|
// Compute the tick format.
|
|
|
|
var format = tickFormat || x1.tickFormat(8);
|
|
|
|
|
|
|
|
// Update the tick groups.
|
|
|
|
var tick = g.selectAll("g.tick")
|
2011-04-10 00:46:25 +04:00
|
|
|
.data(x1.ticks(8), function(d) {
|
|
|
|
return this.textContent || format(d);
|
|
|
|
});
|
2011-04-09 21:43:09 +04:00
|
|
|
|
|
|
|
// Initialize the ticks with the old scale, x0.
|
|
|
|
var tickEnter = tick.enter().append("svg:g")
|
|
|
|
.attr("class", "tick")
|
|
|
|
.attr("transform", d3_chart_bulletTranslate(x0))
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1e-6);
|
2011-04-09 21:43:09 +04:00
|
|
|
|
|
|
|
tickEnter.append("svg:line")
|
|
|
|
.attr("y1", height)
|
|
|
|
.attr("y2", height * 7 / 6);
|
|
|
|
|
|
|
|
tickEnter.append("svg:text")
|
|
|
|
.attr("text-anchor", "middle")
|
|
|
|
.attr("dy", "1em")
|
|
|
|
.attr("y", height * 7 / 6)
|
|
|
|
.text(format);
|
|
|
|
|
|
|
|
// Transition the entering ticks to the new scale, x1.
|
|
|
|
tickEnter.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("transform", d3_chart_bulletTranslate(x1))
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1);
|
2011-04-09 21:43:09 +04:00
|
|
|
|
|
|
|
// Transition the updating ticks to the new scale, x1.
|
|
|
|
var tickUpdate = tick.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("transform", d3_chart_bulletTranslate(x1))
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1);
|
2011-04-09 21:43:09 +04:00
|
|
|
|
|
|
|
tickUpdate.select("line")
|
|
|
|
.attr("y1", height)
|
|
|
|
.attr("y2", height * 7 / 6);
|
|
|
|
|
|
|
|
tickUpdate.select("text")
|
|
|
|
.attr("y", height * 7 / 6);
|
|
|
|
|
|
|
|
// Transition the exiting ticks to the new scale, x1.
|
|
|
|
tick.exit().transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("transform", d3_chart_bulletTranslate(x1))
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1e-6)
|
2011-04-09 21:43:09 +04:00
|
|
|
.remove();
|
|
|
|
});
|
2011-05-01 08:52:14 +04:00
|
|
|
d3.timer.flush();
|
2011-04-07 21:21:11 +04:00
|
|
|
}
|
|
|
|
|
2011-04-06 22:47:55 +04:00
|
|
|
// left, right, top, bottom
|
|
|
|
bullet.orient = function(x) {
|
|
|
|
if (!arguments.length) return orient;
|
|
|
|
orient = x;
|
2011-04-09 21:43:09 +04:00
|
|
|
reverse = orient == "right" || orient == "bottom";
|
2011-04-06 22:47:55 +04:00
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
// ranges (bad, satisfactory, good)
|
2011-04-06 22:47:55 +04:00
|
|
|
bullet.ranges = function(x) {
|
|
|
|
if (!arguments.length) return ranges;
|
|
|
|
ranges = x;
|
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
// markers (previous, goal)
|
2011-04-06 22:47:55 +04:00
|
|
|
bullet.markers = function(x) {
|
|
|
|
if (!arguments.length) return markers;
|
|
|
|
markers = x;
|
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
// measures (actual, forecast)
|
2011-04-06 22:47:55 +04:00
|
|
|
bullet.measures = function(x) {
|
|
|
|
if (!arguments.length) return measures;
|
|
|
|
measures = x;
|
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
2011-04-06 23:49:15 +04:00
|
|
|
bullet.width = function(x) {
|
|
|
|
if (!arguments.length) return width;
|
2011-04-09 01:40:29 +04:00
|
|
|
width = x;
|
2011-04-06 23:49:15 +04:00
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
|
|
|
bullet.height = function(x) {
|
|
|
|
if (!arguments.length) return height;
|
|
|
|
height = x;
|
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
|
|
|
bullet.tickFormat = function(x) {
|
|
|
|
if (!arguments.length) return tickFormat;
|
|
|
|
tickFormat = x;
|
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
2011-04-08 01:36:25 +04:00
|
|
|
bullet.duration = function(x) {
|
|
|
|
if (!arguments.length) return duration;
|
|
|
|
duration = x;
|
|
|
|
return bullet;
|
|
|
|
};
|
|
|
|
|
2011-04-06 22:47:55 +04:00
|
|
|
return bullet;
|
|
|
|
};
|
2011-04-08 03:57:33 +04:00
|
|
|
|
|
|
|
function d3_chart_bulletRanges(d) {
|
|
|
|
return d.ranges;
|
|
|
|
}
|
|
|
|
|
|
|
|
function d3_chart_bulletMarkers(d) {
|
|
|
|
return d.markers;
|
|
|
|
}
|
|
|
|
|
|
|
|
function d3_chart_bulletMeasures(d) {
|
|
|
|
return d.measures;
|
|
|
|
}
|
|
|
|
|
2011-04-09 01:40:29 +04:00
|
|
|
function d3_chart_bulletTranslate(x) {
|
|
|
|
return function(d) {
|
|
|
|
return "translate(" + x(d) + ",0)";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function d3_chart_bulletWidth(x) {
|
|
|
|
var x0 = x(0);
|
|
|
|
return function(d) {
|
|
|
|
return Math.abs(x(d) - x0);
|
|
|
|
};
|
|
|
|
}
|
2011-05-21 12:10:16 +04:00
|
|
|
// Implements a horizon layout, which is a variation of a single-series
|
|
|
|
// area chart where the area is folded into multiple bands. Color is used to
|
|
|
|
// encode band, allowing the size of the chart to be reduced significantly
|
|
|
|
// without impeding readability. This layout algorithm is based on the work of
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
// J. Heer, N. Kong and M. Agrawala in "Sizing the Horizon: The Effects of Chart
|
|
|
|
// Size and Layering on the Graphical Perception of Time Series Visualizations",
|
|
|
|
// CHI 2009. http://hci.stanford.edu/publications/2009/heer-horizon-chi09.pdf
|
2011-05-21 12:10:16 +04:00
|
|
|
d3.chart.horizon = function() {
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
var bands = 1, // between 1 and 5, typically
|
|
|
|
mode = "offset", // or mirror
|
|
|
|
interpolate = "linear", // or basis, monotone, step-before, etc.
|
|
|
|
x = d3_chart_horizonX,
|
|
|
|
y = d3_chart_horizonY,
|
|
|
|
w = 960,
|
|
|
|
h = 40,
|
2011-05-21 12:10:16 +04:00
|
|
|
duration = 0;
|
|
|
|
|
2011-05-26 11:10:14 +04:00
|
|
|
var color = d3.scale.linear()
|
|
|
|
.domain([-1, 0, 1])
|
|
|
|
.range(["#d62728", "#fff", "#1f77b4"]);
|
|
|
|
|
2011-05-21 12:10:16 +04:00
|
|
|
// For each small multiple…
|
|
|
|
function horizon(g) {
|
|
|
|
g.each(function(d, i) {
|
|
|
|
var g = d3.select(this),
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
n = 2 * bands + 1,
|
|
|
|
xMin = Infinity,
|
|
|
|
xMax = -Infinity,
|
|
|
|
yMax = -Infinity,
|
2011-05-21 12:10:16 +04:00
|
|
|
x0, // old x-scale
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
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];
|
2011-05-21 12:10:16 +04:00
|
|
|
});
|
|
|
|
|
|
|
|
// Compute the new x- and y-scales.
|
|
|
|
var x1 = d3.scale.linear().domain([xMin, xMax]).range([0, w]),
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
y1 = d3.scale.linear().domain([0, yMax]).range([0, h * bands]);
|
2011-05-21 12:10:16 +04:00
|
|
|
|
|
|
|
// Retrieve the old scales, if this is an update.
|
|
|
|
if (this.__chart__) {
|
|
|
|
x0 = this.__chart__.x;
|
|
|
|
y0 = this.__chart__.y;
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
id = this.__chart__.id;
|
2011-05-21 12:10:16 +04:00
|
|
|
} else {
|
|
|
|
x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
|
|
|
|
y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
id = ++d3_chart_horizonId;
|
2011-05-21 12:10:16 +04:00
|
|
|
}
|
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
// We'll use a defs to store the area path and the clip path.
|
|
|
|
var defs = g.selectAll("defs")
|
|
|
|
.data([data]);
|
|
|
|
|
|
|
|
var defsEnter = defs.enter().append("svg:defs");
|
|
|
|
|
|
|
|
// The clip path is a simple rect.
|
|
|
|
defsEnter.append("svg:clipPath")
|
|
|
|
.attr("id", "d3_chart_horizon_clip" + id)
|
|
|
|
.append("svg:rect")
|
|
|
|
.attr("width", w)
|
|
|
|
.attr("height", h);
|
2011-05-21 12:10:16 +04:00
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
defs.select("rect").transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("width", w)
|
|
|
|
.attr("height", h);
|
|
|
|
|
|
|
|
// The area path is rendered with our resuable d3.svg.area.
|
|
|
|
defsEnter.append("svg:path")
|
|
|
|
.attr("id", "d3_chart_horizon_path" + id)
|
|
|
|
.attr("d", d3_chart_horizonArea
|
|
|
|
.interpolate(interpolate)
|
2011-05-21 12:10:16 +04:00
|
|
|
.x(function(d) { return x0(d[0]); })
|
|
|
|
.y0(h * bands)
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
.y1(function(d) { return h * bands - y0(d[1]); }))
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("d", d3_chart_horizonArea
|
2011-05-21 12:10:16 +04:00
|
|
|
.x(function(d) { return x1(d[0]); })
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
.y1(function(d) { return h * bands - y1(d[1]); }));
|
2011-05-21 12:10:16 +04:00
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
defs.select("path").transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("d", d3_chart_horizonArea);
|
|
|
|
|
|
|
|
// We'll use a container to clip all horizon layers at once.
|
|
|
|
g.selectAll("g")
|
|
|
|
.data([null])
|
|
|
|
.enter().append("svg:g")
|
|
|
|
.attr("clip-path", "url(#d3_chart_horizon_clip" + id + ")");
|
|
|
|
|
|
|
|
// Define the transform function based on the mode.
|
|
|
|
var transform = 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 + ")"; };
|
|
|
|
|
|
|
|
// Instantiate each copy of the path with different transforms.
|
|
|
|
var u = g.select("g").selectAll("use")
|
2011-05-26 11:10:14 +04:00
|
|
|
.data(d3.range(-1, -bands - 1, -1).concat(d3.range(1, bands + 1)), Number);
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
|
2011-05-26 11:10:14 +04:00
|
|
|
// TODO don't fudge the enter transition
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
u.enter().append("svg:use")
|
|
|
|
.attr("xlink:href", "#d3_chart_horizon_path" + id)
|
2011-05-26 11:10:14 +04:00
|
|
|
.attr("transform", function(d) { return transform(d + (d > 0 ? 1 : -1)); })
|
|
|
|
.style("fill", color)
|
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
.attr("transform", transform);
|
|
|
|
|
|
|
|
u.transition()
|
|
|
|
.duration(duration)
|
2011-05-26 11:10:14 +04:00
|
|
|
.attr("transform", transform)
|
|
|
|
.style("fill", color);
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
|
2011-05-26 11:10:14 +04:00
|
|
|
u.exit().transition()
|
|
|
|
.duration(duration)
|
|
|
|
.attr("transform", transform)
|
|
|
|
.remove();
|
|
|
|
|
|
|
|
// Stash the new scales.
|
|
|
|
this.__chart__ = {x: x1, y: y1, id: id};
|
2011-05-21 12:10:16 +04:00
|
|
|
});
|
|
|
|
d3.timer.flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
horizon.duration = function(x) {
|
|
|
|
if (!arguments.length) return duration;
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
duration = +x;
|
2011-05-21 12:10:16 +04:00
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
|
|
|
horizon.bands = function(x) {
|
|
|
|
if (!arguments.length) return bands;
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
bands = +x;
|
2011-05-26 11:10:14 +04:00
|
|
|
color.domain([-bands, 0, bands]);
|
2011-05-21 12:10:16 +04:00
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
|
|
|
horizon.mode = function(x) {
|
|
|
|
if (!arguments.length) return mode;
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
mode = x + "";
|
2011-05-21 12:10:16 +04:00
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
2011-05-26 11:10:14 +04:00
|
|
|
horizon.colors = function(x) {
|
|
|
|
if (!arguments.length) return color.range();
|
|
|
|
color.range(x);
|
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
horizon.interpolate = function(x) {
|
|
|
|
if (!arguments.length) return interpolate;
|
|
|
|
interpolate = x + "";
|
2011-05-21 12:10:16 +04:00
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
horizon.x = function(z) {
|
|
|
|
if (!arguments.length) return x;
|
|
|
|
x = z;
|
2011-05-21 12:10:16 +04:00
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
horizon.y = function(z) {
|
|
|
|
if (!arguments.length) return y;
|
|
|
|
y = z;
|
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
|
|
|
horizon.width = function(x) {
|
2011-07-16 21:16:41 +04:00
|
|
|
if (!arguments.length) return w;
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
w = +x;
|
2011-05-21 12:10:16 +04:00
|
|
|
return horizon;
|
|
|
|
};
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
|
|
|
|
horizon.height = function(x) {
|
2011-07-16 21:16:41 +04:00
|
|
|
if (!arguments.length) return h;
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
h = +x;
|
|
|
|
return horizon;
|
|
|
|
};
|
|
|
|
|
2011-05-21 12:10:16 +04:00
|
|
|
return horizon;
|
|
|
|
};
|
2011-05-22 00:29:43 +04:00
|
|
|
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
var d3_chart_horizonArea = d3.svg.area(),
|
|
|
|
d3_chart_horizonId = 0;
|
|
|
|
|
2011-05-22 00:29:43 +04:00
|
|
|
function d3_chart_horizonX(d) {
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
return d[0];
|
2011-05-22 00:29:43 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
function d3_chart_horizonY(d) {
|
Horizon chart improvements.
Use width and height attributes instead of size, for consistency with other
chart templates. (Though, note that chart templates are inconsistent with
layouts in this regard, which use a size property. But let's remain locally
consistent for now.) Default x and y properties to [0] and [1] to match the
defaults of d3.svg.area.
Add support for mode and interpolate properties. The mode can be either "offset"
or "mirror", with the default being "offset". The interpolate property is the
same as that used by d3.svg.area, and defaults to "linear".
The horizon chart now properly clips the horizon layers, such that if a horizon
chart is use within a larger SVG element, it will not extend the chart bounds.
In addition, this commit fixes a bug in how unique IDs are assigned to the clip
and area paths; it's a shame that SVG does not support a way to refer to paths
locally.
Note that because negative values are offset or mirrored, the horizon chart will
render twice as many paths (use elements) as the requested number of bands. For
example, if the default bands of 1 is used, there will be one negative band and
one positive band. In the data has no negative values, then the negative band
will be empty. Keep this in mind as it affects the layer's class attribute
(such as "q0-3" and "q2-3" for the default single-band horizon).
2011-05-22 22:26:46 +04:00
|
|
|
return d[1];
|
2011-05-22 00:29:43 +04:00
|
|
|
}
|
2011-04-24 13:18:03 +04:00
|
|
|
// Based on http://vis.stanford.edu/protovis/ex/qqplot.html
|
|
|
|
d3.chart.qq = function() {
|
|
|
|
var width = 1,
|
|
|
|
height = 1,
|
|
|
|
duration = 0,
|
|
|
|
domain = null,
|
2011-04-25 15:59:11 +04:00
|
|
|
tickFormat = null,
|
2011-05-01 08:16:58 +04:00
|
|
|
n = 100,
|
|
|
|
x = d3_chart_qqX,
|
|
|
|
y = d3_chart_qqY;
|
2011-04-24 13:18:03 +04:00
|
|
|
|
|
|
|
// For each small multiple…
|
|
|
|
function qq(g) {
|
|
|
|
g.each(function(d, i) {
|
|
|
|
var g = d3.select(this),
|
2011-05-01 08:16:58 +04:00
|
|
|
qx = d3_chart_qqQuantiles(n, x.call(this, d, i)),
|
|
|
|
qy = d3_chart_qqQuantiles(n, y.call(this, d, i)),
|
|
|
|
xd = domain && domain.call(this, d, i) || [d3.min(qx), d3.max(qx)], // new x-domain
|
|
|
|
yd = domain && domain.call(this, d, i) || [d3.min(qy), d3.max(qy)], // new y-domain
|
|
|
|
x0, // old x-scale
|
|
|
|
y0; // old y-scale
|
2011-04-24 13:18:03 +04:00
|
|
|
|
|
|
|
// Compute the new x-scale.
|
|
|
|
var x1 = d3.scale.linear()
|
2011-05-01 08:16:58 +04:00
|
|
|
.domain(xd)
|
2011-04-24 13:18:03 +04:00
|
|
|
.range([0, width]);
|
2011-05-01 08:16:58 +04:00
|
|
|
|
|
|
|
// Compute the new y-scale.
|
2011-04-24 13:18:03 +04:00
|
|
|
var y1 = d3.scale.linear()
|
2011-05-01 08:16:58 +04:00
|
|
|
.domain(yd)
|
2011-04-24 13:18:03 +04:00
|
|
|
.range([height, 0]);
|
|
|
|
|
|
|
|
// Retrieve the old scales, if this is an update.
|
|
|
|
if (this.__chart__) {
|
|
|
|
x0 = this.__chart__.x;
|
|
|
|
y0 = this.__chart__.y;
|
|
|
|
} else {
|
2011-05-01 08:16:58 +04:00
|
|
|
x0 = d3.scale.linear().domain([0, Infinity]).range(x1.range());
|
|
|
|
y0 = d3.scale.linear().domain([0, Infinity]).range(y1.range());
|
2011-04-24 13:18:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Stash the new scales.
|
|
|
|
this.__chart__ = {x: x1, y: y1};
|
|
|
|
|
|
|
|
// Update diagonal line.
|
|
|
|
var diagonal = g.selectAll("line.diagonal")
|
2011-05-01 08:16:58 +04:00
|
|
|
.data([null]);
|
2011-04-24 13:18:03 +04:00
|
|
|
|
|
|
|
diagonal.enter().append("svg:line")
|
|
|
|
.attr("class", "diagonal")
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("x1", x1(yd[0]))
|
|
|
|
.attr("y1", y1(xd[0]))
|
|
|
|
.attr("x2", x1(yd[1]))
|
|
|
|
.attr("y2", y1(xd[1]));
|
2011-04-24 13:18:03 +04:00
|
|
|
|
|
|
|
diagonal.transition()
|
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("x1", x1(yd[0]))
|
|
|
|
.attr("y1", y1(xd[0]))
|
|
|
|
.attr("x2", x1(yd[1]))
|
|
|
|
.attr("y2", y1(xd[1]));
|
2011-04-24 13:18:03 +04:00
|
|
|
|
|
|
|
// Update quantile plots.
|
2011-05-01 08:16:58 +04:00
|
|
|
var circle = g.selectAll("circle")
|
|
|
|
.data(d3.range(n).map(function(i) {
|
|
|
|
return {x: qx[i], y: qy[i]};
|
|
|
|
}));
|
|
|
|
|
|
|
|
circle.enter().append("svg:circle")
|
|
|
|
.attr("class", "quantile")
|
|
|
|
.attr("r", 4.5)
|
|
|
|
.attr("cx", function(d) { return x0(d.x); })
|
|
|
|
.attr("cy", function(d) { return y0(d.y); })
|
|
|
|
.style("opacity", 1e-6)
|
2011-04-24 13:18:03 +04:00
|
|
|
.transition()
|
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("cx", function(d) { return x1(d.x); })
|
|
|
|
.attr("cy", function(d) { return y1(d.y); })
|
|
|
|
.style("opacity", 1);
|
2011-04-24 13:18:03 +04:00
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
circle.transition()
|
2011-04-24 13:18:03 +04:00
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("cx", function(d) { return x1(d.x); })
|
|
|
|
.attr("cy", function(d) { return y1(d.y); })
|
|
|
|
.style("opacity", 1);
|
2011-04-24 13:18:03 +04:00
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
circle.exit().transition()
|
2011-04-24 13:18:03 +04:00
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("cx", function(d) { return x1(d.x); })
|
|
|
|
.attr("cy", function(d) { return y1(d.y); })
|
2011-04-24 13:18:03 +04:00
|
|
|
.style("opacity", 1e-6)
|
|
|
|
.remove();
|
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
var xformat = tickFormat || x1.tickFormat(4),
|
|
|
|
yformat = tickFormat || y1.tickFormat(4),
|
|
|
|
tx = function(d) { return "translate(" + x1(d) + "," + height + ")"; },
|
|
|
|
ty = function(d) { return "translate(0," + y1(d) + ")"; };
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
// Update x-ticks.
|
|
|
|
var xtick = g.selectAll("g.x.tick")
|
|
|
|
.data(x1.ticks(4), function(d) {
|
|
|
|
return this.textContent || xformat(d);
|
|
|
|
});
|
|
|
|
|
|
|
|
var xtickEnter = xtick.enter().append("svg:g")
|
|
|
|
.attr("class", "x tick")
|
|
|
|
.attr("transform", function(d) { return "translate(" + x0(d) + "," + height + ")"; })
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1e-6);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
xtickEnter.append("svg:line")
|
|
|
|
.attr("y1", 0)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("y2", -6);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
xtickEnter.append("svg:text")
|
|
|
|
.attr("text-anchor", "middle")
|
|
|
|
.attr("dy", "1em")
|
|
|
|
.text(xformat);
|
|
|
|
|
|
|
|
// Transition the entering ticks to the new scale, x1.
|
|
|
|
xtickEnter.transition()
|
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("transform", tx)
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
// Transition the updating ticks to the new scale, x1.
|
2011-05-01 08:16:58 +04:00
|
|
|
xtick.transition()
|
2011-04-25 15:59:11 +04:00
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("transform", tx)
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
// Transition the exiting ticks to the new scale, x1.
|
|
|
|
xtick.exit().transition()
|
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("transform", tx)
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1e-6)
|
2011-04-25 15:59:11 +04:00
|
|
|
.remove();
|
|
|
|
|
|
|
|
// Update ticks.
|
|
|
|
var ytick = g.selectAll("g.y.tick")
|
|
|
|
.data(y1.ticks(4), function(d) {
|
|
|
|
return this.textContent || yformat(d);
|
|
|
|
});
|
|
|
|
|
|
|
|
var ytickEnter = ytick.enter().append("svg:g")
|
|
|
|
.attr("class", "y tick")
|
|
|
|
.attr("transform", function(d) { return "translate(0," + y0(d) + ")"; })
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1e-6);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
ytickEnter.append("svg:line")
|
|
|
|
.attr("x1", 0)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("x2", 6);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
ytickEnter.append("svg:text")
|
|
|
|
.attr("text-anchor", "end")
|
|
|
|
.attr("dx", "-.5em")
|
|
|
|
.attr("dy", ".3em")
|
|
|
|
.text(yformat);
|
|
|
|
|
|
|
|
// Transition the entering ticks to the new scale, y1.
|
|
|
|
ytickEnter.transition()
|
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("transform", ty)
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
// Transition the updating ticks to the new scale, y1.
|
2011-05-01 08:16:58 +04:00
|
|
|
ytick.transition()
|
2011-04-25 15:59:11 +04:00
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("transform", ty)
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1);
|
2011-04-25 15:59:11 +04:00
|
|
|
|
|
|
|
// Transition the exiting ticks to the new scale, y1.
|
|
|
|
ytick.exit().transition()
|
|
|
|
.duration(duration)
|
2011-05-01 08:16:58 +04:00
|
|
|
.attr("transform", ty)
|
2011-05-27 08:15:28 +04:00
|
|
|
.style("opacity", 1e-6)
|
2011-04-25 15:59:11 +04:00
|
|
|
.remove();
|
2011-04-24 13:18:03 +04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
qq.width = function(x) {
|
|
|
|
if (!arguments.length) return width;
|
|
|
|
width = x;
|
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
|
|
|
qq.height = function(x) {
|
|
|
|
if (!arguments.length) return height;
|
|
|
|
height = x;
|
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
|
|
|
qq.duration = function(x) {
|
|
|
|
if (!arguments.length) return duration;
|
|
|
|
duration = x;
|
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
|
|
|
qq.domain = function(x) {
|
|
|
|
if (!arguments.length) return domain;
|
|
|
|
domain = x == null ? x : d3.functor(x);
|
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
qq.count = function(z) {
|
|
|
|
if (!arguments.length) return n;
|
|
|
|
n = z;
|
2011-04-24 13:18:03 +04:00
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
qq.x = function(z) {
|
|
|
|
if (!arguments.length) return x;
|
|
|
|
x = z;
|
2011-04-24 13:18:03 +04:00
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
qq.y = function(z) {
|
|
|
|
if (!arguments.length) return y;
|
|
|
|
y = z;
|
2011-04-24 13:18:03 +04:00
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
2011-04-25 15:59:11 +04:00
|
|
|
qq.tickFormat = function(x) {
|
|
|
|
if (!arguments.length) return tickFormat;
|
|
|
|
tickFormat = x;
|
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
2011-04-24 13:18:03 +04:00
|
|
|
return qq;
|
|
|
|
};
|
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
function d3_chart_qqQuantiles(n, values) {
|
|
|
|
var m = values.length - 1;
|
|
|
|
values = values.slice().sort(d3.ascending);
|
|
|
|
return d3.range(n).map(function(i) {
|
|
|
|
return values[~~(i * m / n)];
|
|
|
|
});
|
2011-04-24 13:18:03 +04:00
|
|
|
}
|
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
function d3_chart_qqX(d) {
|
|
|
|
return d.x;
|
2011-04-24 13:18:03 +04:00
|
|
|
}
|
|
|
|
|
2011-05-01 08:16:58 +04:00
|
|
|
function d3_chart_qqY(d) {
|
|
|
|
return d.y;
|
2011-04-24 13:18:03 +04:00
|
|
|
}
|
2011-06-14 04:30:30 +04:00
|
|
|
})();
|