copy datahandler changes over from full-closure branch
[dygraphs.git] / datahandler / datahandler.js
CommitLineData
a49c164a
DE
1/**
2 * @license
3 * Copyright 2013 David Eberlein (david.eberlein@ch.sauter-bc.com)
4 * MIT-licensed (http://opensource.org/licenses/MIT)
5 */
6
7/**
8 * @fileoverview This file contains the managment of data handlers
9 * @author David Eberlein (david.eberlein@ch.sauter-bc.com)
10 *
11 * The idea is to define a common, generic data format that works for all data
12 * structures supported by dygraphs. To make this possible, the DataHandler
13 * interface is introduced. This makes it possible, that dygraph itself can work
14 * with the same logic for every data type independent of the actual format and
15 * the DataHandler takes care of the data format specific jobs.
16 * DataHandlers are implemented for all data types supported by Dygraphs and
17 * return Dygraphs compliant formats.
18 * By default the correct DataHandler is chosen based on the options set.
19 * Optionally the user may use his own DataHandler (similar to the plugin
20 * system).
21 *
22 *
23 * The unified data format returend by each handler is defined as so:
24 * series[n][point] = [x,y,(extras)]
25 *
26 * This format contains the common basis that is needed to draw a simple line
27 * series extended by optional extras for more complex graphing types. It
28 * contains a primitive x value as first array entry, a primitive y value as
29 * second array entry and an optional extras object for additional data needed.
30 *
31 * x must always be a number.
32 * y must always be a number, NaN of type number or null.
33 * extras is optional and must be interpreted by the DataHandler. It may be of
34 * any type.
35 *
36 * In practice this might look something like this:
37 * default: [x, yVal]
38 * errorBar / customBar: [x, yVal, [yTopVariance, yBottomVariance] ]
39 *
40 */
a49c164a
DE
41/*global Dygraph:false */
42/*global DygraphLayout:false */
3ea41d86 43
a49c164a 44/**
a49c164a
DE
45 *
46 * The data handler is responsible for all data specific operations. All of the
47 * series data it receives and returns is always in the unified data format.
48 * Initially the unified data is created by the extractSeries method
749281f8 49 * @constructor
a49c164a
DE
50 */
51Dygraph.DataHandler = function () {
749281f8
DV
52};
53
54/**
55 * A collection of functions to create and retrieve data handlers.
56 * @type {Object.<!Dygraph.DataHandler>}
57 */
58Dygraph.DataHandlers = {};
a49c164a 59
749281f8 60(function() {
a49c164a 61
749281f8 62"use strict";
a49c164a 63
749281f8 64var handler = Dygraph.DataHandler;
a49c164a 65
749281f8
DV
66/**
67 * X-value array index constant for unified data samples.
68 * @const
69 * @type {number}
70 */
71handler.X = 0;
a49c164a 72
749281f8
DV
73/**
74 * Y-value array index constant for unified data samples.
75 * @const
76 * @type {number}
77 */
78handler.Y = 1;
79
80/**
81 * Extras-value array index constant for unified data samples.
82 * @const
83 * @type {number}
84 */
85handler.EXTRAS = 2;
86
87/**
88 * Extracts one series from the raw data (a 2D array) into an array of the
89 * unified data format.
90 * This is where undesirable points (i.e. negative values on log scales and
91 * missing values through which we wish to connect lines) are dropped.
92 * TODO(danvk): the "missing values" bit above doesn't seem right.
93 *
94 * @param {!Array.<Array>} rawData The raw data passed into dygraphs where
95 * rawData[i] = [x,ySeries1,...,ySeriesN].
96 * @param {!number} seriesIndex Index of the series to extract. All other
97 * series should be ignored.
98 * @param {!DygraphOptions} options Dygraph options.
99 * @return {Array.<[!number,?number,?]>} The series in the unified data format
100 * where series[i] = [x,y,{extras}].
101 */
102handler.prototype.extractSeries = function(rawData, seriesIndex, options) {
103};
a49c164a 104
749281f8
DV
105/**
106 * Converts a series to a Point array.
107 *
108 * @param {!Array.<[!number,?number,?]>} series The series in the unified
109 * data format where series[i] = [x,y,{extras}].
110 * @param {!string} setName Name of the series.
111 * @param {!number} boundaryIdStart Index offset of the first point, equal to the
112 * number of skipped points left of the date window minimum (if any).
113 * @return {!Array.<Dygraph.PointType>} List of points for this series.
114 */
115handler.prototype.seriesToPoints = function(series, setName, boundaryIdStart) {
116 // TODO(bhs): these loops are a hot-spot for high-point-count charts. In
117 // fact,
118 // on chrome+linux, they are 6 times more expensive than iterating through
119 // the
120 // points and drawing the lines. The brunt of the cost comes from allocating
121 // the |point| structures.
122 var points = [];
123 for ( var i = 0; i < series.length; ++i) {
124 var item = series[i];
125 var yraw = item[1];
126 var yval = yraw === null ? null : Dygraph.parseFloat(yraw);
127 var point = {
128 x : NaN,
129 y : NaN,
130 xval : Dygraph.parseFloat(item[0]),
131 yval : yval,
132 name : setName, // TODO(danvk): is this really necessary?
133 idx : i + boundaryIdStart
134 };
135 points.push(point);
136 }
137 this.onPointsCreated_(series, points);
138 return points;
139};
a49c164a 140
749281f8
DV
141/**
142 * Callback called for each series after the series points have been generated
143 * which will later be used by the plotters to draw the graph.
144 * Here data may be added to the seriesPoints which is needed by the plotters.
145 * The indexes of series and points are in sync meaning the original data
146 * sample for series[i] is points[i].
147 *
148 * @param {!Array.<[!number,?number,?]>} series The series in the unified
149 * data format where series[i] = [x,y,{extras}].
150 * @param {!Array.<Dygraph.PointType>} points The corresponding points passed
151 * to the plotter.
152 * @protected
153 */
154handler.prototype.onPointsCreated_ = function(series, points) {
155};
a49c164a 156
749281f8
DV
157/**
158 * Calculates the rolling average of a data set.
159 *
160 * @param {!Array.<[!number,?number,?]>} series The series in the unified
161 * data format where series[i] = [x,y,{extras}].
162 * @param {!number} rollPeriod The number of points over which to average the data
163 * @param {!DygraphOptions} options The dygraph options.
164 * TODO(danvk): be more specific than "Array" here.
165 * @return {!Array.<[!number,?number,?]>} the rolled series.
166 */
167handler.prototype.rollingAverage = function(series, rollPeriod, options) {
168};
a49c164a 169
749281f8
DV
170/**
171 * Computes the range of the data series (including confidence intervals).
172 *
173 * @param {!Array.<[!number,?number,?]>} series The series in the unified
174 * data format where series[i] = [x, y, {extras}].
175 * @param {!Array.<number>} dateWindow The x-value range to display with
176 * the format: [min, max].
177 * @param {!DygraphOptions} options The dygraph options.
178 * @return {Array.<number>} The low and high extremes of the series in the
179 * given window with the format: [low, high].
180 */
181handler.prototype.getExtremeYValues = function(series, dateWindow, options) {
182};
a49c164a 183
749281f8
DV
184/**
185 * Callback called for each series after the layouting data has been
186 * calculated before the series is drawn. Here normalized positioning data
187 * should be calculated for the extras of each point.
188 *
189 * @param {!Array.<Dygraph.PointType>} points The points passed to
190 * the plotter.
191 * @param {!Object} axis The axis on which the series will be plotted.
192 * @param {!boolean} logscale Weather or not to use a logscale.
193 */
194handler.prototype.onLineEvaluated = function(points, axis, logscale) {
195};
a49c164a 196
749281f8
DV
197/**
198 * Helper method that computes the y value of a line defined by the points p1
199 * and p2 and a given x value.
200 *
201 * @param {!Array.<number>} p1 left point ([x,y]).
202 * @param {!Array.<number>} p2 right point ([x,y]).
203 * @param {!number} xValue The x value to compute the y-intersection for.
204 * @return {number} corresponding y value to x on the line defined by p1 and p2.
205 * @private
206 */
207handler.prototype.computeYInterpolation_ = function(p1, p2, xValue) {
208 var deltaY = p2[1] - p1[1];
209 var deltaX = p2[0] - p1[0];
210 var gradient = deltaY / deltaX;
211 var growth = (xValue - p1[0]) * gradient;
212 return p1[1] + growth;
213};
a49c164a 214
749281f8
DV
215/**
216 * Helper method that returns the first and the last index of the given series
217 * that lie inside the given dateWindow.
218 *
219 * @param {!Array.<[!number,?number,?]>} series The series in the unified
220 * data format where series[i] = [x,y,{extras}].
221 * @param {!Array.<number>} dateWindow The x-value range to display with
222 * the format: [min,max].
223 * @return {!Array.<[!number,?number,?]>} The samples of the series that
224 * are in the given date window.
225 * @private
226 */
227handler.prototype.getIndexesInWindow_ = function(series, dateWindow) {
228 var firstIdx = 0, lastIdx = series.length - 1;
229 if (dateWindow) {
230 var idx = 0;
231 var low = dateWindow[0];
232 var high = dateWindow[1];
233
234 // Start from each side of the array to minimize the performance
235 // needed.
236 while (idx < series.length - 1 && series[idx][0] < low) {
237 firstIdx++;
238 idx++;
a49c164a 239 }
749281f8
DV
240 idx = series.length - 1;
241 while (idx > 0 && series[idx][0] > high) {
242 lastIdx--;
243 idx--;
a49c164a 244 }
749281f8
DV
245 }
246 if (firstIdx <= lastIdx) {
247 return [ firstIdx, lastIdx ];
248 } else {
249 return [ 0, series.length - 1 ];
250 }
a49c164a 251};
3ea41d86
DV
252
253})();