From b4da924345451316aaf4d2d7418b5215ddf33038 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 21 Nov 2016 23:22:22 -0500 Subject: [PATCH 1/2] fix for date histograms with uniform bins and test calc of uniform AND nonuniform bins --- src/traces/histogram/calc.js | 16 ++++-- src/traces/histogram2d/calc.js | 35 +++++++++--- test/jasmine/tests/histogram2d_test.js | 51 ++++++++++++++++++ test/jasmine/tests/histogram_test.js | 73 ++++++++++++++++++++++++++ 4 files changed, 164 insertions(+), 11 deletions(-) diff --git a/src/traces/histogram/calc.js b/src/traces/histogram/calc.js index 5b43a8ad36c..5248979368a 100644 --- a/src/traces/histogram/calc.js +++ b/src/traces/histogram/calc.js @@ -47,8 +47,8 @@ module.exports = function calc(gd, trace) { } var binspec = trace[maindata + 'bins'], - allbins = typeof binspec.size === 'string', - bins = allbins ? [] : binspec, + nonuniformBins = typeof binspec.size === 'string', + bins = nonuniformBins ? [] : binspec, // make the empty bin array i2, binend, @@ -85,13 +85,23 @@ module.exports = function calc(gd, trace) { size.push(sizeinit); // nonuniform bins (like months) we need to search, // rather than straight calculate the bin we're in - if(allbins) bins.push(i); + if(nonuniformBins) bins.push(i); // nonuniform bins also need nonuniform normalization factors if(densitynorm) inc.push(1 / (i2 - i)); if(doavg) counts.push(0); i = i2; } + // for date axes we need bin bounds to be calcdata. For nonuniform bins + // we already have this, but uniform with start/end/size they're still strings. + if(!nonuniformBins && pa.type === 'date') { + bins = { + start: pa.r2c(bins.start), + end: pa.r2c(bins.end), + size: bins.size + }; + } + var nMax = size.length; // bin the data for(i = 0; i < pos0.length; i++) { diff --git a/src/traces/histogram2d/calc.js b/src/traces/histogram2d/calc.js index 79a42406f61..1f524e34ee0 100644 --- a/src/traces/histogram2d/calc.js +++ b/src/traces/histogram2d/calc.js @@ -64,8 +64,10 @@ module.exports = function calc(gd, trace) { z = []; var onecol = [], zerocol = [], - xbins = (typeof(trace.xbins.size) === 'string') ? [] : trace.xbins, - ybins = (typeof(trace.xbins.size) === 'string') ? [] : trace.ybins, + nonuniformBinsX = (typeof(trace.xbins.size) === 'string'), + nonuniformBinsY = (typeof(trace.ybins.size) === 'string'), + xbins = nonuniformBinsX ? [] : trace.xbins, + ybins = nonuniformBinsY ? [] : trace.ybins, total = 0, n, m, @@ -103,10 +105,10 @@ module.exports = function calc(gd, trace) { for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size)) { onecol.push(sizeinit); - if(Array.isArray(xbins)) xbins.push(i); + if(nonuniformBinsX) xbins.push(i); if(doavg) zerocol.push(0); } - if(Array.isArray(xbins)) xbins.push(i); + if(nonuniformBinsX) xbins.push(i); var nx = onecol.length; x0 = trace.xbins.start; @@ -121,10 +123,10 @@ module.exports = function calc(gd, trace) { for(i = binStart; i < binEnd; i = Axes.tickIncrement(i, binspec.size)) { z.push(onecol.concat()); - if(Array.isArray(ybins)) ybins.push(i); + if(nonuniformBinsY) ybins.push(i); if(doavg) counts.push(zerocol.concat()); } - if(Array.isArray(ybins)) ybins.push(i); + if(nonuniformBinsY) ybins.push(i); var ny = z.length; y0 = trace.ybins.start; @@ -134,15 +136,32 @@ module.exports = function calc(gd, trace) { if(densitynorm) { xinc = onecol.map(function(v, i) { - if(Array.isArray(xbins)) return 1 / (xbins[i + 1] - xbins[i]); + if(nonuniformBinsX) return 1 / (xbins[i + 1] - xbins[i]); return 1 / dx; }); yinc = z.map(function(v, i) { - if(Array.isArray(ybins)) return 1 / (ybins[i + 1] - ybins[i]); + if(nonuniformBinsY) return 1 / (ybins[i + 1] - ybins[i]); return 1 / dy; }); } + // for date axes we need bin bounds to be calcdata. For nonuniform bins + // we already have this, but uniform with start/end/size they're still strings. + if(!nonuniformBinsX && xa.type === 'date') { + xbins = { + start: xa.r2c(xbins.start), + end: xa.r2c(xbins.end), + size: xbins.size + }; + } + if(!nonuniformBinsY && ya.type === 'date') { + ybins = { + start: ya.r2c(ybins.start), + end: ya.r2c(ybins.end), + size: ybins.size + }; + } + // put data into bins for(i = 0; i < serieslen; i++) { diff --git a/test/jasmine/tests/histogram2d_test.js b/test/jasmine/tests/histogram2d_test.js index b2dd0ebd28e..204568e03c6 100644 --- a/test/jasmine/tests/histogram2d_test.js +++ b/test/jasmine/tests/histogram2d_test.js @@ -1,4 +1,8 @@ +var Plots = require('@src/plots/plots'); +var Lib = require('@src/lib'); + var supplyDefaults = require('@src/traces/histogram2d/defaults'); +var calc = require('@src/traces/histogram2d/calc'); describe('Test histogram2d', function() { @@ -56,4 +60,51 @@ describe('Test histogram2d', function() { }); + + describe('calc', function() { + function _calc(opts) { + var base = { type: 'histogram2d' }, + trace = Lib.extendFlat({}, base, opts), + gd = { data: [trace] }; + + Plots.supplyDefaults(gd); + var fullTrace = gd._fullData[0]; + + var out = calc(gd, fullTrace); + delete out.trace; + return out; + } + + // remove tzOffset when we move to UTC + var oneDay = 24 * 3600000; + + it('should handle both uniform and nonuniform date bins', function() { + var out = _calc({ + x: ['1970-01-01', '1970-01-01', '1970-01-02', '1970-01-04'], + nbinsx: 4, + y: ['1970-01-01', '1970-01-01', '1971-01-01', '1973-01-01'], + nbinsy: 4 + }); + + expect(out.x0).toBe('1970-01-01'); + expect(out.dx).toBe(oneDay); + + // TODO: even though the binning is done on non-uniform bins, + // the display makes them linear (using only y0 and dy) + // when we sort out https://github.com/plotly/plotly.js/issues/1151 + // lets also make it display the bins with nonuniform size, + // and ensure we don't generate an extra bin on the end (see + // first row of z below) + expect(out.y0).toBe('1969-07-02 15:24'); + expect(out.dy).toBe(365.2 * oneDay); + + expect(out.z).toEqual([ + [0, 0, 0, 0], + [2, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 0, 0], + [0, 0, 0, 1] + ]); + }); + }); }); diff --git a/test/jasmine/tests/histogram_test.js b/test/jasmine/tests/histogram_test.js index 3a441fbb7a0..5263835f67f 100644 --- a/test/jasmine/tests/histogram_test.js +++ b/test/jasmine/tests/histogram_test.js @@ -1,4 +1,8 @@ +var Plots = require('@src/plots/plots'); +var Lib = require('@src/lib'); + var supplyDefaults = require('@src/traces/histogram/defaults'); +var calc = require('@src/traces/histogram/calc'); describe('Test histogram', function() { @@ -127,4 +131,73 @@ describe('Test histogram', function() { }); + + describe('calc', function() { + function _calc(opts) { + var base = { type: 'histogram' }, + trace = Lib.extendFlat({}, base, opts), + gd = { data: [trace] }; + + Plots.supplyDefaults(gd); + var fullTrace = gd._fullData[0]; + + var out = calc(gd, fullTrace); + delete out[0].trace; + return out; + } + + // remove tzOffset when we move to UTC + var tzOffset = (new Date(1970, 0, 1)).getTimezoneOffset() * 60000, + oneDay = 24 * 3600000; + + it('should handle auto dates with nonuniform (month) bins', function() { + var out = _calc({ + x: ['1970-01-01', '1970-01-01', '1971-01-01', '1973-01-01'], + nbinsx: 4 + }); + + // TODO: https://github.com/plotly/plotly.js/issues/1151 + // these bins should shift when we implement that + + // note that x1-x0 = 365 days, but the others are 365.5 days + + // ALSO: this gives half-day gaps between all but the first two + // bars. Now that we have explicit per-bar positioning, perhaps + // we should fill the space, rather than insisting on equal-width + // bars? + var x0 = tzOffset + 15768000000, + x1 = x0 + oneDay * 365, + x2 = x1 + oneDay * 365.5, + x3 = x2 + oneDay * 365.5; + + expect(out).toEqual([ + // full calcdata has x and y too (and t in the first one), + // but those come later from setPositions. + {b: 0, p: x0, s: 2}, + {b: 0, p: x1, s: 1}, + {b: 0, p: x2, s: 0}, + {b: 0, p: x3, s: 1} + ]); + }); + + it('should handle auto dates with uniform (day) bins', function() { + var out = _calc({ + x: ['1970-01-01', '1970-01-01', '1970-01-02', '1970-01-04'], + nbinsx: 4 + }); + + var x0 = tzOffset, + x1 = x0 + oneDay, + x2 = x1 + oneDay, + x3 = x2 + oneDay; + + expect(out).toEqual([ + {b: 0, p: x0, s: 2}, + {b: 0, p: x1, s: 1}, + {b: 0, p: x2, s: 0}, + {b: 0, p: x3, s: 1} + ]); + }); + + }); }); From 2a57525d25847a208d2948e6cc3a8f6136646b92 Mon Sep 17 00:00:00 2001 From: alexcjohnson Date: Mon, 21 Nov 2016 23:41:30 -0500 Subject: [PATCH 2/2] temp fix for histogram2d test in UTC environment --- test/jasmine/tests/histogram2d_test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/jasmine/tests/histogram2d_test.js b/test/jasmine/tests/histogram2d_test.js index 204568e03c6..45620c68a92 100644 --- a/test/jasmine/tests/histogram2d_test.js +++ b/test/jasmine/tests/histogram2d_test.js @@ -75,8 +75,10 @@ describe('Test histogram2d', function() { return out; } - // remove tzOffset when we move to UTC - var oneDay = 24 * 3600000; + // remove tzJan/tzJuly when we move to UTC + var oneDay = 24 * 3600000, + tzJan = (new Date(1970, 0, 1)).getTimezoneOffset(), + tzJuly = (new Date(1970, 6, 1)).getTimezoneOffset(); it('should handle both uniform and nonuniform date bins', function() { var out = _calc({ @@ -95,7 +97,7 @@ describe('Test histogram2d', function() { // lets also make it display the bins with nonuniform size, // and ensure we don't generate an extra bin on the end (see // first row of z below) - expect(out.y0).toBe('1969-07-02 15:24'); + expect(out.y0).toBe(tzJan === tzJuly ? '1969-07-02 14:24' : '1969-07-02 15:24'); expect(out.dy).toBe(365.2 * oneDay); expect(out.z).toEqual([