Dygraph.update(this.options, new_options ? new_options : {});
};
+/**
+ * Return a copy of the point at the indicated index, with its yval unstacked.
+ * @param int index of point in layout_.points
+ */
+DygraphLayout.prototype.unstackPointAtIndex = function(idx) {
+ var point = this.points[idx];
+
+ // Clone the point since we modify it
+ var unstackedPoint = {};
+ for (var i in point) {
+ unstackedPoint[i] = point[i];
+ }
+
+ if (!this.attr_("stackedGraph")) {
+ return unstackedPoint;
+ }
+
+ // The unstacked yval is equal to the current yval minus the yval of the
+ // next point at the same xval.
+ for (var i = idx+1; i < this.points.length; i++) {
+ if (this.points[i].xval == point.xval) {
+ unstackedPoint.yval -= this.points[i].yval;
+ break;
+ }
+ }
+
+ return unstackedPoint;
+}
+
// Subclass PlotKit.CanvasRenderer to add:
// 1. X/Y grid overlay
// 2. Ability to draw error bars (if required)
for (var i = 0; i < setCount; i++) {
var setName = setNames[i];
var color = this.colors[setName];
+ var strokeWidth = this.dygraph_.attr_("strokeWidth", setName);
// setup graphics context
context.save();
var point = this.layout.points[0];
- var pointSize = this.dygraph_.attr_("pointSize");
+ var pointSize = this.dygraph_.attr_("pointSize", setName);
var prevX = null, prevY = null;
- var drawPoints = this.dygraph_.attr_("drawPoints");
+ var drawPoints = this.dygraph_.attr_("drawPoints", setName);
var points = this.layout.points;
for (var j = 0; j < points.length; j++) {
var point = points[j];
prevX = point.canvasx;
prevY = point.canvasy;
} else {
- ctx.beginPath();
- ctx.strokeStyle = color;
- ctx.lineWidth = this.options.strokeWidth;
- ctx.moveTo(prevX, prevY);
- if (stepPlot) {
- ctx.lineTo(point.canvasx, prevY);
+ // TODO(danvk): figure out why this conditional is necessary.
+ if (strokeWidth) {
+ ctx.beginPath();
+ ctx.strokeStyle = color;
+ ctx.lineWidth = strokeWidth;
+ ctx.moveTo(prevX, prevY);
+ if (stepPlot) {
+ ctx.lineTo(point.canvasx, prevY);
+ }
+ prevX = point.canvasx;
+ prevY = point.canvasy;
+ ctx.lineTo(prevX, prevY);
+ ctx.stroke();
}
- prevX = point.canvasx;
- prevY = point.canvasy;
- ctx.lineTo(prevX, prevY);
- ctx.stroke();
}
if (drawPoints || isIsolated) {
this.start_();
};
-Dygraph.prototype.attr_ = function(name) {
- if (typeof(this.user_attrs_[name]) != 'undefined') {
+Dygraph.prototype.attr_ = function(name, seriesName) {
+ if (seriesName &&
+ typeof(this.user_attrs_[seriesName]) != 'undefined' &&
+ this.user_attrs_[seriesName] != null &&
+ typeof(this.user_attrs_[seriesName][name]) != 'undefined') {
+ return this.user_attrs_[seriesName][name];
+ } else if (typeof(this.user_attrs_[name]) != 'undefined') {
return this.user_attrs_[name];
} else if (typeof(this.attrs_[name]) != 'undefined') {
return this.attrs_[name];
*/
Dygraph.prototype.updateSelection_ = function() {
// Clear the previously drawn vertical, if there is one
- var circleSize = this.attr_('highlightCircleSize');
var ctx = this.canvas_.getContext("2d");
if (this.previousVerticalX_ >= 0) {
+ // Determine the maximum highlight circle size.
+ var maxCircleSize = 0;
+ var labels = this.attr_('labels');
+ for (var i = 1; i < labels.length; i++) {
+ var r = this.attr_('highlightCircleSize', labels[i]);
+ if (r > maxCircleSize) maxCircleSize = r;
+ }
var px = this.previousVerticalX_;
- ctx.clearRect(px - circleSize - 1, 0, 2 * circleSize + 2, this.height_);
+ ctx.clearRect(px - maxCircleSize - 1, 0,
+ 2 * maxCircleSize + 2, this.height_);
}
var isOK = function(x) { return x && !isNaN(x); };
if (this.attr_('showLabelsOnHighlight')) {
// Set the status message to indicate the selected point(s)
for (var i = 0; i < this.selPoints_.length; i++) {
- if (!this.attr_("labelsShowZeroValues") && this.selPoints_[i].yval == 0) continue;
+ if (!this.attr_("labelsShowZeroValues") && this.selPoints_[i].yval == 0) continue;
if (!isOK(this.selPoints_[i].canvasy)) continue;
if (this.attr_("labelsSeparateLines")) {
replace += "<br/>";
ctx.save();
for (var i = 0; i < this.selPoints_.length; i++) {
if (!isOK(this.selPoints_[i].canvasy)) continue;
+ var circleSize =
+ this.attr_('highlightCircleSize', this.selPoints_[i].name);
ctx.beginPath();
ctx.fillStyle = this.plotter_.colors[this.selPoints_[i].name];
ctx.arc(canvasx, this.selPoints_[i].canvasy, circleSize,
if (row !== false && row >= 0) {
for (var i in this.layout_.datasets) {
if (row < this.layout_.datasets[i].length) {
- this.selPoints_.push(this.layout_.points[pos+row]);
+ var point = this.layout_.points[pos+row];
+
+ if (this.attr_("stackedGraph")) {
+ point = this.layout_.unstackPointAtIndex(pos+row);
+ }
+
+ this.selPoints_.push(point);
}
pos += this.layout_.datasets[i].length;
}
this.setColors_();
this.attrs_['pointSize'] = 0.5 * this.attr_('highlightCircleSize');
- var connectSeparatedPoints = this.attr_('connectSeparatedPoints');
-
// Loop over the fields (series). Go from the last to the first,
// because if they're stacked that's how we accumulate the values.
for (var i = data[0].length - 1; i >= 1; i--) {
if (!this.visibility()[i - 1]) continue;
+ var connectSeparatedPoints = this.attr_('connectSeparatedPoints', i);
+
var series = [];
for (var j = 0; j < data.length; j++) {
if (data[j][i] != null || !connectSeparatedPoints) {
var labels = [data.getColumnLabel(0)];
for (var i = 0; i < colIdx.length; i++) {
labels.push(data.getColumnLabel(colIdx[i]));
+ if (this.attr_("errorBars")) i += 1;
}
this.attrs_.labels = labels;
cols = labels.length;
var row = [];
if (typeof(data.getValue(i, 0)) === 'undefined' ||
data.getValue(i, 0) === null) {
- this.warning("Ignoring row " + i +
- " of DataTable because of undefined or null first column.");
+ this.warn("Ignoring row " + i +
+ " of DataTable because of undefined or null first column.");
continue;
}
if (attrs.valueRange) {
this.valueRange_ = attrs.valueRange;
}
+
+ // TODO(danvk): validate per-series options.
+ // Supported:
+ // strokeWidth
+ // pointSize
+ // drawPoints
+ // highlightCircleSize
+
Dygraph.update(this.user_attrs_, attrs);
Dygraph.update(this.renderOptions_, attrs);
return this.annotations_;
};
+/**
+ * Get the index of a series (column) given its name. The first column is the
+ * x-axis, so the data series start with index 1.
+ */
+Dygraph.prototype.indexFromSetName = function(name) {
+ var labels = this.attr_("labels");
+ for (var i = 0; i < labels.length; i++) {
+ if (labels[i] == name) return i;
+ }
+ return null;
+};
+
Dygraph.addAnnotationRule = function() {
if (Dygraph.addedAnnotationCSS) return;
{
labels: labels,
drawPoints: true,
+ strokeWidth: 0.0,
drawCallback: function(g, is_initial) {
if (!is_initial) return;
var c = g.getColors();
var b = (sum_y - a * sum_x) / num;
coeffs[series] = [b, a];
- if (console) {
+ if (typeof(console) != 'undefined') {
console.log("coeffs(" + series + "): [" + b + ", " + a + "]");
}
// Generate a new data set with the regression lines.
var new_labels = [];
var new_colors = [];
+ var new_opts = {};
for (var i = 0; i < labels.length; i++) {
new_labels.push(labels[i]);
if (i) new_colors.push(orig_colors[i - 1]);
if (coeffs[i]) {
// Darken the series by 50% to generate its regression.
- new_labels.push(labels[i] + " Regression");
+ var label = labels[i] + " Regression";
+ new_labels.push(label);
var c = new RGBColor(orig_colors[i - 1]);
c.r = Math.floor(255 - 0.5 * (255 - c.r));
c.g = Math.floor(255 - 0.5 * (255 - c.g));
c.b = Math.floor(255 - 0.5 * (255 - c.b));
new_colors.push(c.toHex());
+ new_opts[label] = {
+ drawPoints: false,
+ strokeWidth: 1.0
+ };
}
}
}
}
- // TODO(danvk): set colors intelligently.
-
- g.updateOptions({
- file: new_data,
- labels: new_labels,
- colors: new_colors
- });
+ new_opts.file = new_data;
+ new_opts.labels = new_labels;
+ new_opts.colors = new_colors;
+ g.updateOptions(new_opts);
}
function clearLines() {
for (var i = 0; i < coeffs.length; i++) coeffs[i] = null;
updateChart();
}
-
- // function drawLines(ctx, area, layout) {
- // if (typeof(g) == 'undefined') return; // won't be set on the initial draw.
-
- // var range = g.xAxisRange();
- // for (var i = 0; i < coeffs.length; i++) {
- // if (!coeffs[i]) continue;
- // var a = coeffs[i][1];
- // var b = coeffs[i][0];
-
- // var x1 = range[0];
- // var y1 = a * x1 + b;
- // var x2 = range[1];
- // var y2 = a * x2 + b;
-
- // var p1 = g.toDomCoords(x1, y1);
- // var p2 = g.toDomCoords(x2, y2);
-
- // var color = g.getColors()[i - 1];
- // ctx.save();
- // ctx.strokeStyle = color;
- // ctx.lineWidth = 1.0;
- // ctx.beginPath();
- // ctx.moveTo(p1[0], p1[1]);
- // ctx.lineTo(p2[0], p2[1]);
- // ctx.closePath();
- // ctx.stroke();
- // ctx.restore();
- // }
- // }
</script>
{
labels: ['X', 'Y1', 'Y2'],
underlayCallback: drawLines,
- drawPoints: true
+ drawPoints: true,
+ strokeWidth: 0.0
}
);
var b = (sum_y - a * sum_x) / num;
coeffs[series] = [b, a];
- if (console) {
+ if (typeof(console) != 'undefined') {
console.log("coeffs(" + series + "): [" + b + ", " + a + "]");
}
--- /dev/null
+<html>
+ <head>
+ <title>Per-Series Properties</title>
+ <!--[if IE]>
+ <script type="text/javascript" src="excanvas.js"></script>
+ <![endif]-->
+ <script type="text/javascript" src="../strftime/strftime-min.js"></script>
+ <script type="text/javascript" src="../rgbcolor/rgbcolor.js"></script>
+ <script type="text/javascript" src="../dygraph-canvas.js"></script>
+ <script type="text/javascript" src="../dygraph.js"></script>
+ </head>
+ <body>
+ <h2>Chart with per-series properties</h2>
+ <div id="demodiv"></div>
+ <br/><br/>
+ <div id="demodiv2"></div>
+
+ <script type="text/javascript">
+ g = new Dygraph(
+ document.getElementById("demodiv"),
+ function() {
+ var zp = function(x) { if (x < 10) return "0"+x; else return x; };
+ var r = "date,parabola,line,another line,sine wave\n";
+ for (var i=1; i<=31; i++) {
+ r += "200610" + zp(i);
+ r += "," + 10*(i*(31-i));
+ r += "," + 10*(8*i);
+ r += "," + 10*(250 - 8*i);
+ r += "," + 10*(125 + 125 * Math.sin(0.3*i));
+ r += "\n";
+ }
+ return r;
+ },
+ {
+ strokeWidth: 2,
+ 'parabola': {
+ strokeWidth: 0.0,
+ drawPoints: true,
+ pointSize: 4,
+ highlightCircleSize: 6
+ },
+ 'line': {
+ strokeWidth: 1.0,
+ drawPoints: true,
+ pointSize: 1.5
+ },
+ 'sine wave': {
+ strokeWidth: 3,
+ highlightCircleSize: 10
+ }
+ }
+ );
+ </script>
+</body>
+</html>
<div id="stacked_missing_div"></div>
<p>Stacked graph with many series:</p>
<div id="stacked_many_div"></div>
+ <p>Change selection/highlighting on all graphs:</p>
+ <div id="graph_selection_div">
+ <select onchange="javascript:setSelection(this.options[this.selectedIndex].value);">
+ <option value="" selected></option>
+ <option value="0">0</option>
+ <option value="10">10</option>
+ <option value="20">20</option>
+ <option value="30">30</option>
+ <option value="40">40</option>
+ <option value="50">50</option>
+ <option value="60">60</option>
+ <option value="70">70</option>
+ <option value="80">80</option>
+ <option value="90">90</option>
+ <option value="99">99</option>
+ </select>
+ </div>
<script type="text/javascript">
data = "X,x,100-x\n";
for (var i = 0; i < 100; i++) {
data += i + "," + i + "," + (100 - i) + "\n";
}
+
+ var graphs = [];
- new Dygraph(document.getElementById("simple_div"),
- data);
- new Dygraph(document.getElementById("stacked_div"),
- data,
- { stackedGraph: true });
+ graphs.push(
+ new Dygraph(
+ document.getElementById("simple_div"),
+ data));
+
+ graphs.push(
+ new Dygraph(
+ document.getElementById("stacked_div"),
+ data,
+ { stackedGraph: true }));
missing_data = "X,x,100-x\n";
for (var i = 0; i < 100; i++) {
}
}
- new Dygraph(document.getElementById("simple_missing_div"),
- missing_data);
- new Dygraph(document.getElementById("stacked_missing_div"),
- missing_data,
- { stackedGraph: true });
+ graphs.push(
+ new Dygraph(
+ document.getElementById("simple_missing_div"),
+ missing_data));
+
+ graphs.push(
+ new Dygraph(
+ document.getElementById("stacked_missing_div"),
+ missing_data,
+ { stackedGraph: true }));
many_data = "X,a,b,c,d,e,100-a,100-b,100-c,100-d,100-e\n";
for (var i = 0; i < 100; i++) {
many_data += "\n";
}
- new Dygraph(document.getElementById("stacked_many_div"),
- many_data,
- { stackedGraph: true });
+ graphs.push(
+ new Dygraph(
+ document.getElementById("stacked_many_div"),
+ many_data,
+ { stackedGraph: true }));
+
+ function setSelection(row) {
+ for (var i = 0; i < graphs.length; i++) {
+ graphs[i].setSelection(row ? row : false);
+ }
+ }
</script>
</body>
</html>