X-Git-Url: https://adrianiainlam.tk/git/?a=blobdiff_plain;f=datahandler%2Fdatahandler.js;h=b3eae91ea612a10367295a2d408d5699405ae501;hb=fb2b89c8d082b2bd83b5e809d343254e388adb4c;hp=5ed0112de72a23e65cc3ee97bc9951b8df3743c8;hpb=023b7bd44183090c18e450fbd717ad933fac2e41;p=dygraphs.git diff --git a/datahandler/datahandler.js b/datahandler/datahandler.js index 5ed0112..b3eae91 100644 --- a/datahandler/datahandler.js +++ b/datahandler/datahandler.js @@ -41,9 +41,15 @@ /*global Dygraph:false */ /*global DygraphLayout:false */ -(function() { - -"use strict"; +/** + * + * The data handler is responsible for all data specific operations. All of the + * series data it receives and returns is always in the unified data format. + * Initially the unified data is created by the extractSeries method + * @constructor + */ +Dygraph.DataHandler = function () { +}; /** * A collection of functions to create and retrieve data handlers. @@ -51,213 +57,214 @@ */ Dygraph.DataHandlers = {}; +(function() { + +"use strict"; + +var handler = Dygraph.DataHandler; + /** - * - * The data handler is responsible for all data specific operations. All of the - * series data it receives and returns is always in the unified data format. - * Initially the unified data is created by the extractSeries method + * X-value array index constant for unified data samples. + * @const + * @type {number} */ -Dygraph.DataHandler = function () { - /** - * Constructor for all data handlers. - * @constructor - */ - var handler = function() { - return this; - }; - - /** - * X-value array index constant for unified data samples. - * @const - * @type {number} - */ - handler.X = 0; +handler.X = 0; - /** - * Y-value array index constant for unified data samples. - * @const - * @type {number} - */ - handler.Y = 1; +/** + * Y-value array index constant for unified data samples. + * @const + * @type {number} + */ +handler.Y = 1; - /** - * Extras-value array index constant for unified data samples. - * @const - * @type {number} - */ - handler.EXTRAS = 2; +/** + * Extras-value array index constant for unified data samples. + * @const + * @type {number} + */ +handler.EXTRAS = 2; - /** - * Extracts one series from the raw data (a 2D array) into an array of the - * unified data format. - * This is where undesirable points (i.e. negative values on log scales and - * missing values through which we wish to connect lines) are dropped. - * TODO(danvk): the "missing values" bit above doesn't seem right. - * - * @param rawData {!Array.} The raw data passed into dygraphs where - * rawData[i] = [x,ySeries1,...,ySeriesN]. - * @param seriesIndex {!number} Index of the series to extract. All other series should - * be ignored. - * @param options {!DygraphOptions} Dygraph options. - * @returns {Array.<[!number,?number,?]>} The series in the unified data format - * where series[i] = [x,y,{extras}]. - * @public - */ - handler.prototype.extractSeries = function(rawData, seriesIndex, options) { - }; +/** + * Extracts one series from the raw data (a 2D array) into an array of the + * unified data format. + * This is where undesirable points (i.e. negative values on log scales and + * missing values through which we wish to connect lines) are dropped. + * TODO(danvk): the "missing values" bit above doesn't seem right. + * + * @param {!Array.} rawData The raw data passed into dygraphs where + * rawData[i] = [x,ySeries1,...,ySeriesN]. + * @param {!number} seriesIndex Index of the series to extract. All other + * series should be ignored. + * @param {!DygraphOptions} options Dygraph options. + * @return {Array.<[!number,?number,?]>} The series in the unified data format + * where series[i] = [x,y,{extras}]. + */ +handler.prototype.extractSeries = function(rawData, seriesIndex, options) { +}; - /** - * Converts a series to a Point array. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!string} setName Name of the series. - * @param {!number} boundaryIdStart Index offset of the first point, equal to the - * number of skipped points left of the date window minimum (if any). - * @return {!Array.} List of points for this series. - * @public - */ - handler.prototype.seriesToPoints = function(series, setName, boundaryIdStart) { - // TODO(bhs): these loops are a hot-spot for high-point-count charts. In - // fact, - // on chrome+linux, they are 6 times more expensive than iterating through - // the - // points and drawing the lines. The brunt of the cost comes from allocating - // the |point| structures. - var points = []; - for ( var i = 0; i < series.length; ++i) { - var item = series[i]; - var yraw = item[1]; - var yval = yraw === null ? null : DygraphLayout.parseFloat_(yraw); - var point = { - x : NaN, - y : NaN, - xval : DygraphLayout.parseFloat_(item[0]), - yval : yval, - name : setName, // TODO(danvk): is this really necessary? - idx : i + boundaryIdStart - }; - points.push(point); - } - handler.prototype.onPointsCreated_(series, points); - return points; - }; +/** + * Converts a series to a Point array. The resulting point array must be + * returned in increasing order of idx property. + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!string} setName Name of the series. + * @param {!number} boundaryIdStart Index offset of the first point, equal to the + * number of skipped points left of the date window minimum (if any). + * @return {!Array.} List of points for this series. + */ +handler.prototype.seriesToPoints = function(series, setName, boundaryIdStart) { + // TODO(bhs): these loops are a hot-spot for high-point-count charts. In + // fact, + // on chrome+linux, they are 6 times more expensive than iterating through + // the + // points and drawing the lines. The brunt of the cost comes from allocating + // the |point| structures. + var points = []; + for ( var i = 0; i < series.length; ++i) { + var item = series[i]; + var yraw = item[1]; + var yval = yraw === null ? null : handler.parseFloat(yraw); + var point = { + x : NaN, + y : NaN, + xval : handler.parseFloat(item[0]), + yval : yval, + name : setName, // TODO(danvk): is this really necessary? + idx : i + boundaryIdStart + }; + points.push(point); + } + this.onPointsCreated_(series, points); + return points; +}; - /** - * Callback called for each series after the series points have been generated - * which will later be used by the plotters to draw the graph. - * Here data may be added to the seriesPoints which is needed by the plotters. - * The indexes of series and points are in sync meaning the original data - * sample for series[i] is points[i]. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!Array.} points The corresponding points passed - * to the plotter. - * @private - */ - handler.prototype.onPointsCreated_ = function(series, points) { - }; +/** + * Callback called for each series after the series points have been generated + * which will later be used by the plotters to draw the graph. + * Here data may be added to the seriesPoints which is needed by the plotters. + * The indexes of series and points are in sync meaning the original data + * sample for series[i] is points[i]. + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!Array.} points The corresponding points passed + * to the plotter. + * @protected + */ +handler.prototype.onPointsCreated_ = function(series, points) { +}; - /** - * Calculates the rolling average of a data set. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!number} rollPeriod The number of points over which to average the data - * @param {!DygraphOptions} options The dygraph options. - * @return the rolled series. - * @public - */ - handler.prototype.rollingAverage = function(series, rollPeriod, options) { - }; +/** + * Calculates the rolling average of a data set. + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!number} rollPeriod The number of points over which to average the data + * @param {!DygraphOptions} options The dygraph options. + * @return {!Array.<[!number,?number,?]>} the rolled series. + */ +handler.prototype.rollingAverage = function(series, rollPeriod, options) { +}; - /** - * Computes the range of the data series (including confidence intervals). - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!Array.} dateWindow The x-value range to display with - * the format: [min,max]. - * @param {!DygraphOptions} options The dygraph options. - * @return {Array.} The low and high extremes of the series in the given window with - * the format: [low, high]. - * @public - */ - handler.prototype.getExtremeYValues = function(series, dateWindow, options) { - }; +/** + * Computes the range of the data series (including confidence intervals). + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x, y, {extras}]. + * @param {!Array.} dateWindow The x-value range to display with + * the format: [min, max]. + * @param {!DygraphOptions} options The dygraph options. + * @return {Array.} The low and high extremes of the series in the + * given window with the format: [low, high]. + */ +handler.prototype.getExtremeYValues = function(series, dateWindow, options) { +}; - /** - * Callback called for each series after the layouting data has been - * calculated before the series is drawn. Here normalized positioning data - * should be calculated for the extras of each point. - * - * @param {!Array.} points The points passed to - * the plotter. - * @param {!Object} axis The axis on which the series will be plotted. - * @param {!boolean} logscale Weather or not to use a logscale. - * @public - */ - handler.prototype.onLineEvaluated = function(points, axis, logscale) { - }; +/** + * Callback called for each series after the layouting data has been + * calculated before the series is drawn. Here normalized positioning data + * should be calculated for the extras of each point. + * + * @param {!Array.} points The points passed to + * the plotter. + * @param {!Object} axis The axis on which the series will be plotted. + * @param {!boolean} logscale Weather or not to use a logscale. + */ +handler.prototype.onLineEvaluated = function(points, axis, logscale) { +}; - /** - * Helper method that computes the y value of a line defined by the points p1 - * and p2 and a given x value. - * - * @param {!Array.} p1 left point ([x,y]). - * @param {!Array.} p2 right point ([x,y]). - * @param {!number} xValue The x value to compute the y-intersection for. - * @return {number} corresponding y value to x on the line defined by p1 and p2. - * @private - */ - handler.prototype.computeYInterpolation_ = function(p1, p2, xValue) { - var deltaY = p2[1] - p1[1]; - var deltaX = p2[0] - p1[0]; - var gradient = deltaY / deltaX; - var growth = (xValue - p1[0]) * gradient; - return p1[1] + growth; - }; +/** + * Helper method that computes the y value of a line defined by the points p1 + * and p2 and a given x value. + * + * @param {!Array.} p1 left point ([x,y]). + * @param {!Array.} p2 right point ([x,y]). + * @param {!number} xValue The x value to compute the y-intersection for. + * @return {number} corresponding y value to x on the line defined by p1 and p2. + * @private + */ +handler.prototype.computeYInterpolation_ = function(p1, p2, xValue) { + var deltaY = p2[1] - p1[1]; + var deltaX = p2[0] - p1[0]; + var gradient = deltaY / deltaX; + var growth = (xValue - p1[0]) * gradient; + return p1[1] + growth; +}; - /** - * Helper method that returns the first and the last index of the given series - * that lie inside the given dateWindow. - * - * @param {!Array.<[!number,?number,?]>} series The series in the unified - * data format where series[i] = [x,y,{extras}]. - * @param {!Array.} dateWindow The x-value range to display with - * the format: [min,max]. - * @return {!Array.<[!number,?number,?]>} The samples of the series that - * are in the given date window. - * @private - */ - handler.prototype.getIndexesInWindow_ = function(series, dateWindow) { - var firstIdx = 0, lastIdx = series.length - 1; - if (dateWindow) { - var idx = 0; - var low = dateWindow[0]; - var high = dateWindow[1]; +/** + * Helper method that returns the first and the last index of the given series + * that lie inside the given dateWindow. + * + * @param {!Array.<[!number,?number,?]>} series The series in the unified + * data format where series[i] = [x,y,{extras}]. + * @param {!Array.} dateWindow The x-value range to display with + * the format: [min,max]. + * @return {!Array.<[!number,?number,?]>} The samples of the series that + * are in the given date window. + * @private + */ +handler.prototype.getIndexesInWindow_ = function(series, dateWindow) { + var firstIdx = 0, lastIdx = series.length - 1; + if (dateWindow) { + var idx = 0; + var low = dateWindow[0]; + var high = dateWindow[1]; - // Start from each side of the array to minimize the performance - // needed. - while (idx < series.length - 1 && series[idx][0] < low) { - firstIdx++; - idx++; - } - idx = series.length - 1; - while (idx > 0 && series[idx][0] > high) { - lastIdx--; - idx--; - } + // Start from each side of the array to minimize the performance + // needed. + while (idx < series.length - 1 && series[idx][0] < low) { + firstIdx++; + idx++; } - if (firstIdx <= lastIdx) { - return [ firstIdx, lastIdx ]; - } else { - return [ 0, series.length - 1 ]; + idx = series.length - 1; + while (idx > 0 && series[idx][0] > high) { + lastIdx--; + idx--; } - }; + } + if (firstIdx <= lastIdx) { + return [ firstIdx, lastIdx ]; + } else { + return [ 0, series.length - 1 ]; + } +}; + +/** + * Optimized replacement for parseFloat, which was way too slow when almost + * all values were type number, with few edge cases, none of which were strings. + * @param {?number} val + * @return {number} + * @protected + */ +handler.parseFloat = function(val) { + // parseFloat(null) is NaN + if (val === null) { + return NaN; + } - return handler; + // Assume it's a number or NaN. If it's something else, I'll be shocked. + return val; }; })();