Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions extras/ribbon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* @license
* Copyright 2015 Petr Shevtsov (petr.shevtsov@gmail.com)
* MIT-licensed (http://opensource.org/licenses/MIT)
*
* Plugin options:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be helpful to have a high-level description of this plugin and what it does, e.g. what is a "palette interval"?

*
* `data`: Array of numeric values (0-1) corresponding to the position
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the palette / data distinction? Why not just pass in a list of colors for each rectangle?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, first of all, for example, data items could be somehow calculated (by some other different tools — e.g. R). So it would require some extra layer to process the appropriate mappings between calculations and actual colors. Also, data is data but color is representation.

* in the pallete interval.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

palette (sp)

*
* `parser`: Function (`function (data, dygraph)`) returning the array of numeric
* values. Function arguments: raw data, dygraph instance.
*
* `options`: Object with the following properties:
*
* `palette`: Colors Array. Default: ["transparent", "#ef2929", "#8ae234"]
*
* `height`: Value (0-1) representing the height of the ribbon: 1 - full height,
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's interesting that the x-values of the ribbons are tied to the data whereas the y-values are not (the ribbon doesn't move with y-panning and y-zooming). Why did you choose to do it this way?

* 0.5 - 50% height and so on.
*
* `position`: Value (0-1) representing the start position of the ribbon:
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bottom would be a better name for this, or bottomFrac

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, renaming height to top and position to bottom seems reasonable.

* 0 - start from the bottom of chart, 0.25 - start from the 25% of the chart
* height and so on.
*/

/*global Dygraph:false */

Dygraph.Plugins.Ribbon = (function() {
"use strict";

var ribbon = function(options) {
options = options || {};

this.ribbonData_ = options.data || null;
this.ribbonDataParser_ = options.parser || null;
this.ribbonOptions_ = options.options || {};

var defaultOptions = {
palette: [
"transparent",
"#ef2929",
"#8ae234"
],
height: 1,
position: 0
};

this.ribbonOptions_ = Dygraph.update(defaultOptions, this.ribbonOptions_);
this.ribbonOptions_.height = Math.min(this.ribbonOptions_.height, 1);
this.ribbonOptions_.height = Math.max(this.ribbonOptions_.height, 0);
this.ribbonOptions_.position = Math.min(this.ribbonOptions_.position, 1);
this.ribbonOptions_.position = Math.max(this.ribbonOptions_.position, 0);
};

ribbon.prototype.toString = function() {
return "Ribbon Plugin";
};

ribbon.prototype.activate = function(g) {
if (this.ribbonData_ !== null || this.ribbonDataParser_ !== null) {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you want to throw if this isn't the case? Does it always represent misconfiguration?

return {
willDrawChart: this.willDrawChart
};
}
};

ribbon.prototype.decodeColor = function(val) {
var max = Math.max.apply(null, this.ribbonData_);
val = val / max;
var idx = Math.ceil((this.ribbonOptions_.palette.length - 1) * val);

return this.ribbonOptions_.palette[idx];
};

ribbon.prototype.willDrawChart = function(e) {
var g = e.dygraph;
var layout = g.layout_;
var points = layout.points[0];
var area = layout.getPlotArea();
if (this.ribbonData_ === null) {
this.ribbonData_ = this.ribbonDataParser_(g.rawData_, g);
}
for (var i = 0; i < points.length; i++) {
var point = points[i];
var nextpoint = points[i + 1];

var left = g.toDomCoords(point.xval, 0)[0];
var right = (nextpoint === undefined) ? g.canvas_.width : g.toDomCoords(nextpoint.xval, 0)[0];
var color = this.decodeColor(this.ribbonData_[point.idx]);
var y = area.h * (1 - this.ribbonOptions_.height) + area.y;
var h = (area.h - area.h * this.ribbonOptions_.position) - y;
g.hidden_ctx_.fillStyle = color;
g.hidden_ctx_.fillRect(left, y, right - left, h);
}
};

return ribbon;
})();
142 changes: 142 additions & 0 deletions tests/ribbon.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html>
<head>
<title>Colored Ribbon demo</title>
<script type="text/javascript" src="../dygraph-dev.js"></script>
<script type="text/javascript" src="../extras/ribbon.js"></script>
</head>
<body>
<h1>Colored Ribbon demo</h1>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pshevtsov , following can be included in the <div> of the ribbon.html to document the use, in addition to the source code of the examples:

Description:

Ribbon is a horizontal band of colors that runs through the chart. It can useful to visualize categorical variables (http://en.wikipedia.org/wiki/Categorical_variable) that change over time (along the x-axis). For example multiple states of economy like "bust","recession", "recovery", "boom" can be encoded as [0, 1, 2, 3] respectively (or normalized as [0, 0.33, 0.66, 1]) and assigned colors ["red","orange","green","purple"].

Arguments:

data:
Array of numeric values in the range from 0 to 1. Ribbon data array must be of same length as number of rows of Dygraph raw data table. Each of the values (0-1) scales to a position in the palette array scale (see palette argument below). For example, if palette is defined as ["transparent", "#ef2929", "#8ae234"], then 0 is the leftmost position ("#ef2929") on the palette scale, 1 is the rightmost position ("#8ae234") and 0.5 is the middle position ("#ef2929")

parser:
Function (function (data, dygraph)) returning the array of numeric values. Function arguments: raw data, dygraph instance. Provide it if the ribbon data, i.e. encodings are part of the dygraph raw data table rather than supplied separately through ribbon data argument. See example 2.

options:
Object with the following properties:

  • palette:
    array of hexadecimal color codes. Default: ["transparent", "#ef2929", "#8ae234"]. Pick colors from e.g. http://www.w3schools.com/tags/ref_colorpicker.asp
  • top:
    vertical position of the top edge of the ribbon relative to chart height. Value in the range from 0 (bottom edge of the chart) to 1 (top edge of the chart). E.g. 0.5 places the top edge of the ribbon at the 50% height of the chart.
  • bottom:
    vertical position of the bottom edge of the ribbon relative to chart height. See top.

<h2>Colored Ribbon with default options</h2>
<div id="div_g1" style="width:600px; height:300px;"></div>

<h2>Colored Ribbon with Encoded colors in data column</h2>
<div id="div_g2" style="width:600px; height:300px;"></div>

<h2>Colored Ribbon with customized height</h2>
<div id="div_g3" style="width:600px; height:300px;"></div>

<h2>Colored Ribbon with customized position</h2>
<div id="div_g4" style="width:600px; height:300px;"></div>

<h2>Colored Ribbon with customized colors</h2>
<div id="div_g5" style="width:600px; height:300px;"></div>

<script type="text/javascript">
function data() {
return "Date,High,Low\n" +
"20070101,62,39\n" +
"20070102,62,44\n" +
"20070103,62,42\n" +
"20070104,57,45\n" +
"20070105,54,44\n" +
"20070106,55,36\n" +
"20070107,62,45\n" +
"20070108,66,48\n" +
"20070109,63,39\n" +
"20070110,57,37\n";
}

var ribbon = new Dygraph.Plugins.Ribbon({
data: [0, 0.5, 0.5, 1, 1, 1, 0, 0, 0, 0.5]
});

var g1 = new Dygraph(
document.getElementById("div_g1"),
data,
{
plugins: [ribbon]
}
);

function data_color() {
return "Date,High,Low,Color\n" +
"20070101,62,39\n" +
"20070102,62,44,0.5\n" +
"20070103,62,42,0.5\n" +
"20070104,57,45,1\n" +
"20070105,54,44,1\n" +
"20070106,55,36,1\n" +
"20070107,62,45\n" +
"20070108,66,48\n" +
"20070109,63,39\n" +
"20070110,57,37,0.5\n";
}

ribbon = new Dygraph.Plugins.Ribbon({
parser: function(data) {
var colorIdx = 3;
var colors = [];
for (var i = 0; i < data.length; i++) {
var row = data[i];
if (row[colorIdx]) {
colors.push(row[colorIdx]);
} else {
colors.push(0);
}
}
return colors;
}
});

var g2 = new Dygraph(
document.getElementById("div_g2"),
data_color,
{
plugins: [ribbon]
}
);

ribbon = new Dygraph.Plugins.Ribbon({
data: [0, 0.5, 0.5, 1, 1, 1, 0, 0, 0, 0.5],
options: {
height: 0.75
}
});

var g3 = new Dygraph(
document.getElementById("div_g3"),
data,
{
plugins: [ribbon]
}
);

ribbon = new Dygraph.Plugins.Ribbon({
data: [0, 0.5, 0.5, 1, 1, 1, 0, 0, 0, 0.5],
options: {
height: 0.75,
position: 0.25
}
});

var g4 = new Dygraph(
document.getElementById("div_g4"),
data,
{
plugins: [ribbon]
}
);

ribbon = new Dygraph.Plugins.Ribbon({
data: [0, 0.5, 0.5, 1, 1, 1, 0, 0, 0, 0.5],
options: {
palette: [
"#ad7fa8",
"#75507b",
"#5c3566"
]
}
});

var g5 = new Dygraph(
document.getElementById("div_g5"),
data,
{
plugins: [ribbon]
}
);
</script>
</body>
</html>