diff --git a/src/plots/cartesian/align_period.js b/src/plots/cartesian/align_period.js new file mode 100644 index 00000000000..abc833490cf --- /dev/null +++ b/src/plots/cartesian/align_period.js @@ -0,0 +1,90 @@ +/** +* Copyright 2012-2020, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var isNumeric = require('fast-isnumeric'); +var Lib = require('../../lib'); +var dateTime2ms = Lib.dateTime2ms; +var incrementMonth = Lib.incrementMonth; +var constants = require('../../constants/numerical'); +var ONEAVGMONTH = constants.ONEAVGMONTH; + +module.exports = function alignPeriod(trace, ax, axLetter, vals) { + if(ax.type !== 'date') return vals; + + var alignment = trace[axLetter + 'periodalignment']; + if(!alignment) return vals; + + var period = trace[axLetter + 'period']; + var mPeriod; + if(isNumeric(period)) { + period = +period; + if(period <= 0) return vals; + } else if(typeof period === 'string' && period.charAt(0) === 'M') { + var n = +(period.substring(1)); + if(n > 0 && Math.round(n) === n) { + mPeriod = n; + } else return vals; + } + + var calendar = ax.calendar; + + var isStart = 'start' === alignment; + // var isMiddle = 'middle' === alignment; + var isEnd = 'end' === alignment; + + var period0 = trace[axLetter + 'period0']; + var base = dateTime2ms(period0, calendar) || 0; + + var newVals = []; + var len = vals.length; + for(var i = 0; i < len; i++) { + var v = vals[i]; + + var nEstimated, startTime, endTime; + if(mPeriod) { + // guess at how many periods away from base we are + nEstimated = Math.round((v - base) / (mPeriod * ONEAVGMONTH)); + endTime = incrementMonth(base, mPeriod * nEstimated, calendar); + + // iterate to get the exact bounds before and after v + // there may be ways to make this faster, but most of the time + // we'll only execute each loop zero or one time. + while(endTime > v) { + endTime = incrementMonth(endTime, -mPeriod, calendar); + } + while(endTime <= v) { + endTime = incrementMonth(endTime, mPeriod, calendar); + } + + // now we know endTime is the boundary immediately after v + // so startTime is obtained by incrementing backward one period. + startTime = incrementMonth(endTime, -mPeriod, calendar); + } else { // case of ms + nEstimated = Math.round((v - base) / period); + endTime = base + nEstimated * period; + + while(endTime > v) { + endTime -= period; + } + while(endTime <= v) { + endTime += period; + } + + startTime = endTime - period; + } + + newVals[i] = ( + isStart ? startTime : + isEnd ? endTime : + (startTime + endTime) / 2 + ); + } + return newVals; +}; diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index 1100cef9340..c477b13c395 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -30,7 +30,6 @@ var ONEMIN = numConstants.ONEMIN; var ONESEC = numConstants.ONESEC; var axisIds = require('./axis_ids'); - var constants = require('./constants'); var HOUR_PATTERN = constants.HOUR_PATTERN; var WEEKDAY_PATTERN = constants.WEEKDAY_PATTERN; diff --git a/src/traces/bar/attributes.js b/src/traces/bar/attributes.js index b922b4a86d8..fd7572d7eef 100644 --- a/src/traces/bar/attributes.js +++ b/src/traces/bar/attributes.js @@ -59,6 +59,13 @@ module.exports = { y0: scatterAttrs.y0, dy: scatterAttrs.dy, + xperiod: scatterAttrs.xperiod, + yperiod: scatterAttrs.yperiod, + xperiod0: scatterAttrs.xperiod0, + yperiod0: scatterAttrs.yperiod0, + xperiodalignment: scatterAttrs.xperiodalignment, + yperiodalignment: scatterAttrs.yperiodalignment, + text: scatterAttrs.text, texttemplate: texttemplateAttrs({editType: 'plot'}, { keys: constants.eventDataKeys diff --git a/src/traces/bar/calc.js b/src/traces/bar/calc.js index 67b994e98cb..218808f5212 100644 --- a/src/traces/bar/calc.js +++ b/src/traces/bar/calc.js @@ -9,6 +9,7 @@ 'use strict'; var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var hasColorscale = require('../../components/colorscale/helpers').hasColorscale; var colorscaleCalc = require('../../components/colorscale/calc'); var arraysToCalcdata = require('./arrays_to_calcdata'); @@ -17,18 +18,23 @@ var calcSelection = require('../scatter/calc_selection'); module.exports = function calc(gd, trace) { var xa = Axes.getFromId(gd, trace.xaxis || 'x'); var ya = Axes.getFromId(gd, trace.yaxis || 'y'); - var size, pos; + var size, pos, origPos; var sizeOpts = { msUTC: !!(trace.base || trace.base === 0) }; + var hasPeriod; if(trace.orientation === 'h') { size = xa.makeCalcdata(trace, 'x', sizeOpts); - pos = ya.makeCalcdata(trace, 'y'); + origPos = ya.makeCalcdata(trace, 'y'); + pos = alignPeriod(trace, ya, 'y', origPos); + hasPeriod = !!trace.yperiodalignment; } else { size = ya.makeCalcdata(trace, 'y', sizeOpts); - pos = xa.makeCalcdata(trace, 'x'); + origPos = xa.makeCalcdata(trace, 'x'); + pos = alignPeriod(trace, xa, 'x', origPos); + hasPeriod = !!trace.xperiodalignment; } // create the "calculated data" to plot @@ -39,6 +45,10 @@ module.exports = function calc(gd, trace) { for(var i = 0; i < serieslen; i++) { cd[i] = { p: pos[i], s: size[i] }; + if(hasPeriod) { + cd[i].orig_p = origPos[i]; // used by hover + } + if(trace.ids) { cd[i].id = String(trace.ids[i]); } diff --git a/src/traces/bar/defaults.js b/src/traces/bar/defaults.js index d6284ff5351..f9393cf766d 100644 --- a/src/traces/bar/defaults.js +++ b/src/traces/bar/defaults.js @@ -13,6 +13,7 @@ var Color = require('../../components/color'); var Registry = require('../../registry'); var handleXYDefaults = require('../scatter/xy_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleStyleDefaults = require('./style_defaults'); var getAxisGroup = require('../../plots/cartesian/axis_ids').getAxisGroup; var attributes = require('./attributes'); @@ -30,6 +31,8 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v'); coerce('base'); coerce('offset'); diff --git a/src/traces/bar/hover.js b/src/traces/bar/hover.js index 8cb3d926540..7ad4a82c822 100644 --- a/src/traces/bar/hover.js +++ b/src/traces/bar/hover.js @@ -158,7 +158,9 @@ function hoverOnBars(pointData, xval, yval, hovermode) { var extent = t.extents[t.extents.round(di.p)]; pointData[posLetter + '0'] = pa.c2p(isClosest ? minPos(di) : extent[0], true); pointData[posLetter + '1'] = pa.c2p(isClosest ? maxPos(di) : extent[1], true); - pointData[posLetter + 'LabelVal'] = di.p; + + var hasPeriod = di.orig_p !== undefined; + pointData[posLetter + 'LabelVal'] = hasPeriod ? di.orig_p : di.p; pointData.labelLabel = hoverLabelText(pa, pointData[posLetter + 'LabelVal']); pointData.valueLabel = hoverLabelText(sa, pointData[sizeLetter + 'LabelVal']); diff --git a/src/traces/box/attributes.js b/src/traces/box/attributes.js index 91316d919a9..dd6bc37823e 100644 --- a/src/traces/box/attributes.js +++ b/src/traces/box/attributes.js @@ -76,6 +76,13 @@ module.exports = { ].join(' ') }, + xperiod: scatterAttrs.xperiod, + yperiod: scatterAttrs.yperiod, + xperiod0: scatterAttrs.xperiod0, + yperiod0: scatterAttrs.yperiod0, + xperiodalignment: scatterAttrs.xperiodalignment, + yperiodalignment: scatterAttrs.yperiodalignment, + name: { valType: 'string', role: 'info', diff --git a/src/traces/box/calc.js b/src/traces/box/calc.js index 0c9115dc03b..90d40ac9216 100644 --- a/src/traces/box/calc.js +++ b/src/traces/box/calc.js @@ -11,6 +11,7 @@ var isNumeric = require('fast-isnumeric'); var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var Lib = require('../../lib'); var BADNUM = require('../../constants/numerical').BADNUM; @@ -29,19 +30,24 @@ module.exports = function calc(gd, trace) { var valAxis, valLetter; var posAxis, posLetter; + var hasPeriod; if(trace.orientation === 'h') { valAxis = xa; valLetter = 'x'; posAxis = ya; posLetter = 'y'; + hasPeriod = !!trace.yperiodalignment; } else { valAxis = ya; valLetter = 'y'; posAxis = xa; posLetter = 'x'; + hasPeriod = !!trace.xperiodalignment; } - var posArray = getPos(trace, posLetter, posAxis, fullLayout[numKey]); + var allPosArrays = getPosArrays(trace, posLetter, posAxis, fullLayout[numKey]); + var posArray = allPosArrays[0]; + var origPos = allPosArrays[1]; var dv = Lib.distinctVals(posArray); var posDistinct = dv.vals; var dPos = dv.minDiff / 2; @@ -77,6 +83,9 @@ module.exports = function calc(gd, trace) { cdi = {}; cdi.pos = cdi[posLetter] = posi; + if(hasPeriod && origPos) { + cdi.orig_p = origPos[i]; // used by hover + } cdi.q1 = d2c('q1'); cdi.med = d2c('median'); @@ -303,13 +312,15 @@ module.exports = function calc(gd, trace) { // so if you want one box // per trace, set x0 (y0) to the x (y) value or category for this trace // (or set x (y) to a constant array matching y (x)) -function getPos(trace, posLetter, posAxis, num) { +function getPosArrays(trace, posLetter, posAxis, num) { var hasPosArray = posLetter in trace; var hasPos0 = posLetter + '0' in trace; var hasPosStep = 'd' + posLetter in trace; if(hasPosArray || (hasPos0 && hasPosStep)) { - return posAxis.makeCalcdata(trace, posLetter); + var origPos = posAxis.makeCalcdata(trace, posLetter); + var pos = alignPeriod(trace, posAxis, posLetter, origPos); + return [pos, origPos]; } var pos0; @@ -337,7 +348,7 @@ function getPos(trace, posLetter, posAxis, num) { var out = new Array(len); for(var i = 0; i < len; i++) out[i] = pos0c; - return out; + return [out]; } function makeBins(x, dx) { diff --git a/src/traces/box/defaults.js b/src/traces/box/defaults.js index 300053e6e5d..849a3d433da 100644 --- a/src/traces/box/defaults.js +++ b/src/traces/box/defaults.js @@ -11,6 +11,7 @@ var Lib = require('../../lib'); var Registry = require('../../registry'); var Color = require('../../components/color'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleGroupingDefaults = require('../bar/defaults').handleGroupingDefaults; var autoType = require('../../plots/cartesian/axis_autotype'); var attributes = require('./attributes'); @@ -23,6 +24,8 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { handleSampleDefaults(traceIn, traceOut, coerce, layout); if(traceOut.visible === false) return; + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + var hasPreCompStats = traceOut._hasPreCompStats; if(hasPreCompStats) { diff --git a/src/traces/box/hover.js b/src/traces/box/hover.js index 3fee0d1e638..1ad3c705fb8 100644 --- a/src/traces/box/hover.js +++ b/src/traces/box/hover.js @@ -143,7 +143,7 @@ function hoverOnBoxes(pointData, xval, yval, hovermode) { pointData[pLetter + '0'] = pAxis.c2p(di.pos + t.bPos - boxDeltaNeg, true); pointData[pLetter + '1'] = pAxis.c2p(di.pos + t.bPos + boxDeltaPos, true); - pointData[pLetter + 'LabelVal'] = di.pos; + pointData[pLetter + 'LabelVal'] = di.orig_p !== undefined ? di.orig_p : di.pos; var spikePosAttr = pLetter + 'Spike'; pointData.spikeDistance = dxy(di) * spikePseudoDistance / hoverPseudoDistance; @@ -257,14 +257,16 @@ function hoverOnPoints(pointData, xval, yval) { hovertemplate: trace.hovertemplate }); + var origPos = di.orig_p; + var pos = origPos !== undefined ? origPos : di.pos; var pa; if(trace.orientation === 'h') { pa = ya; closePtData.xLabelVal = pt.x; - closePtData.yLabelVal = di.pos; + closePtData.yLabelVal = pos; } else { pa = xa; - closePtData.xLabelVal = di.pos; + closePtData.xLabelVal = pos; closePtData.yLabelVal = pt.y; } diff --git a/src/traces/candlestick/attributes.js b/src/traces/candlestick/attributes.js index 754096a3d45..1a9f491dc10 100644 --- a/src/traces/candlestick/attributes.js +++ b/src/traces/candlestick/attributes.js @@ -27,6 +27,10 @@ function directionAttrs(lineColorDefault) { } module.exports = { + xperiod: OHLCattrs.xperiod, + xperiod0: OHLCattrs.xperiod0, + xperiodalignment: OHLCattrs.xperiodalignment, + x: OHLCattrs.x, open: OHLCattrs.open, high: OHLCattrs.high, diff --git a/src/traces/candlestick/calc.js b/src/traces/candlestick/calc.js index 25f4b1f10aa..eb06d1916aa 100644 --- a/src/traces/candlestick/calc.js +++ b/src/traces/candlestick/calc.js @@ -10,6 +10,7 @@ var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var calcCommon = require('../ohlc/calc').calcCommon; @@ -18,9 +19,10 @@ module.exports = function(gd, trace) { var xa = Axes.getFromId(gd, trace.xaxis); var ya = Axes.getFromId(gd, trace.yaxis); - var x = xa.makeCalcdata(trace, 'x'); + var origX = xa.makeCalcdata(trace, 'x'); + var x = alignPeriod(trace, xa, 'x', origX); - var cd = calcCommon(gd, trace, x, ya, ptFunc); + var cd = calcCommon(gd, trace, origX, x, ya, ptFunc); if(cd.length) { Lib.extendFlat(cd[0].t, { diff --git a/src/traces/candlestick/defaults.js b/src/traces/candlestick/defaults.js index 244cfafa768..200f21c98ed 100644 --- a/src/traces/candlestick/defaults.js +++ b/src/traces/candlestick/defaults.js @@ -12,6 +12,7 @@ var Lib = require('../../lib'); var Color = require('../../components/color'); var handleOHLC = require('../ohlc/ohlc_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var attributes = require('./attributes'); module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { @@ -25,6 +26,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce, {x: true}); + coerce('line.width'); handleDirection(traceIn, traceOut, coerce, 'increasing'); diff --git a/src/traces/contour/attributes.js b/src/traces/contour/attributes.js index 2d44f30d4df..66ca08583ce 100644 --- a/src/traces/contour/attributes.js +++ b/src/traces/contour/attributes.js @@ -31,6 +31,14 @@ module.exports = extendFlat({ y: heatmapAttrs.y, y0: heatmapAttrs.y0, dy: heatmapAttrs.dy, + + xperiod: heatmapAttrs.xperiod, + yperiod: heatmapAttrs.yperiod, + xperiod0: scatterAttrs.xperiod0, + yperiod0: scatterAttrs.yperiod0, + xperiodalignment: heatmapAttrs.xperiodalignment, + yperiodalignment: heatmapAttrs.yperiodalignment, + text: heatmapAttrs.text, hovertext: heatmapAttrs.hovertext, transpose: heatmapAttrs.transpose, diff --git a/src/traces/contour/defaults.js b/src/traces/contour/defaults.js index b0489275155..a25249b8495 100644 --- a/src/traces/contour/defaults.js +++ b/src/traces/contour/defaults.js @@ -11,6 +11,7 @@ var Lib = require('../../lib'); var handleXYZDefaults = require('../heatmap/xyz_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleConstraintDefaults = require('./constraint_defaults'); var handleContoursDefaults = require('./contours_defaults'); var handleStyleDefaults = require('./style_defaults'); @@ -32,6 +33,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('text'); coerce('hovertext'); coerce('hovertemplate'); diff --git a/src/traces/funnel/attributes.js b/src/traces/funnel/attributes.js index e18309dd7f6..de06d0786ae 100644 --- a/src/traces/funnel/attributes.js +++ b/src/traces/funnel/attributes.js @@ -25,6 +25,13 @@ module.exports = { y0: barAttrs.y0, dy: barAttrs.dy, + xperiod: barAttrs.xperiod, + yperiod: barAttrs.yperiod, + xperiod0: barAttrs.xperiod0, + yperiod0: barAttrs.yperiod0, + xperiodalignment: barAttrs.xperiodalignment, + yperiodalignment: barAttrs.yperiodalignment, + hovertext: barAttrs.hovertext, hovertemplate: hovertemplateAttrs({}, { keys: constants.eventDataKeys diff --git a/src/traces/funnel/calc.js b/src/traces/funnel/calc.js index 7f182e5ea26..468fb1a6153 100644 --- a/src/traces/funnel/calc.js +++ b/src/traces/funnel/calc.js @@ -9,6 +9,7 @@ 'use strict'; var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var arraysToCalcdata = require('./arrays_to_calcdata'); var calcSelection = require('../scatter/calc_selection'); var BADNUM = require('../../constants/numerical').BADNUM; @@ -16,14 +17,19 @@ var BADNUM = require('../../constants/numerical').BADNUM; module.exports = function calc(gd, trace) { var xa = Axes.getFromId(gd, trace.xaxis || 'x'); var ya = Axes.getFromId(gd, trace.yaxis || 'y'); - var size, pos, i, cdi; + var size, pos, origPos, i, cdi; + var hasPeriod; if(trace.orientation === 'h') { size = xa.makeCalcdata(trace, 'x'); - pos = ya.makeCalcdata(trace, 'y'); + origPos = ya.makeCalcdata(trace, 'y'); + pos = alignPeriod(trace, ya, 'y', origPos); + hasPeriod = !!trace.yperiodalignment; } else { size = ya.makeCalcdata(trace, 'y'); - pos = xa.makeCalcdata(trace, 'x'); + origPos = xa.makeCalcdata(trace, 'x'); + pos = alignPeriod(trace, xa, 'x', origPos); + hasPeriod = !!trace.xperiodalignment; } // create the "calculated data" to plot @@ -55,6 +61,10 @@ module.exports = function calc(gd, trace) { trace._base[i] = -0.5 * cdi.s; + if(hasPeriod) { + cd[i].orig_p = origPos[i]; // used by hover + } + if(trace.ids) { cdi.id = String(trace.ids[i]); } diff --git a/src/traces/funnel/defaults.js b/src/traces/funnel/defaults.js index 4d1ea4b47e2..acf9f65083b 100644 --- a/src/traces/funnel/defaults.js +++ b/src/traces/funnel/defaults.js @@ -13,6 +13,7 @@ var Lib = require('../../lib'); var handleGroupingDefaults = require('../bar/defaults').handleGroupingDefaults; var handleText = require('../bar/defaults').handleText; var handleXYDefaults = require('../scatter/xy_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var attributes = require('./attributes'); var Color = require('../../components/color'); @@ -27,6 +28,8 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('orientation', (traceOut.y && !traceOut.x) ? 'v' : 'h'); coerce('offset'); coerce('width'); diff --git a/src/traces/heatmap/attributes.js b/src/traces/heatmap/attributes.js index c4e9770930f..d6c6093112c 100644 --- a/src/traces/heatmap/attributes.js +++ b/src/traces/heatmap/attributes.js @@ -29,6 +29,13 @@ module.exports = extendFlat({ y0: extendFlat({}, scatterAttrs.y0, {impliedEdits: {ytype: 'scaled'}}), dy: extendFlat({}, scatterAttrs.dy, {impliedEdits: {ytype: 'scaled'}}), + xperiod: extendFlat({}, scatterAttrs.xperiod, {impliedEdits: {xtype: 'scaled'}}), + yperiod: extendFlat({}, scatterAttrs.yperiod, {impliedEdits: {ytype: 'scaled'}}), + xperiod0: extendFlat({}, scatterAttrs.xperiod0, {impliedEdits: {xtype: 'scaled'}}), + yperiod0: extendFlat({}, scatterAttrs.yperiod0, {impliedEdits: {ytype: 'scaled'}}), + xperiodalignment: extendFlat({}, scatterAttrs.xperiodalignment, {impliedEdits: {xtype: 'scaled'}}), + yperiodalignment: extendFlat({}, scatterAttrs.yperiodalignment, {impliedEdits: {ytype: 'scaled'}}), + text: { valType: 'data_array', editType: 'calc', diff --git a/src/traces/heatmap/calc.js b/src/traces/heatmap/calc.js index 0f8f0c50f95..5e83bfb8d00 100644 --- a/src/traces/heatmap/calc.js +++ b/src/traces/heatmap/calc.js @@ -11,6 +11,7 @@ var Registry = require('../../registry'); var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var histogram2dCalc = require('../histogram2d/calc'); var colorscaleCalc = require('../../components/colorscale/calc'); @@ -30,15 +31,9 @@ module.exports = function calc(gd, trace) { var isHist = Registry.traceIs(trace, 'histogram'); var isGL2D = Registry.traceIs(trace, 'gl2d'); var zsmooth = isContour ? 'best' : trace.zsmooth; - var x; - var x0; - var dx; - var y; - var y0; - var dy; - var z; - var i; - var binned; + var x, x0, dx, origX; + var y, y0, dy, origY; + var z, i, binned; // cancel minimum tick spacings (only applies to bars and boxes) xa._minDtick = 0; @@ -46,12 +41,16 @@ module.exports = function calc(gd, trace) { if(isHist) { binned = histogram2dCalc(gd, trace); + origX = binned.orig_x; x = binned.x; x0 = binned.x0; dx = binned.dx; + + origY = binned.orig_y; y = binned.y; y0 = binned.y0; dy = binned.dy; + z = binned.z; } else { var zIn = trace.z; @@ -61,8 +60,12 @@ module.exports = function calc(gd, trace) { y = trace._y; zIn = trace._z; } else { - x = trace._x = trace.x ? xa.makeCalcdata(trace, 'x') : []; - y = trace._y = trace.y ? ya.makeCalcdata(trace, 'y') : []; + origX = trace.x ? xa.makeCalcdata(trace, 'x') : []; + origY = trace.y ? ya.makeCalcdata(trace, 'y') : []; + x = alignPeriod(trace, xa, 'x', origX); + y = alignPeriod(trace, ya, 'y', origY); + trace._x = x; + trace._y = y; } x0 = trace.x0; @@ -144,6 +147,13 @@ module.exports = function calc(gd, trace) { hovertext: trace._hovertext || trace.hovertext }; + if(trace.xperiodalignment && origX) { + cd0.orig_x = origX; + } + if(trace.yperiodalignment && origY) { + cd0.orig_y = origY; + } + if(xIn && xIn.length === xArray.length - 1) cd0.xCenter = xIn; if(yIn && yIn.length === yArray.length - 1) cd0.yCenter = yIn; diff --git a/src/traces/heatmap/convert_column_xyz.js b/src/traces/heatmap/convert_column_xyz.js index ab5f180f7fa..5818e8d69f0 100644 --- a/src/traces/heatmap/convert_column_xyz.js +++ b/src/traces/heatmap/convert_column_xyz.js @@ -11,11 +11,15 @@ var Lib = require('../../lib'); var BADNUM = require('../../constants/numerical').BADNUM; +var alignPeriod = require('../../plots/cartesian/align_period'); module.exports = function convertColumnData(trace, ax1, ax2, var1Name, var2Name, arrayVarNames) { var colLen = trace._length; var col1 = ax1.makeCalcdata(trace, var1Name); var col2 = ax2.makeCalcdata(trace, var2Name); + col1 = alignPeriod(trace, ax1, var1Name, col1); + col2 = alignPeriod(trace, ax2, var2Name, col2); + var textCol = trace.text; var hasColumnText = (textCol !== undefined && Lib.isArray1D(textCol)); var hoverTextCol = trace.hovertext; diff --git a/src/traces/heatmap/defaults.js b/src/traces/heatmap/defaults.js index b3fda9d8c9b..360ce5531a3 100644 --- a/src/traces/heatmap/defaults.js +++ b/src/traces/heatmap/defaults.js @@ -12,6 +12,7 @@ var Lib = require('../../lib'); var handleXYZDefaults = require('./xyz_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleStyleDefaults = require('./style_defaults'); var colorscaleDefaults = require('../../components/colorscale/defaults'); var attributes = require('./attributes'); @@ -28,6 +29,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('text'); coerce('hovertext'); coerce('hovertemplate'); diff --git a/src/traces/heatmap/hover.js b/src/traces/heatmap/hover.js index 86c2a42c6f3..4f9f5131b9a 100644 --- a/src/traces/heatmap/hover.js +++ b/src/traces/heatmap/hover.js @@ -70,14 +70,21 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay var y0 = ya.c2p(y[ny]); var y1 = ya.c2p(y[ny + 1]); + var _x, _y; if(contour) { + _x = cd0.orig_x || x; + _y = cd0.orig_y || y; + x1 = x0; - xl = x[nx]; + xl = _x[nx]; y1 = y0; - yl = y[ny]; + yl = _y[ny]; } else { - xl = xc ? xc[nx] : ((x[nx] + x[nx + 1]) / 2); - yl = yc ? yc[ny] : ((y[ny] + y[ny + 1]) / 2); + _x = cd0.orig_x || xc || x; + _y = cd0.orig_y || yc || y; + + xl = xc ? _x[nx] : ((_x[nx] + _x[nx + 1]) / 2); + yl = yc ? _y[ny] : ((_y[ny] + _y[ny + 1]) / 2); if(xa && xa.type === 'category') xl = x[nx]; if(ya && ya.type === 'category') yl = y[ny]; diff --git a/src/traces/histogram/attributes.js b/src/traces/histogram/attributes.js index 243a7df93ab..d50e8bd6cd3 100644 --- a/src/traces/histogram/attributes.js +++ b/src/traces/histogram/attributes.js @@ -30,6 +30,13 @@ module.exports = { ].join(' ') }, + xperiod: barAttrs.xperiod, + yperiod: barAttrs.yperiod, + xperiod0: barAttrs.xperiod0, + yperiod0: barAttrs.yperiod0, + xperiodalignment: barAttrs.xperiodalignment, + yperiodalignment: barAttrs.yperiodalignment, + text: extendFlat({}, barAttrs.text, { description: [ 'Sets hover text elements associated with each bar.', diff --git a/src/traces/histogram/calc.js b/src/traces/histogram/calc.js index 540813836aa..6859a747d12 100644 --- a/src/traces/histogram/calc.js +++ b/src/traces/histogram/calc.js @@ -13,6 +13,7 @@ var isNumeric = require('fast-isnumeric'); var Lib = require('../../lib'); var Registry = require('../../registry'); var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var arraysToCalcdata = require('../bar/arrays_to_calcdata'); var binFunctions = require('./bin_functions'); @@ -23,16 +24,19 @@ var getBinSpanLabelRound = require('./bin_label_vals'); function calc(gd, trace) { var pos = []; var size = []; - var pa = Axes.getFromId(gd, trace.orientation === 'h' ? trace.yaxis : trace.xaxis); - var mainData = trace.orientation === 'h' ? 'y' : 'x'; + var isHorizontal = trace.orientation === 'h'; + var pa = Axes.getFromId(gd, isHorizontal ? trace.yaxis : trace.xaxis); + var mainData = isHorizontal ? 'y' : 'x'; var counterData = {x: 'y', y: 'x'}[mainData]; var calendar = trace[mainData + 'calendar']; + var hasPeriod = trace[mainData + 'periodalignment']; var cumulativeSpec = trace.cumulative; var i; var binsAndPos = calcAllAutoBins(gd, trace, pa, mainData); var binSpec = binsAndPos[0]; var pos0 = binsAndPos[1]; + var origPos = binsAndPos[2]; var nonuniformBins = typeof binSpec.size === 'string'; var binEdges = []; @@ -185,13 +189,21 @@ function calc(gd, trace) { b: 0 }; + if(hasPeriod) { + cdi.orig_p = origPos[i]; + } + // setup hover and event data fields, // N.B. pts and "hover" positions ph0/ph1 don't seem to make much sense // for cumulative distributions if(!cumulativeSpec.enabled) { cdi.pts = inputPoints[i]; if(uniqueValsPerBin) { - cdi.ph0 = cdi.ph1 = (inputPoints[i].length) ? pos0[inputPoints[i][0]] : pos[i]; + if(hasPeriod) { + cdi.ph0 = cdi.ph1 = cdi.pts.length ? origPos[cdi.pts[0]] : cdi.orig_p; + } else { + cdi.ph0 = cdi.ph1 = cdi.pts.length ? pos0[cdi.pts[0]] : cdi.p; + } } else { // Defer evaluation of ph(0|1) in crossTraceCalc trace._computePh = true; @@ -233,7 +245,7 @@ function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) { var groupName = trace['_' + mainData + 'bingroup']; var binOpts = fullLayout._histogramBinOpts[groupName]; var isOverlay = fullLayout.barmode === 'overlay'; - var i, traces, tracei, calendar, pos0, autoVals, cumulativeSpec; + var i, traces, tracei, calendar, pos0, origPos, autoVals, cumulativeSpec; var r2c = function(v) { return pa.r2c(v, 0, calendar); }; var c2r = function(v) { return pa.c2r(v, 0, calendar); }; @@ -272,7 +284,9 @@ function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) { if(tracei.visible) { var mainDatai = binOpts.dirs[i]; - pos0 = tracei['_' + mainDatai + 'pos0'] = pa.makeCalcdata(tracei, mainDatai); + origPos = pa.makeCalcdata(tracei, mainDatai); + pos0 = alignPeriod(trace, pa, mainData, origPos); + tracei['_' + mainDatai + 'pos0'] = pos0; allPos = Lib.concat(allPos, pos0); delete tracei['_' + mainData + 'autoBinFinished']; @@ -320,7 +334,7 @@ function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) { // Several single-valued histograms! Stop infinite recursion, // just return an extra flag that tells handleSingleValueOverlays // to sort out this trace too - if(_overlayEdgeCase) return [newBinSpec, pos0, true]; + if(_overlayEdgeCase) return [newBinSpec, pos0, origPos, true]; newBinSpec = handleSingleValueOverlays(gd, trace, pa, mainData, binAttr); } @@ -407,7 +421,7 @@ function calcAllAutoBins(gd, trace, pa, mainData, _overlayEdgeCase) { delete trace[autoBinAttr]; } - return [traceBinOptsCalc, pos0]; + return [traceBinOptsCalc, pos0, origPos, false]; } /* @@ -441,7 +455,7 @@ function handleSingleValueOverlays(gd, trace, pa, mainData, binAttr) { } else { var resulti = calcAllAutoBins(gd, tracei, pa, mainData, true); var binSpeci = resulti[0]; - var isSingleValued = resulti[2]; + var isSingleValued = resulti[3]; // so we can use this result when we get to tracei in the normal // course of events, mark it as done and put _pos0 back diff --git a/src/traces/histogram/defaults.js b/src/traces/histogram/defaults.js index 0c125945e31..0a27f041cc7 100644 --- a/src/traces/histogram/defaults.js +++ b/src/traces/histogram/defaults.js @@ -12,6 +12,7 @@ var Registry = require('../../registry'); var Lib = require('../../lib'); var Color = require('../../components/color'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleStyleDefaults = require('../bar/style_defaults'); var attributes = require('./attributes'); @@ -46,6 +47,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + traceOut._length = len; var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); diff --git a/src/traces/histogram2d/attributes.js b/src/traces/histogram2d/attributes.js index 105b3b8f58e..974f68f55a9 100644 --- a/src/traces/histogram2d/attributes.js +++ b/src/traces/histogram2d/attributes.js @@ -22,6 +22,13 @@ module.exports = extendFlat( x: histogramAttrs.x, y: histogramAttrs.y, + xperiod: histogramAttrs.xperiod, + yperiod: histogramAttrs.yperiod, + xperiod0: histogramAttrs.xperiod0, + yperiod0: histogramAttrs.yperiod0, + xperiodalignment: histogramAttrs.xperiodalignment, + yperiodalignment: histogramAttrs.yperiodalignment, + z: { valType: 'data_array', editType: 'calc', diff --git a/src/traces/histogram2d/calc.js b/src/traces/histogram2d/calc.js index ef8854be255..99d27590a9c 100644 --- a/src/traces/histogram2d/calc.js +++ b/src/traces/histogram2d/calc.js @@ -34,13 +34,21 @@ module.exports = function calc(gd, trace) { var xBinsAndPos = calcAllAutoBins(gd, trace, xa, 'x'); var xBinSpec = xBinsAndPos[0]; var xPos0 = xBinsAndPos[1]; + var origX = xBinsAndPos[2]; var yBinsAndPos = calcAllAutoBins(gd, trace, ya, 'y'); var yBinSpec = yBinsAndPos[0]; var yPos0 = yBinsAndPos[1]; + var origY = yBinsAndPos[2]; var serieslen = trace._length; - if(xPos0.length > serieslen) xPos0.splice(serieslen, xPos0.length - serieslen); - if(yPos0.length > serieslen) yPos0.splice(serieslen, yPos0.length - serieslen); + if(xPos0.length > serieslen) { + xPos0.splice(serieslen, xPos0.length - serieslen); + origX.splice(serieslen, origX.length - serieslen); + } + if(yPos0.length > serieslen) { + yPos0.splice(serieslen, yPos0.length - serieslen); + origY.splice(serieslen, origY.length - serieslen); + } // make the empty bin array & scale the map var z = []; @@ -131,6 +139,8 @@ module.exports = function calc(gd, trace) { var uniqueValsPerY = true; var xVals = new Array(nx); var yVals = new Array(ny); + var xOrig = []; + var yOrig = []; var xGapLow = Infinity; var xGapHigh = Infinity; var yGapLow = Infinity; @@ -141,6 +151,9 @@ module.exports = function calc(gd, trace) { n = Lib.findBin(xi, xbins); m = Lib.findBin(yi, ybins); if(n >= 0 && n < nx && m >= 0 && m < ny) { + if(origX) xOrig[n] = origX[i]; + if(origY) yOrig[m] = origY[i]; + total += binfunc(n, i, z[m], rawCounterData, counts[m]); inputPoints[m][n].push(i); @@ -168,14 +181,18 @@ module.exports = function calc(gd, trace) { } return { + orig_x: xOrig, x: xPos0, xRanges: getRanges(xEdges, uniqueValsPerX && xVals, xGapLow, xGapHigh, xa, xcalendar), x0: x0, dx: dx, + + orig_y: yOrig, y: yPos0, yRanges: getRanges(yEdges, uniqueValsPerY && yVals, yGapLow, yGapHigh, ya, ycalendar), y0: y0, dy: dy, + z: z, pts: inputPoints }; diff --git a/src/traces/histogram2d/defaults.js b/src/traces/histogram2d/defaults.js index 6ba52066c9a..e32e5eb9a80 100644 --- a/src/traces/histogram2d/defaults.js +++ b/src/traces/histogram2d/defaults.js @@ -11,6 +11,7 @@ var Lib = require('../../lib'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleSampleDefaults = require('./sample_defaults'); var handleStyleDefaults = require('../heatmap/style_defaults'); var colorscaleDefaults = require('../../components/colorscale/defaults'); @@ -25,6 +26,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleSampleDefaults(traceIn, traceOut, coerce, layout); if(traceOut.visible === false) return; + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + handleStyleDefaults(traceIn, traceOut, coerce, layout); colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: '', cLetter: 'z'}); coerce('hovertemplate'); diff --git a/src/traces/histogram2d/hover.js b/src/traces/histogram2d/hover.js index b2f67b2f4d4..f7c6fee0fa4 100644 --- a/src/traces/histogram2d/hover.js +++ b/src/traces/histogram2d/hover.js @@ -22,11 +22,23 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, hoverLay var ny = indices[0]; var nx = indices[1]; var cd0 = pointData.cd[0]; - var xRange = cd0.xRanges[nx]; - var yRange = cd0.yRanges[ny]; - pointData.xLabel = hoverLabelText(pointData.xa, xRange[0], xRange[1]); - pointData.yLabel = hoverLabelText(pointData.ya, yRange[0], yRange[1]); + var trace = cd0.trace; + var hasPeriodX = !!trace.xperiodalignment; + var hasPeriodY = !!trace.yperiodalignment; + + if(hasPeriodX) { + pointData.xLabel = hoverLabelText(pointData.xa, cd0.orig_x[nx]); + } else { + var xRange = cd0.xRanges[nx]; + pointData.xLabel = hoverLabelText(pointData.xa, xRange[0], xRange[1]); + } + if(hasPeriodY) { + pointData.yLabel = hoverLabelText(pointData.ya, cd0.orig_x[ny]); + } else { + var yRange = cd0.yRanges[ny]; + pointData.yLabel = hoverLabelText(pointData.ya, yRange[0], yRange[1]); + } return pts; }; diff --git a/src/traces/histogram2dcontour/attributes.js b/src/traces/histogram2dcontour/attributes.js index b315b9cde0d..1395cf664b6 100644 --- a/src/traces/histogram2dcontour/attributes.js +++ b/src/traces/histogram2dcontour/attributes.js @@ -15,6 +15,13 @@ var colorScaleAttrs = require('../../components/colorscale/attributes'); var extendFlat = require('../../lib/extend').extendFlat; module.exports = extendFlat({ + xperiod: histogram2dAttrs.xperiod, + yperiod: histogram2dAttrs.yperiod, + xperiod0: histogram2dAttrs.xperiod0, + yperiod0: histogram2dAttrs.yperiod0, + xperiodalignment: histogram2dAttrs.xperiodalignment, + yperiodalignment: histogram2dAttrs.yperiodalignment, + x: histogram2dAttrs.x, y: histogram2dAttrs.y, z: histogram2dAttrs.z, diff --git a/src/traces/histogram2dcontour/defaults.js b/src/traces/histogram2dcontour/defaults.js index fc3457777d7..ac11538c0ff 100644 --- a/src/traces/histogram2dcontour/defaults.js +++ b/src/traces/histogram2dcontour/defaults.js @@ -11,6 +11,7 @@ var Lib = require('../../lib'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleSampleDefaults = require('../histogram2d/sample_defaults'); var handleContoursDefaults = require('../contour/contours_defaults'); var handleStyleDefaults = require('../contour/style_defaults'); @@ -29,6 +30,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout handleSampleDefaults(traceIn, traceOut, coerce, layout); if(traceOut.visible === false) return; + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + handleContoursDefaults(traceIn, traceOut, coerce, coerce2); handleStyleDefaults(traceIn, traceOut, coerce, layout); coerce('hovertemplate'); diff --git a/src/traces/ohlc/attributes.js b/src/traces/ohlc/attributes.js index 9d36ec76173..c09f86206a8 100644 --- a/src/traces/ohlc/attributes.js +++ b/src/traces/ohlc/attributes.js @@ -34,6 +34,10 @@ function directionAttrs(lineColorDefault) { module.exports = { + xperiod: scatterAttrs.xperiod, + xperiod0: scatterAttrs.xperiod0, + xperiodalignment: scatterAttrs.xperiodalignment, + x: { valType: 'data_array', editType: 'calc+clearAxisTypes', diff --git a/src/traces/ohlc/calc.js b/src/traces/ohlc/calc.js index f90153c537a..c02617c8f0b 100644 --- a/src/traces/ohlc/calc.js +++ b/src/traces/ohlc/calc.js @@ -11,6 +11,7 @@ var Lib = require('../../lib'); var _ = Lib._; var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var BADNUM = require('../../constants/numerical').BADNUM; function calc(gd, trace) { @@ -20,10 +21,12 @@ function calc(gd, trace) { var tickLen = convertTickWidth(gd, xa, trace); var minDiff = trace._minDiff; trace._minDiff = null; + var origX = trace._origX; + trace._origX = null; var x = trace._xcalc; trace._xcalc = null; - var cd = calcCommon(gd, trace, x, ya, ptFunc); + var cd = calcCommon(gd, trace, origX, x, ya, ptFunc); trace._extremes[xa._id] = Axes.findExtremes(xa, x, {vpad: minDiff / 2}); if(cd.length) { @@ -49,7 +52,7 @@ function ptFunc(o, h, l, c) { // shared between OHLC and candlestick // ptFunc makes a calcdata point specific to each trace type, from oi, hi, li, ci -function calcCommon(gd, trace, x, ya, ptFunc) { +function calcCommon(gd, trace, origX, x, ya, ptFunc) { var o = ya.makeCalcdata(trace, 'open'); var h = ya.makeCalcdata(trace, 'high'); var l = ya.makeCalcdata(trace, 'low'); @@ -62,6 +65,8 @@ function calcCommon(gd, trace, x, ya, ptFunc) { var increasing = true; var cPrev = null; + var hasPeriod = !!trace.xperiodalignment; + var cd = []; for(var i = 0; i < x.length; i++) { var xi = x[i]; @@ -90,6 +95,7 @@ function calcCommon(gd, trace, x, ya, ptFunc) { pt.x = pt.pos; pt.y = [li, hi]; + if(hasPeriod) pt.orig_p = origX[i]; // used by hover if(hasTextArray) pt.tx = trace.text[i]; if(hasHovertextArray) pt.htx = trace.hovertext[i]; @@ -121,7 +127,7 @@ function calcCommon(gd, trace, x, ya, ptFunc) { * in all traces; when a trace uses this in its * calc step it deletes _minDiff, so that next calc this is * done again in case the data changed. - * also since we need it here, stash _xcalc on the trace + * also since we need it here, stash _xcalc (and _origX) on the trace */ function convertTickWidth(gd, xa, trace) { var minDiff = trace._minDiff; @@ -143,7 +149,10 @@ function convertTickWidth(gd, xa, trace) { ) { ohlcTracesOnThisXaxis.push(tracei); - var xcalc = xa.makeCalcdata(tracei, 'x'); + var origX = xa.makeCalcdata(tracei, 'x'); + tracei._origX = origX; + + var xcalc = alignPeriod(trace, xa, 'x', origX); tracei._xcalc = xcalc; var _minDiff = Lib.distinctVals(xcalc).minDiff; diff --git a/src/traces/ohlc/defaults.js b/src/traces/ohlc/defaults.js index 78f22e790f0..21f44d89374 100644 --- a/src/traces/ohlc/defaults.js +++ b/src/traces/ohlc/defaults.js @@ -11,6 +11,7 @@ var Lib = require('../../lib'); var handleOHLC = require('./ohlc_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var attributes = require('./attributes'); module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) { @@ -24,6 +25,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce, {x: true}); + coerce('line.width'); coerce('line.dash'); diff --git a/src/traces/ohlc/hover.js b/src/traces/ohlc/hover.js index c11cfeec77b..6f138b41df1 100644 --- a/src/traces/ohlc/hover.js +++ b/src/traces/ohlc/hover.js @@ -88,7 +88,7 @@ function getClosestPoint(pointData, xval, yval, hovermode) { pointData.x0 = xa.c2p(di.pos + centerShift - displayHalfWidth, true); pointData.x1 = xa.c2p(di.pos + centerShift + displayHalfWidth, true); - pointData.xLabelVal = di.pos; + pointData.xLabelVal = di.orig_p !== undefined ? di.orig_p : di.pos; pointData.spikeDistance = dxy(di) * spikePseudoDistance / hoverPseudoDistance; pointData.xSpike = xa.c2p(di.pos, true); diff --git a/src/traces/scatter/attributes.js b/src/traces/scatter/attributes.js index 780ae61a8b3..d7e5ef20e04 100644 --- a/src/traces/scatter/attributes.js +++ b/src/traces/scatter/attributes.js @@ -16,8 +16,55 @@ var dash = require('../../components/drawing/attributes').dash; var Drawing = require('../../components/drawing'); var constants = require('./constants'); + var extendFlat = require('../../lib/extend').extendFlat; +function axisPeriod(axis) { + return { + valType: 'any', + dflt: 0, + role: 'info', + editType: 'calc', + description: [ + 'Only relevant when the axis `type` is *date*.', + 'Sets the period positioning in milliseconds or *M* on the ' + axis + ' axis.', + 'Special values in the form of *M* could be used to declare', + 'the number of months. In this case `n` must be a positive integer.' + ].join(' ') + }; +} + +function axisPeriod0(axis) { + return { + valType: 'any', + role: 'info', + editType: 'calc', + description: [ + 'Only relevant when the axis `type` is *date*.', + 'Sets the base for period positioning in milliseconds or date string on the ' + axis + ' axis.', + 'When `' + axis + 'period` is round number of weeks,', + 'the `' + axis + 'period0` by default would be on a Sunday i.e. 2000-01-02,', + 'otherwise it would be at 2000-01-01.' + ].join(' ') + }; +} + +function axisPeriodAlignment(axis) { + return { + valType: 'enumerated', + values: [ + 'start', 'middle', 'end' + ], + dflt: 'middle', + role: 'style', + editType: 'calc', + description: [ + 'Only relevant when the axis `type` is *date*.', + 'Sets the alignment of data points on the ' + axis + ' axis.' + ].join(' ') + }; +} + module.exports = { x: { valType: 'data_array', @@ -80,6 +127,13 @@ module.exports = { ].join(' ') }, + xperiod: axisPeriod('x'), + yperiod: axisPeriod('y'), + xperiod0: axisPeriod0('x0'), + yperiod0: axisPeriod0('y0'), + xperiodalignment: axisPeriodAlignment('x'), + yperiodalignment: axisPeriodAlignment('y'), + stackgroup: { valType: 'string', role: 'info', diff --git a/src/traces/scatter/calc.js b/src/traces/scatter/calc.js index 7b82a397562..59ae9466434 100644 --- a/src/traces/scatter/calc.js +++ b/src/traces/scatter/calc.js @@ -12,6 +12,7 @@ var isNumeric = require('fast-isnumeric'); var Lib = require('../../lib'); var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var BADNUM = require('../../constants/numerical').BADNUM; var subTypes = require('./subtypes'); @@ -23,8 +24,11 @@ function calc(gd, trace) { var fullLayout = gd._fullLayout; var xa = Axes.getFromId(gd, trace.xaxis || 'x'); var ya = Axes.getFromId(gd, trace.yaxis || 'y'); - var x = xa.makeCalcdata(trace, 'x'); - var y = ya.makeCalcdata(trace, 'y'); + var origX = xa.makeCalcdata(trace, 'x'); + var origY = ya.makeCalcdata(trace, 'y'); + var x = alignPeriod(trace, xa, 'x', origX); + var y = alignPeriod(trace, ya, 'y', origY); + var serieslen = trace._length; var cd = new Array(serieslen); var ids = trace.ids; @@ -55,6 +59,9 @@ function calc(gd, trace) { calcAxisExpansion(gd, trace, xa, ya, x, y, ppad); } + var hasPeriodX = !!trace.xperiodalignment; + var hasPeriodY = !!trace.yperiodalignment; + for(i = 0; i < serieslen; i++) { var cdi = cd[i] = {}; var xValid = isNumeric(x[i]); @@ -62,6 +69,13 @@ function calc(gd, trace) { if(xValid && yValid) { cdi[xAttr] = x[i]; cdi[yAttr] = y[i]; + + if(hasPeriodX) { + cdi.orig_x = origX[i]; // used by hover + } + if(hasPeriodY) { + cdi.orig_y = origY[i]; // used by hover + } } else if(stackGroupOpts && (isV ? xValid : yValid)) { // if we're stacking we need to hold on to all valid positions // even with invalid sizes diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index 08758d76181..a69c5675999 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -15,6 +15,7 @@ var attributes = require('./attributes'); var constants = require('./constants'); var subTypes = require('./subtypes'); var handleXYDefaults = require('./xy_defaults'); +var handlePeriodDefaults = require('./period_defaults'); var handleStackDefaults = require('./stack_defaults'); var handleMarkerDefaults = require('./marker_defaults'); var handleLineDefaults = require('./line_defaults'); @@ -32,6 +33,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout if(!traceOut.visible) return; + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + var stackGroupOpts = handleStackDefaults(traceIn, traceOut, layout, coerce); var defaultMode = !stackGroupOpts && (len < constants.PTS_LINESONLY) ? diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index b481dc3ca19..e6ca5f9bbc1 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -78,8 +78,8 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { // the normalized individual sizes, so that's what I'm doing here // for now. var sizeVal = orientation && (di.sNorm || di.s); - var xLabelVal = (orientation === 'h') ? sizeVal : di.x; - var yLabelVal = (orientation === 'v') ? sizeVal : di.y; + var xLabelVal = (orientation === 'h') ? sizeVal : di.orig_x !== undefined ? di.orig_x : di.x; + var yLabelVal = (orientation === 'v') ? sizeVal : di.orig_y !== undefined ? di.orig_y : di.y; Lib.extendFlat(pointData, { color: getTraceColor(trace, di), diff --git a/src/traces/scatter/period_defaults.js b/src/traces/scatter/period_defaults.js new file mode 100644 index 00000000000..cb02dc0f936 --- /dev/null +++ b/src/traces/scatter/period_defaults.js @@ -0,0 +1,43 @@ +/** +* Copyright 2012-2020, Plotly, Inc. +* All rights reserved. +* +* This source code is licensed under the MIT license found in the +* LICENSE file in the root directory of this source tree. +*/ + +'use strict'; + +var dateTick0 = require('../../lib').dateTick0; +var numConstants = require('../../constants/numerical'); +var ONEWEEK = numConstants.ONEWEEK; + +function getPeriod0Dflt(period, calendar) { + var n = period / ONEWEEK; + return dateTick0(calendar, Math.round(n) === n); +} + +module.exports = function handlePeriodDefaults(traceIn, traceOut, layout, coerce, opts) { + if(!opts) { + opts = { + x: true, + y: true + }; + } + + if(opts.x) { + var xperiod = coerce('xperiod'); + if(xperiod) { + coerce('xperiod0', getPeriod0Dflt(xperiod, traceOut.xcalendar)); + coerce('xperiodalignment'); + } + } + + if(opts.y) { + var yperiod = coerce('yperiod'); + if(yperiod) { + coerce('yperiod0', getPeriod0Dflt(yperiod, traceOut.ycalendar)); + coerce('yperiodalignment'); + } + } +}; diff --git a/src/traces/scattergl/attributes.js b/src/traces/scattergl/attributes.js index a47f87cd08e..bd6747b10da 100644 --- a/src/traces/scattergl/attributes.js +++ b/src/traces/scattergl/attributes.js @@ -28,6 +28,13 @@ var attrs = module.exports = overrideAll({ y0: scatterAttrs.y0, dy: scatterAttrs.dy, + xperiod: scatterAttrs.xperiod, + yperiod: scatterAttrs.yperiod, + xperiod0: scatterAttrs.xperiod0, + yperiod0: scatterAttrs.yperiod0, + xperiodalignment: scatterAttrs.xperiodalignment, + yperiodalignment: scatterAttrs.yperiodalignment, + text: scatterAttrs.text, hovertext: scatterAttrs.hovertext, diff --git a/src/traces/scattergl/calc.js b/src/traces/scattergl/calc.js index df2b4156032..8e07449f9a4 100644 --- a/src/traces/scattergl/calc.js +++ b/src/traces/scattergl/calc.js @@ -13,6 +13,7 @@ var cluster = require('@plotly/point-cluster'); var Lib = require('../../lib'); var AxisIDs = require('../../plots/cartesian/axis_ids'); var findExtremes = require('../../plots/cartesian/autorange').findExtremes; +var alignPeriod = require('../../plots/cartesian/align_period'); var scatterCalc = require('../scatter/calc'); var calcMarkerSize = scatterCalc.calcMarkerSize; @@ -36,8 +37,15 @@ module.exports = function calc(gd, trace) { var stash = {}; var i, xx, yy; - var x = trace._x = xa.makeCalcdata(trace, 'x'); - var y = trace._y = ya.makeCalcdata(trace, 'y'); + var origX = xa.makeCalcdata(trace, 'x'); + var origY = ya.makeCalcdata(trace, 'y'); + var x = alignPeriod(trace, xa, 'x', origX); + var y = alignPeriod(trace, ya, 'y', origY); + trace._x = x; + trace._y = y; + + if(trace.xperiodalignment) trace._origX = origX; + if(trace.yperiodalignment) trace._origY = origY; // we need hi-precision for scatter2d, // regl-scatter2d uses NaNs for bad/missing values diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js index a30becb09ee..9775b3c6669 100644 --- a/src/traces/scattergl/defaults.js +++ b/src/traces/scattergl/defaults.js @@ -16,6 +16,7 @@ var attributes = require('./attributes'); var constants = require('../scatter/constants'); var subTypes = require('../scatter/subtypes'); var handleXYDefaults = require('../scatter/xy_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var handleMarkerDefaults = require('../scatter/marker_defaults'); var handleLineDefaults = require('../scatter/line_defaults'); var handleFillColorDefaults = require('../scatter/fillcolor_defaults'); @@ -34,6 +35,9 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout traceOut.visible = false; return; } + + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; coerce('text'); diff --git a/src/traces/scattergl/hover.js b/src/traces/scattergl/hover.js index 6e7c03a947c..4705f158ec7 100644 --- a/src/traces/scattergl/hover.js +++ b/src/traces/scattergl/hover.js @@ -161,16 +161,19 @@ function calcHover(pointData, x, y, trace) { var fakeCd = {}; fakeCd[pointData.index] = di; + var origX = trace._origX; + var origY = trace._origY; + var pointData2 = Lib.extendFlat({}, pointData, { color: getTraceColor(trace, di), x0: xp - rad, x1: xp + rad, - xLabelVal: di.x, + xLabelVal: origX ? origX[id] : di.x, y0: yp - rad, y1: yp + rad, - yLabelVal: di.y, + yLabelVal: origY ? origY[id] : di.y, cd: fakeCd, distance: minDist, diff --git a/src/traces/violin/attributes.js b/src/traces/violin/attributes.js index 388733c93f4..6a7df22505a 100644 --- a/src/traces/violin/attributes.js +++ b/src/traces/violin/attributes.js @@ -16,6 +16,7 @@ module.exports = { x: boxAttrs.x, x0: boxAttrs.x0, y0: boxAttrs.y0, + name: extendFlat({}, boxAttrs.name, { description: [ 'Sets the trace name.', diff --git a/src/traces/waterfall/attributes.js b/src/traces/waterfall/attributes.js index b722160ec10..d00499e9520 100644 --- a/src/traces/waterfall/attributes.js +++ b/src/traces/waterfall/attributes.js @@ -77,6 +77,13 @@ module.exports = { y0: barAttrs.y0, dy: barAttrs.dy, + xperiod: barAttrs.xperiod, + yperiod: barAttrs.yperiod, + xperiod0: barAttrs.xperiod0, + yperiod0: barAttrs.yperiod0, + xperiodalignment: barAttrs.xperiodalignment, + yperiodalignment: barAttrs.yperiodalignment, + hovertext: barAttrs.hovertext, hovertemplate: hovertemplateAttrs({}, { keys: constants.eventDataKeys diff --git a/src/traces/waterfall/calc.js b/src/traces/waterfall/calc.js index 636099e1d15..7d57bd0ece3 100644 --- a/src/traces/waterfall/calc.js +++ b/src/traces/waterfall/calc.js @@ -9,6 +9,7 @@ 'use strict'; var Axes = require('../../plots/cartesian/axes'); +var alignPeriod = require('../../plots/cartesian/align_period'); var mergeArray = require('../../lib').mergeArray; var calcSelection = require('../scatter/calc_selection'); var BADNUM = require('../../constants/numerical').BADNUM; @@ -24,14 +25,19 @@ function isTotal(a) { module.exports = function calc(gd, trace) { var xa = Axes.getFromId(gd, trace.xaxis || 'x'); var ya = Axes.getFromId(gd, trace.yaxis || 'y'); - var size, pos; + var size, pos, origPos; + var hasPeriod; if(trace.orientation === 'h') { size = xa.makeCalcdata(trace, 'x'); - pos = ya.makeCalcdata(trace, 'y'); + origPos = ya.makeCalcdata(trace, 'y'); + pos = alignPeriod(trace, ya, 'y', origPos); + hasPeriod = !!trace.yperiodalignment; } else { size = ya.makeCalcdata(trace, 'y'); - pos = xa.makeCalcdata(trace, 'x'); + origPos = xa.makeCalcdata(trace, 'x'); + pos = alignPeriod(trace, xa, 'x', origPos); + hasPeriod = !!trace.xperiodalignment; } // create the "calculated data" to plot @@ -85,6 +91,10 @@ module.exports = function calc(gd, trace) { hasTotals = true; } + if(hasPeriod) { + cd[i].orig_p = origPos[i]; // used by hover + } + if(trace.ids) { cdi.id = String(trace.ids[i]); } diff --git a/src/traces/waterfall/defaults.js b/src/traces/waterfall/defaults.js index 022589a99e1..256d8d5a995 100644 --- a/src/traces/waterfall/defaults.js +++ b/src/traces/waterfall/defaults.js @@ -13,6 +13,7 @@ var Lib = require('../../lib'); var handleGroupingDefaults = require('../bar/defaults').handleGroupingDefaults; var handleText = require('../bar/defaults').handleText; var handleXYDefaults = require('../scatter/xy_defaults'); +var handlePeriodDefaults = require('../scatter/period_defaults'); var attributes = require('./attributes'); var Color = require('../../components/color'); var delta = require('../../constants/delta.js'); @@ -38,6 +39,8 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { return; } + handlePeriodDefaults(traceIn, traceOut, layout, coerce); + coerce('measure'); coerce('orientation', (traceOut.x && !traceOut.y) ? 'h' : 'v'); diff --git a/test/image/baselines/gl2d_period_positioning.png b/test/image/baselines/gl2d_period_positioning.png new file mode 100644 index 00000000000..a861bd64403 Binary files /dev/null and b/test/image/baselines/gl2d_period_positioning.png differ diff --git a/test/image/baselines/period_positioning.png b/test/image/baselines/period_positioning.png new file mode 100644 index 00000000000..69cacd47bb3 Binary files /dev/null and b/test/image/baselines/period_positioning.png differ diff --git a/test/image/baselines/period_positioning2.png b/test/image/baselines/period_positioning2.png new file mode 100644 index 00000000000..0e414d77290 Binary files /dev/null and b/test/image/baselines/period_positioning2.png differ diff --git a/test/image/baselines/period_positioning3.png b/test/image/baselines/period_positioning3.png new file mode 100644 index 00000000000..967ddbc0824 Binary files /dev/null and b/test/image/baselines/period_positioning3.png differ diff --git a/test/image/baselines/period_positioning4.png b/test/image/baselines/period_positioning4.png new file mode 100644 index 00000000000..25895a1608e Binary files /dev/null and b/test/image/baselines/period_positioning4.png differ diff --git a/test/image/baselines/period_positioning5.png b/test/image/baselines/period_positioning5.png new file mode 100644 index 00000000000..ed5326bda35 Binary files /dev/null and b/test/image/baselines/period_positioning5.png differ diff --git a/test/image/baselines/period_positioning6.png b/test/image/baselines/period_positioning6.png new file mode 100644 index 00000000000..0a60dc74996 Binary files /dev/null and b/test/image/baselines/period_positioning6.png differ diff --git a/test/image/baselines/period_positioning7.png b/test/image/baselines/period_positioning7.png new file mode 100644 index 00000000000..0192fe0aa8b Binary files /dev/null and b/test/image/baselines/period_positioning7.png differ diff --git a/test/image/baselines/period_positioning8.png b/test/image/baselines/period_positioning8.png new file mode 100644 index 00000000000..80a692e441c Binary files /dev/null and b/test/image/baselines/period_positioning8.png differ diff --git a/test/image/mocks/gl2d_period_positioning.json b/test/image/mocks/gl2d_period_positioning.json new file mode 100644 index 00000000000..358e8872653 --- /dev/null +++ b/test/image/mocks/gl2d_period_positioning.json @@ -0,0 +1,34 @@ +{ + "data": [ + { + "name": "scattergl", + "type": "scattergl", + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01", "2005-01-01", "2006-01-01"], + "y": ["1970-01-01", "1970-02-01", "1970-03-01", "1970-04-01", "1970-05-01", "1970-06-01"], + "xperiod": "M12", + "xperiodalignment": "middle", + "yperiod": "M1", + "yperiodalignment": "middle" + } + ], + "layout": { + "width": 600, + "height": 300, + "margin": { + "t": 30, + "b": 30 + }, + "showlegend": true, + "hovermode": "closest", + + "xaxis": { + "ticklabelmode": "period", + "tickcolor": "black" + }, + + "yaxis": { + "ticklabelmode": "period", + "tickcolor": "black" + } + } +} diff --git a/test/image/mocks/period_positioning.json b/test/image/mocks/period_positioning.json new file mode 100644 index 00000000000..fc6be78312e --- /dev/null +++ b/test/image/mocks/period_positioning.json @@ -0,0 +1,220 @@ +{ + "data": [ + { + "xaxis": "x", + "yaxis": "y", + "name": "bar (v)", + "type": "bar", + "y": [1, 2, 3, 4], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "xperiod": "M12", + "xperiodalignment": "middle" + }, + { + "xaxis": "x", + "yaxis": "y", + "name": "scatter", + "type": "scatter", + "y": [1, 2, 3, 4], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "xperiod": "M12", + "xperiodalignment": "middle" + }, + + { + "xaxis": "x2", + "yaxis": "y2", + "name": "bar (h)", + "type": "bar", + "orientation": "h", + "x": [1, 2, 3, 4], + "y": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "yperiod": "M12", + "yperiodalignment": "middle" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "name": "scatter2", + "type": "scatter", + "x": [1, 2, 3, 4], + "y": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "yperiod": "M12", + "yperiodalignment": "middle" + }, + + { + "xaxis": "x3", + "yaxis": "y3", + "name": "waterfall", + "type": "waterfall", + "y": [4, -3, 2, -1], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "xperiod": "M12", + "xperiodalignment": "middle" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "name": "funnel", + "type": "funnel", + "x": [4, 3, 2, 1], + "y": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "yperiod": "M12", + "yperiodalignment": "middle" + }, + + { + "xaxis": "x5", + "yaxis": "y5", + "name": "heatmap", + "type": "heatmap", + "zsmooth": "best", + "showscale": false, + "showlegend": true, + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "y": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "z": [ + [0, 1, 0, 1], + [1, 0, 1, 0], + [0, 1, 0, 1], + [1, 0, 1, 0] + ], + "xperiod": "M12", + "xperiodalignment": "middle", + "yperiod": "M12", + "yperiodalignment": "middle" + }, + + { + "xaxis": "x6", + "yaxis": "y6", + "name": "contour", + "type": "contour", + "colorscale": "Portland", + "showscale": false, + "showlegend": true, + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "y": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "z": [ + [0, 1, 0, 1], + [1, 0, 1, 0], + [0, 1, 0, 1], + [1, 0, 1, 0] + ], + "xperiod": "M12", + "xperiodalignment": "middle", + "yperiod": "M12", + "yperiodalignment": "middle" + } + ], + "layout": { + "width": 1000, + "height": 500, + "showlegend": true, + "hovermode": "closest", + + "xaxis": { + "ticklabelmode": "period", + "tickcolor": "black", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis2": { + "tickcolor": "black", + "anchor": "y2", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis3": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y3", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis4": { + "anchor": "y4", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis5": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y5", + "domain": [ + 0.7, + 1 + ] + }, + "xaxis6": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y6", + "domain": [ + 0.7, + 1 + ] + }, + + "yaxis": { + "tickcolor": "black", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis2": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis3": { + "tickcolor": "black", + "anchor": "x3", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis4": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis5": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x5", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis6": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x6", + "domain": [ + 0.55, + 1 + ] + } + } +} diff --git a/test/image/mocks/period_positioning2.json b/test/image/mocks/period_positioning2.json new file mode 100644 index 00000000000..727773b4fa4 --- /dev/null +++ b/test/image/mocks/period_positioning2.json @@ -0,0 +1,287 @@ + + +{ + "data": [ + { + "xaxis": "x", + "yaxis": "y", + "name": "histogram(v)", + "type": "histogram", + "x": [ + "2001-01-01", + "2002-01-01", "2002-01-01", + "2003-01-01", "2003-01-01", "2003-01-01", + "2004-01-01", "2004-01-01", "2004-01-01", "2004-01-01" + ], + "xperiod": "M12", + "xperiodalignment": "middle" + }, + + { + "xaxis": "x2", + "yaxis": "y2", + "name": "histogram(h)", + "type": "histogram", + "y": [ + "2001-01-01", + "2002-01-01", "2002-01-01", + "2003-01-01", "2003-01-01", "2003-01-01", + "2004-01-01", "2004-01-01", "2004-01-01", "2004-01-01" + ], + "yperiod": "M12", + "yperiodalignment": "middle" + }, + + { + "xaxis": "x", + "yaxis": "y", + "name": "ohlc", + "type": "ohlc", + "low": [0, 0, 0, 0], + "close": [0.5, 1, 1.5, 2], + "open": [1, 2, 3, 4], + "high": [2, 4, 6, 8], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "xperiod": "M12", + "xperiodalignment": "middle" + }, + + { + "xaxis": "x3", + "yaxis": "y3", + "name": "candlestick", + "type": "candlestick", + "low": [0, 0, 0, 0], + "close": [0.5, 1, 1.5, 2], + "open": [1, 2, 3, 4], + "high": [2, 4, 6, 8], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "xperiod": "M12", + "xperiodalignment": "middle" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "name": "box (v)", + "type": "box", + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "lowerfence": [2, 4, 6, 8], + "q1": [4, 5.5, 7, 8.5], + "median": [6, 7, 8, 9], + "q3": [8, 8.5, 9, 9.5], + "upperfence": [10, 10, 10, 10], + "xperiod": "M12", + "xperiodalignment": "middle" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "name": "box (h)", + "type": "box", + "y": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01"], + "lowerfence": [0, 0, 0, 0], + "q1": [0.5, 1, 1.5, 2], + "median": [1, 2, 3, 4], + "q3": [1.5, 3, 4.5, 6], + "upperfence": [2, 4, 6, 8], + "yperiod": "M12", + "yperiodalignment": "middle" + }, + + { + "xaxis": "x5", + "yaxis": "y5", + "name": "histogram2d", + "type": "histogram2d", + "zsmooth": "best", + "showscale": false, + "showlegend": true, + "x": [ + "2001-01-01", + "2002-01-01", "2002-01-01", + "2003-01-01", "2003-01-01", "2003-01-01", + "2004-01-01", "2004-01-01", "2004-01-01", "2004-01-01" + ], + "y": [ + "2001-01-01", + "2002-01-01", "2002-01-01", + "2003-01-01", "2003-01-01", "2003-01-01", + "2004-01-01", "2004-01-01", "2004-01-01", "2004-01-01" + ], + "z": [ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + ], + "xbins": { + "start": "2001-01-01", + "end": "2005-01-01", + "size": 31557600000 + }, + "ybins": { + "start": "2001-01-01", + "end": "2005-01-01", + "size": 31557600000 + }, + "xperiod": "M12", + "xperiodalignment": "middle", + "yperiod": "M12", + "yperiodalignment": "middle" + }, + + { + "xaxis": "x6", + "yaxis": "y6", + "name": "hist2dcontour", + "type": "histogram2dcontour", + "colorscale": "Portland", + "showscale": false, + "showlegend": true, + "x": [ + "2001-01-01", + "2002-01-01", "2002-01-01", + "2003-01-01", "2003-01-01", "2003-01-01", + "2004-01-01", "2004-01-01", "2004-01-01", "2004-01-01" + ], + "y": [ + "2001-01-01", + "2002-01-01", "2002-01-01", + "2003-01-01", "2003-01-01", "2003-01-01", + "2004-01-01", "2004-01-01", "2004-01-01", "2004-01-01" + ], + "z": [ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + ], + "xbins": { + "start": "2001-01-01", + "end": "2005-01-01", + "size": 31557600000 + }, + "ybins": { + "start": "2001-01-01", + "end": "2005-01-01", + "size": 31557600000 + }, + "xperiod": "M12", + "xperiodalignment": "middle", + "yperiod": "M12", + "yperiodalignment": "middle" + } + ], + "layout": { + "width": 1000, + "height": 500, + "showlegend": true, + "hovermode": "closest", + + "xaxis": { + "rangeslider": {"visible": false}, + "ticklabelmode": "period", + "tickcolor": "black", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis2": { + "tickcolor": "black", + "anchor": "y2", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis3": { + "rangeslider": {"visible": false}, + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y3", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis4": { + "tickcolor": "black", + "anchor": "y4", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis5": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y5", + "domain": [ + 0.7, + 1 + ] + }, + "xaxis6": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y6", + "domain": [ + 0.7, + 1 + ] + }, + + "yaxis": { + "tickcolor": "black", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis2": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis3": { + "tickcolor": "black", + "anchor": "x3", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis4": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis5": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x5", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis6": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "x6", + "domain": [ + 0.55, + 1 + ] + } + } +} diff --git a/test/image/mocks/period_positioning3.json b/test/image/mocks/period_positioning3.json new file mode 100644 index 00000000000..a881dd5313a --- /dev/null +++ b/test/image/mocks/period_positioning3.json @@ -0,0 +1,314 @@ +{ + "data": [ + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M1", + "name": "start (M1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-01", "2001-03-01", "2001-04-01", "2001-05-01", "2001-06-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M1", + "name": "middle (M1)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-01", "2001-03-01", "2001-04-01", "2001-05-01", "2001-06-01"], + "xperiodalignment": "middle" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M1", + "name": "end (M1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-01", "2001-03-01", "2001-04-01", "2001-05-01", "2001-06-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M2", + "name": "start (M2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-03-01", "2001-05-01", "2001-07-01", "2001-09-01", "2001-11-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M2", + "name": "middle (M2)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-03-01", "2001-05-01", "2001-07-01", "2001-09-01", "2001-11-01"], + "xperiodalignment": "middle" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M2", + "name": "end (M2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-03-01", "2001-05-01", "2001-07-01", "2001-09-01", "2001-11-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M3", + "name": "start (M3)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-04-01", "2001-07-01", "2001-10-01", "2002-01-01", "2002-04-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M3", + "name": "middle (M3)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-04-01", "2001-07-01", "2001-10-01", "2002-01-01", "2002-04-01"], + "xperiodalignment": "middle" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M3", + "name": "end (M3)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-04-01", "2001-07-01", "2001-10-01", "2002-01-01", "2002-04-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M6", + "name": "start (M6)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-07-01", "2002-01-01", "2002-07-01", "2003-01-01", "2003-07-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M6", + "name": "middle (M6)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-07-01", "2002-01-01", "2002-07-01", "2003-01-01", "2003-07-01"], + "xperiodalignment": "middle" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M6", + "name": "end (M6)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-07-01", "2002-01-01", "2002-07-01", "2003-01-01", "2003-07-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M12", + "name": "start (M12)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01", "2005-01-01", "2006-01-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M12", + "name": "middle (M12)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01", "2005-01-01", "2006-01-01"], + "xperiodalignment": "middle" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M12", + "name": "end (M12)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2002-01-01", "2003-01-01", "2004-01-01", "2005-01-01", "2006-01-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "start (W1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-08", "2001-01-15", "2001-01-22", "2001-01-29", "2001-02-05"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "bar (W1)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-08", "2001-01-15", "2001-01-22", "2001-01-29", "2001-02-05"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "start (W1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-08", "2001-01-15", "2001-01-22", "2001-01-29", "2001-02-05"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + } + ], + "layout": { + "width": 1000, + "height": 500, + "margin": { + "t": 50, + "l": 50, + "r": 50, + "b": 50 + }, + "showlegend": true, + "hovermode": "closest", + + "xaxis": { + "ticklabelmode": "period", + "tickcolor": "black", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis2": { + "dtick": "M2", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y2", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis3": { + "dtick": "M3", + "tickformat": "Q%q", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y3", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis4": { + "dtick": "M6", + "tickformat": "%Y", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y4", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis5": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y5", + "domain": [ + 0.7, + 1 + ] + }, + "xaxis6": { + "dtick": 604800000, + "tick0": "2001-01-01", + "ticklabelmode": "period", + "tickformat": "W%V", + "tickcolor": "black", + "anchor": "y6", + "domain": [ + 0.7, + 1 + ] + }, + + "yaxis": { + "tickcolor": "black", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis2": { + "tickcolor": "black", + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis3": { + "tickcolor": "black", + "anchor": "x3", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis4": { + "tickcolor": "black", + "anchor": "x4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis5": { + "tickcolor": "black", + "anchor": "x5", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis6": { + "tickcolor": "black", + "anchor": "x6", + "domain": [ + 0.55, + 1 + ] + } + } +} diff --git a/test/image/mocks/period_positioning4.json b/test/image/mocks/period_positioning4.json new file mode 100644 index 00000000000..cffd05e9f2a --- /dev/null +++ b/test/image/mocks/period_positioning4.json @@ -0,0 +1,323 @@ +{ + "data": [ + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M1", + "name": "start (M1)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-15", "2001-02-01", "2001-02-15", "2001-03-01", "2001-03-15"], + "xperiodalignment": "start" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M1", + "name": "middle (M1)", + "marker": { "opacity": 0.5 }, "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-15", "2001-02-01", "2001-02-15", "2001-03-01", "2001-03-15"], + "textposition": "inside", "textangle": 0, "texttemplate": "%{x|%b-%e}", + "xperiodalignment": "middle" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M1", + "name": "end (M1)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-15", "2001-02-01", "2001-02-15", "2001-03-01", "2001-03-15"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M2", + "name": "start (M2)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-01", "2001-03-01", "2001-04-01", "2001-05-01", "2001-06-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M2", + "name": "middle (M2)", + "marker": { "opacity": 0.5 }, "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-01", "2001-03-01", "2001-04-01", "2001-05-01", "2001-06-01"], + "textposition": "inside", "textangle": 0, "texttemplate": "%{x|%b-%e}", + "xperiodalignment": "middle" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M2", + "name": "end (M2)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-01", "2001-03-01", "2001-04-01", "2001-05-01", "2001-06-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M3", + "name": "start (M3)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-15", "2001-04-01", "2001-05-15", "2001-07-01", "2001-08-15"], + "xperiodalignment": "start" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M3", + "name": "middle (M3)", + "marker": { "opacity": 0.5 }, "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-15", "2001-04-01", "2001-05-15", "2001-07-01", "2001-08-15"], + "textposition": "inside", "textangle": 0, "texttemplate": "%{x|%b-%e}", + "xperiodalignment": "middle" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M3", + "name": "end (M3)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-02-15", "2001-04-01", "2001-05-15", "2001-07-01", "2001-08-15"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M6", + "name": "start (M6)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-04-01", "2001-07-01", "2001-10-01", "2002-01-01", "2002-04-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M6", + "name": "middle (M6)", + "marker": { "opacity": 0.5 }, "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-04-01", "2001-07-01", "2001-10-01", "2002-01-01", "2002-04-01"], + "textposition": "inside", "textangle": 0, "texttemplate": "%{x|%b-%e}", + "xperiodalignment": "middle" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M6", + "name": "end (M6)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-04-01", "2001-07-01", "2001-10-01", "2002-01-01", "2002-04-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M12", + "name": "start (M12)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-07-01", "2002-01-01", "2002-07-01", "2003-01-01", "2003-07-01"], + "xperiodalignment": "start" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M12", + "name": "middle (M12)", + "marker": { "opacity": 0.5 }, "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-07-01", "2002-01-01", "2002-07-01", "2003-01-01", "2003-07-01"], + "textposition": "inside", "textangle": 0, "texttemplate": "%{x|%b-%e}", + "xperiodalignment": "middle" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M12", + "name": "end (M12)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-07-01", "2002-01-01", "2002-07-01", "2003-01-01", "2003-07-01"], + "xperiodalignment": "end" + }, + + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "start (W1)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-05", "2001-01-08", "2001-01-12", "2001-01-15", "2001-01-19"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "middle (W1)", + "marker": { "opacity": 0.5 }, "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-05", "2001-01-08", "2001-01-12", "2001-01-15", "2001-01-19"], + "textposition": "inside", "textangle": 0, + "texttemplate": "%{x|%A}", + "hovertemplate": "%{x|%A}, %{y}", + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "end (W1)", + "marker": { "opacity": 0.5 }, "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-05", "2001-01-08", "2001-01-12", "2001-01-15", "2001-01-19"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + } + ], + "layout": { + "barmode": "overlay", + "width": 1000, + "height": 500, + "margin": { + "t": 50, + "l": 50, + "r": 50, + "b": 50 + }, + "showlegend": true, + "hovermode": "closest", + + "xaxis": { + "ticklabelmode": "period", + "tickcolor": "black", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis2": { + "dtick": "M2", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y2", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis3": { + "dtick": "M3", + "tickformat": "Q%q", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y3", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis4": { + "dtick": "M6", + "tickformat": "%Y", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y4", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis5": { + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y5", + "domain": [ + 0.7, + 1 + ] + }, + "xaxis6": { + "dtick": 604800000, + "tick0": "2001-01-01", + "ticklabelmode": "period", + "tickformat": "W%V", + "tickcolor": "black", + "anchor": "y6", + "domain": [ + 0.7, + 1 + ] + }, + + "yaxis": { + "tickcolor": "black", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis2": { + "tickcolor": "black", + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis3": { + "tickcolor": "black", + "anchor": "x3", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis4": { + "tickcolor": "black", + "anchor": "x4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis5": { + "tickcolor": "black", + "anchor": "x5", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis6": { + "tickcolor": "black", + "anchor": "x6", + "domain": [ + 0.55, + 1 + ] + } + } +} diff --git a/test/image/mocks/period_positioning5.json b/test/image/mocks/period_positioning5.json new file mode 100644 index 00000000000..cde9e35ecd1 --- /dev/null +++ b/test/image/mocks/period_positioning5.json @@ -0,0 +1,334 @@ +{ + "data": [ + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M9", + "name": "start (M9)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2001-10", "2002-07", "2003-04", "2004-01", "2004-10"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M9", + "name": "middle (M9)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2001-10", "2002-07", "2003-04", "2004-01", "2004-10"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": "M9", + "name": "end (M9)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2001-10", "2002-07", "2003-04", "2004-01", "2004-10"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + }, + + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M12", + "name": "start (Y1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2002-01", "2003-01", "2004-01", "2005-01", "2006-01"], + "xperiod0": "2002-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M12", + "name": "middle (Y1)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2002-01", "2003-01", "2004-01", "2005-01", "2006-01"], + "xperiod0": "2002-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": "M12", + "name": "end (Y1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2002-01", "2003-01", "2004-01", "2005-01", "2006-01"], + "xperiod0": "2002-01-01", + "xperiodalignment": "end" + }, + + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M18", + "name": "start (Y1.5)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2002-07", "2004-01", "2005-07", "2007-01", "2008-07"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M18", + "name": "middle (Y1.5)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2002-07", "2004-01", "2005-07", "2007-01", "2008-07"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": "M18", + "name": "end (Y1.5)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2002-07", "2004-01", "2005-07", "2007-01", "2008-07"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + }, + + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M24", + "name": "start (Y2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2003-01", "2005-01", "2007-01", "2009-01", "2011-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M24", + "name": "middle (Y2)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2003-01", "2005-01", "2007-01", "2009-01", "2011-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": "M24", + "name": "end (Y2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2003-01", "2005-01", "2007-01", "2009-01", "2011-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + }, + + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M48", + "name": "start (Y4)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2005-01", "2009-01", "2013-01", "2017-01", "2021-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M48", + "name": "middle (Y4)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2005-01", "2009-01", "2013-01", "2017-01", "2021-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": "M48", + "name": "end (Y4)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2005-01", "2009-01", "2013-01", "2017-01", "2021-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + }, + + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": "M120", + "name": "start (Y10)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2011-01", "2021-01", "2031-01", "2041-01", "2051-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": "M120", + "name": "middle (Y10)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2011-01", "2021-01", "2031-01", "2041-01", "2051-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": "M120", + "name": "end (Y5)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01", "2011-01", "2021-01", "2031-01", "2041-01", "2051-01"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + } + + ], + "layout": { + "width": 1000, + "height": 500, + "margin": { + "t": 50, + "l": 50, + "r": 50, + "b": 50 + }, + "showlegend": true, + "hovermode": "closest", + + "xaxis": { + "tick0": "2001-01", + "dtick": "M9", + "ticklabelmode": "period", + "tickcolor": "black", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis2": { + "tick0": "2001-01", + "dtick": "M12", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y2", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis3": { + "tick0": "2001-01", + "dtick": "M18", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y3", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis4": { + "tick0": "2001-01", + "dtick": "M24", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y4", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis5": { + "tick0": "2001-01", + "dtick": "M48", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y5", + "domain": [ + 0.7, + 1 + ] + }, + "xaxis6": { + "tick0": "2001-01", + "dtick": "M120", + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y6", + "domain": [ + 0.7, + 1 + ] + }, + + "yaxis": { + "tickcolor": "black", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis2": { + "tickcolor": "black", + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis3": { + "tickcolor": "black", + "anchor": "x3", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis4": { + "tickcolor": "black", + "anchor": "x4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis5": { + "tickcolor": "black", + "anchor": "x5", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis6": { + "tickcolor": "black", + "anchor": "x6", + "domain": [ + 0.55, + 1 + ] + } + } +} diff --git a/test/image/mocks/period_positioning6.json b/test/image/mocks/period_positioning6.json new file mode 100644 index 00000000000..6f1cfaaae9d --- /dev/null +++ b/test/image/mocks/period_positioning6.json @@ -0,0 +1,333 @@ +{ + "data": [ + { + "xaxis": "x", + "yaxis": "y", + "xperiod": 86400000, + "name": "start (D1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05", "2001-01-06"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": 86400000, + "name": "middle (D1)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05", "2001-01-06"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x", + "yaxis": "y", + "xperiod": 86400000, + "name": "end (D1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05", "2001-01-06"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + }, + + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": 86400000, + "name": "start (D1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2000-12-31", "2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05"], + "xperiod0": "2000-12-31", + "xperiodalignment": "start" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": 86400000, + "name": "middle (D1)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2000-12-31", "2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05"], + "xperiod0": "2000-12-31", + "xperiodalignment": "middle" + }, + { + "xaxis": "x2", + "yaxis": "y2", + "xperiod": 86400000, + "name": "end (D1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2000-12-31", "2001-01-01", "2001-01-02", "2001-01-03", "2001-01-04", "2001-01-05"], + "xperiod0": "2000-12-31", + "xperiodalignment": "end" + }, + + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": 172800000, + "name": "start (D2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-03", "2001-01-05", "2001-01-07", "2001-01-09", "2001-01-11"], + "xperiod0": "2001-01-01", + "xperiodalignment": "start" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": 172800000, + "name": "middle (D2)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-03", "2001-01-05", "2001-01-07", "2001-01-09", "2001-01-11"], + "xperiod0": "2001-01-01", + "xperiodalignment": "middle" + }, + { + "xaxis": "x3", + "yaxis": "y3", + "xperiod": 172800000, + "name": "end (D2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-03", "2001-01-05", "2001-01-07", "2001-01-09", "2001-01-11"], + "xperiod0": "2001-01-01", + "xperiodalignment": "end" + }, + + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": 172800000, + "name": "start (D2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-02", "2001-01-04", "2001-01-06", "2001-01-08", "2001-01-10", "2001-01-12"], + "xperiod0": "2001-01-02", + "xperiodalignment": "start" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": 172800000, + "name": "middle (D2)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-02", "2001-01-04", "2001-01-06", "2001-01-08", "2001-01-10", "2001-01-12"], + "xperiod0": "2001-01-02", + "xperiodalignment": "middle" + }, + { + "xaxis": "x4", + "yaxis": "y4", + "xperiod": 172800000, + "name": "end (D2)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-02", "2001-01-04", "2001-01-06", "2001-01-08", "2001-01-10", "2001-01-12"], + "xperiod0": "2001-01-02", + "xperiodalignment": "end" + }, + + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": 604800000, + "name": "start (W1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-08", "2001-01-15", "2001-01-22", "2001-01-29", "2001-02-05"], + "xperiod0": "2000-12-31", + "xperiodalignment": "start" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": 604800000, + "name": "middle (W1)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-08", "2001-01-15", "2001-01-22", "2001-01-29", "2001-02-05"], + "xperiod0": "2000-12-31", + "xperiodalignment": "middle" + }, + { + "xaxis": "x5", + "yaxis": "y5", + "xperiod": 604800000, + "name": "end (W1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2001-01-01", "2001-01-08", "2001-01-15", "2001-01-22", "2001-01-29", "2001-02-05"], + "xperiod0": "2000-12-31", + "xperiodalignment": "end" + }, + + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "start (W1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2000-12-31", "2001-01-07", "2001-01-14", "2001-01-21", "2001-01-28", "2001-02-04"], + "xperiod0": "2000-12-31", + "xperiodalignment": "start" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "middle (W1)", + "type": "bar", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2000-12-31", "2001-01-07", "2001-01-14", "2001-01-21", "2001-01-28", "2001-02-04"], + "xperiod0": "2000-12-31", + "xperiodalignment": "middle" + }, + { + "xaxis": "x6", + "yaxis": "y6", + "xperiod": 604800000, + "name": "end (W1)", + "type": "scatter", + "y": [1, 2, 3, 4, 5, 6], + "x": ["2000-12-31", "2001-01-07", "2001-01-14", "2001-01-21", "2001-01-28", "2001-02-04"], + "xperiod0": "2000-12-31", + "xperiodalignment": "end" + } + ], + "layout": { + "width": 1000, + "height": 500, + "margin": { + "t": 50, + "l": 50, + "r": 50, + "b": 50 + }, + "showlegend": true, + "hovermode": "closest", + + "xaxis": { + "tick0": "2001-01-01", + "dtick": 86400000, + "ticklabelmode": "period", + "tickcolor": "black", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis2": { + "tick0": "2000-12-31", + "dtick": 86400000, + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y2", + "domain": [ + 0, + 0.3 + ] + }, + "xaxis3": { + "tick0": "2001-01-01", + "dtick": 172800000, + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y3", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis4": { + "tick0": "2001-01-02", + "dtick": 172800000, + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y4", + "domain": [ + 0.35, + 0.65 + ] + }, + "xaxis5": { + "tick0": "2001-01-01", + "dtick": 604800000, + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y5", + "domain": [ + 0.7, + 1 + ] + }, + "xaxis6": { + "tick0": "2000-12-31", + "dtick": 604800000, + "ticklabelmode": "period", + "tickcolor": "black", + "anchor": "y6", + "domain": [ + 0.7, + 1 + ] + }, + + "yaxis": { + "tickcolor": "black", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis2": { + "tickcolor": "black", + "anchor": "x2", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis3": { + "tickcolor": "black", + "anchor": "x3", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis4": { + "tickcolor": "black", + "anchor": "x4", + "domain": [ + 0.55, + 1 + ] + }, + "yaxis5": { + "tickcolor": "black", + "anchor": "x5", + "domain": [ + 0, + 0.45 + ] + }, + "yaxis6": { + "tickcolor": "black", + "anchor": "x6", + "domain": [ + 0.55, + 1 + ] + } + } +} diff --git a/test/image/mocks/period_positioning7.json b/test/image/mocks/period_positioning7.json new file mode 100644 index 00000000000..b901e25b729 --- /dev/null +++ b/test/image/mocks/period_positioning7.json @@ -0,0 +1,401 @@ +{ + "data": [ + { + "xperiod": 604800000, + "xperiod0": "2020-01-06", + "marker": { + "opacity": 0.5, + "line": { + "color": "black", + "width": 1 + } + }, + "name": "start", + "type": "scatter", + "y": [ + 6, + 13, + 20, + 27, + 34, + 41, + 48, + 55, + 62, + 69, + 76, + 83, + 90, + 97, + 104, + 111, + 118, + 125, + 132, + 139, + 146, + 153, + 160, + 167, + 174, + 181, + 188, + 195, + 202, + 209, + 216, + 223, + 230, + 237, + 244, + 251, + 258, + 265, + 272, + 279, + 286, + 293, + 300, + 307, + 314, + 321, + 328, + 335, + 342, + 349, + 356, + 363 + ], + "x": [ + "2020-01-06", + "2020-01-13", + "2020-01-20", + "2020-01-27", + "2020-02-03", + "2020-02-10", + "2020-02-17", + "2020-02-24", + "2020-03-02", + "2020-03-09", + "2020-03-16", + "2020-03-23", + "2020-03-30", + "2020-04-06", + "2020-04-13", + "2020-04-20", + "2020-04-27", + "2020-05-04", + "2020-05-11", + "2020-05-18", + "2020-05-25", + "2020-06-01", + "2020-06-08", + "2020-06-15", + "2020-06-22", + "2020-06-29", + "2020-07-06", + "2020-07-13", + "2020-07-20", + "2020-07-27", + "2020-08-03", + "2020-08-10", + "2020-08-17", + "2020-08-24", + "2020-08-31", + "2020-09-07", + "2020-09-14", + "2020-09-21", + "2020-09-28", + "2020-10-05", + "2020-10-12", + "2020-10-19", + "2020-10-26", + "2020-11-02", + "2020-11-09", + "2020-11-16", + "2020-11-23", + "2020-11-30", + "2020-12-07", + "2020-12-14", + "2020-12-21", + "2020-12-28" + ], + "xperiodalignment": "start" + }, + { + "xperiod": 604800000, + "xperiod0": "2020-01-06", + "marker": { + "opacity": 0.5, + "line": { + "color": "black", + "width": 1 + } + }, + "name": "middle", + "type": "bar", + "y": [ + 6, + 13, + 20, + 27, + 34, + 41, + 48, + 55, + 62, + 69, + 76, + 83, + 90, + 97, + 104, + 111, + 118, + 125, + 132, + 139, + 146, + 153, + 160, + 167, + 174, + 181, + 188, + 195, + 202, + 209, + 216, + 223, + 230, + 237, + 244, + 251, + 258, + 265, + 272, + 279, + 286, + 293, + 300, + 307, + 314, + 321, + 328, + 335, + 342, + 349, + 356, + 363 + ], + "x": [ + "2020-01-06", + "2020-01-13", + "2020-01-20", + "2020-01-27", + "2020-02-03", + "2020-02-10", + "2020-02-17", + "2020-02-24", + "2020-03-02", + "2020-03-09", + "2020-03-16", + "2020-03-23", + "2020-03-30", + "2020-04-06", + "2020-04-13", + "2020-04-20", + "2020-04-27", + "2020-05-04", + "2020-05-11", + "2020-05-18", + "2020-05-25", + "2020-06-01", + "2020-06-08", + "2020-06-15", + "2020-06-22", + "2020-06-29", + "2020-07-06", + "2020-07-13", + "2020-07-20", + "2020-07-27", + "2020-08-03", + "2020-08-10", + "2020-08-17", + "2020-08-24", + "2020-08-31", + "2020-09-07", + "2020-09-14", + "2020-09-21", + "2020-09-28", + "2020-10-05", + "2020-10-12", + "2020-10-19", + "2020-10-26", + "2020-11-02", + "2020-11-09", + "2020-11-16", + "2020-11-23", + "2020-11-30", + "2020-12-07", + "2020-12-14", + "2020-12-21", + "2020-12-28" + ], + "xperiodalignment": "middle" + }, + { + "xperiod": 604800000, + "xperiod0": "2020-01-06", + "marker": { + "opacity": 0.5, + "line": { + "color": "black", + "width": 1 + } + }, + "name": "end", + "type": "scatter", + "y": [ + 6, + 13, + 20, + 27, + 34, + 41, + 48, + 55, + 62, + 69, + 76, + 83, + 90, + 97, + 104, + 111, + 118, + 125, + 132, + 139, + 146, + 153, + 160, + 167, + 174, + 181, + 188, + 195, + 202, + 209, + 216, + 223, + 230, + 237, + 244, + 251, + 258, + 265, + 272, + 279, + 286, + 293, + 300, + 307, + 314, + 321, + 328, + 335, + 342, + 349, + 356, + 363 + ], + "x": [ + "2020-01-06", + "2020-01-13", + "2020-01-20", + "2020-01-27", + "2020-02-03", + "2020-02-10", + "2020-02-17", + "2020-02-24", + "2020-03-02", + "2020-03-09", + "2020-03-16", + "2020-03-23", + "2020-03-30", + "2020-04-06", + "2020-04-13", + "2020-04-20", + "2020-04-27", + "2020-05-04", + "2020-05-11", + "2020-05-18", + "2020-05-25", + "2020-06-01", + "2020-06-08", + "2020-06-15", + "2020-06-22", + "2020-06-29", + "2020-07-06", + "2020-07-13", + "2020-07-20", + "2020-07-27", + "2020-08-03", + "2020-08-10", + "2020-08-17", + "2020-08-24", + "2020-08-31", + "2020-09-07", + "2020-09-14", + "2020-09-21", + "2020-09-28", + "2020-10-05", + "2020-10-12", + "2020-10-19", + "2020-10-26", + "2020-11-02", + "2020-11-09", + "2020-11-16", + "2020-11-23", + "2020-11-30", + "2020-12-07", + "2020-12-14", + "2020-12-21", + "2020-12-28" + ], + "xperiodalignment": "end" + } + ], + "layout": { + "barmode": "overlay", + "width": 900, + "height": 450, + "margin": { + "t": 60, + "l": 60, + "r": 61, + "b": 60 + }, + "showlegend": true, + "hovermode": "closest", + "title": { + "text": "2020 Mondays" + }, + "yaxis": { + "title": { + "text": "day of year" + } + }, + "xaxis": { + "title": { + "text": "week of year" + }, + "tick0": "2020-01-06", + "dtick": 604800000, + "tickformat": "%W", + "ticklabelmode": "period", + "tickcolor": "black" + } + } +} diff --git a/test/image/mocks/period_positioning8.json b/test/image/mocks/period_positioning8.json new file mode 100644 index 00000000000..ddcc3e36464 --- /dev/null +++ b/test/image/mocks/period_positioning8.json @@ -0,0 +1,67 @@ +{ + "data": [ + { + "marker": { + "opacity": 0.5, + "line": { + "width": 2, + "color": "white" + } + }, + "textposition": "inside", + "textangle": 0, + "texttemplate": "%{x|%A %d %b %Y}", + "hovertemplate": "%{x|%A}, %{y}", + "type": "bar", + "y": [ + 28, + 28, + 28, + 12, + 12, + 12, + 12, + 12, + 12, + 12, + 21, + 21, + 21, + 21 + ], + "x": [ + "2020-01-16", + "2020-01-17", + "2020-01-18", + "2020-01-19", + "2020-01-20", + "2020-01-21", + "2020-01-22", + "2020-01-23", + "2020-01-24", + "2020-01-25", + "2020-01-26", + "2020-01-27", + "2020-01-28", + "2020-01-29" + ], + "xperiod": 604800000 + } + ], + "layout": { + "width": 600, + "height": 400, + "margin": { + "t": 50 + }, + "hovermode": "closest", + "barmode": "stack", + "bargap": 0, + "xaxis": { + "dtick": 86400000, + "ticklabelmode": "period", + "tickformat": "%a %m-%d", + "tickcolor": "black" + } + } +} diff --git a/test/jasmine/tests/gl2d_click_test.js b/test/jasmine/tests/gl2d_click_test.js index 8f5c996e072..f5eb64812ab 100644 --- a/test/jasmine/tests/gl2d_click_test.js +++ b/test/jasmine/tests/gl2d_click_test.js @@ -655,3 +655,41 @@ describe('Test hover and click interactions', function() { .then(done); }); }); + +describe('hover with (x|y)period positioning', function() { + 'use strict'; + + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + function _hover(x, y) { + delete gd._hoverdata; + Lib.clearThrottle(); + mouseEvent('mousemove', x, y); + } + + it('@gl shows hover info for scattergl', function(done) { + Plotly.newPlot(gd, require('@mocks/gl2d_period_positioning.json')) + .then(function() { _hover(100, 255); }) + .then(function() { + assertHoverLabelContent({ + name: '', + nums: '(Jan 2001, Jan 1, 1970)' + }); + }) + .then(function() { _hover(470, 45); }) + .then(function() { + assertHoverLabelContent({ + name: '', + nums: '(Jan 2006, Jun 1, 1970)' + }); + }) + .catch(failTest) + .then(done); + }); +}); diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index c117315d5e4..f550a5b625a 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -2391,7 +2391,6 @@ describe('hover on many lines+bars', function() { }); }); - describe('hover info on overlaid subplots', function() { 'use strict'; @@ -2688,6 +2687,498 @@ describe('Hover on multicategory axes', function() { }); }); +describe('hover on traces with (x|y)period positioning', function() { + 'use strict'; + + var gd; + + beforeEach(function() { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + function _hover(x, y) { + delete gd._hoverdata; + Lib.clearThrottle(); + mouseEvent('mousemove', x, y); + } + + it('shows hover info for scatter, bar, waterfall, funnel, heatmap and contour traces', function(done) { + Plotly.newPlot(gd, require('@mocks/period_positioning.json')) + .then(function() { _hover(110, 390); }) + .then(function() { + assertHoverLabelContent({ + name: 'scatter', + nums: '(Jan 2001, 1)' + }); + }) + .then(function() { _hover(290, 285); }) + .then(function() { + assertHoverLabelContent({ + name: 'scatter', + nums: '(Jan 2004, 4)' + }); + }) + .then(function() { _hover(110, 410); }) + .then(function() { + assertHoverLabelContent({ + name: 'bar (v)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function() { _hover(290, 410); }) + .then(function() { + assertHoverLabelContent({ + name: 'bar (v)', + nums: '(Jan 2004, 4)' + }); + }) + .then(function() { _hover(100, 230); }) + .then(function() { + assertHoverLabelContent({ + name: 'bar (h)', + nums: '(1, Jan 2001)' + }); + }) + .then(function() { _hover(100, 120); }) + .then(function() { + assertHoverLabelContent({ + name: 'bar (h)', + nums: '(4, Jan 2004)' + }); + }) + .then(function() { _hover(135, 230); }) + .then(function() { + assertHoverLabelContent({ + name: 'scatter2', + nums: '(1, Jan 2001)' + }); + }) + .then(function() { _hover(305, 120); }) + .then(function() { + assertHoverLabelContent({ + name: 'scatter2', + nums: '(4, Jan 2004)' + }); + }) + .then(function() { _hover(385, 355); }) + .then(function() { + assertHoverLabelContent({ + name: 'waterfall', + nums: [ + '(Jan 2001, 4)', + '4 ▲', + 'Initial: 0' + ].join('\n') + }); + }) + .then(function() { _hover(565, 355); }) + .then(function() { + assertHoverLabelContent({ + name: 'waterfall', + nums: [ + '(Jan 2004, 2)', + '(1) ▼', + 'Initial: 3' + ].join('\n') + }); + }) + .then(function() { _hover(475, 225); }) + .then(function() { + assertHoverLabelContent({ + name: 'funnel', + nums: [ + '(1, Jan 2004)', + '25% of initial', + '50% of previous', + '10% of total' + ].join('\n') + }); + }) + .then(function() { _hover(475, 115); }) + .then(function() { + assertHoverLabelContent({ + name: 'funnel', + nums: [ + '(4, Jan 2001)', + '100% of initial', + '100% of previous', + '40% of total' + ].join('\n') + }); + }) + .then(function() { _hover(665, 365); }) + .then(function() { + assertHoverLabelContent({ + name: 'heatmap', + nums: [ + 'x: Jan 2001', + 'y: Jan 2002', + 'z: 1' + ].join('\n') + }); + }) + .then(function() { _hover(800, 150); }) + .then(function() { + assertHoverLabelContent({ + name: 'contour', + nums: [ + 'x: Jan 2003', + 'y: Jan 2003', + 'z: 0' + ].join('\n') + }); + }) + + .catch(failTest) + .then(done); + }); + + it('shows hover info for box, ohlc, candlestick, histogram, histogram2d and histogram2dcontour traces', function(done) { + Plotly.newPlot(gd, require('@mocks/period_positioning2.json')) + .then(function() { _hover(110, 390); }) + .then(function() { + assertHoverLabelContent({ + name: 'ohlc', + nums: [ + 'Jan 2001', + 'open: 1', + 'high: 2', + 'low: 0', + 'close: 0.5 ▼' + ].join('\n') + }); + }) + .then(function() { _hover(290, 285); }) + .then(function() { + assertHoverLabelContent({ + name: 'ohlc', + nums: [ + 'Jan 2004', + 'open: 4', + 'high: 8', + 'low: 0', + 'close: 2 ▼' + ].join('\n') + }); + }) + .then(function() { _hover(110, 410); }) + .then(function() { + assertHoverLabelContent({ + name: 'histogram(v)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function() { _hover(290, 410); }) + .then(function() { + assertHoverLabelContent({ + name: 'histogram(v)', + nums: '(Jan 2004, 4)' + }); + }) + .then(function() { _hover(100, 230); }) + .then(function() { + assertHoverLabelContent({ + name: 'histogram(h)', + nums: '(1, Jan 2001)' + }); + }) + .then(function() { _hover(100, 120); }) + .then(function() { + assertHoverLabelContent({ + name: 'histogram(h)', + nums: '(4, Jan 2004)' + }); + }) + .then(function() { _hover(565, 355); }) + .then(function() { + assertHoverLabelContent({ + name: 'candlestick', + nums: [ + 'Jan 2004', + 'open: 4', + 'high: 8', + 'low: 0', + 'close: 2 ▼' + ].join('\n') + }); + }) + .then(function() { _hover(385, 355); }) + .then(function() { + assertHoverLabelContent({ + name: ['', '', '', 'box (v)', ''], + nums: [ + '(Jan 2001, min: 2)', + '(Jan 2001, q1: 4)', + '(Jan 2001, q3: 8)', + '(Jan 2001, median: 6)', + '(Jan 2001, max: 10)', + ] + }); + }) + .then(function() { _hover(475, 120); }) + .then(function() { + assertHoverLabelContent({ + name: ['', '', '', '', 'box (h)'], + nums: [ + '(max: 8, Jan 2004)', + '(min: 0, Jan 2004)', + '(q1: 2, Jan 2004)', + '(q3: 6, Jan 2004)', + '(median: 4, Jan 2004)' + ] + }); + }) + .then(function() { _hover(665, 365); }) + .then(function() { + assertHoverLabelContent({ + name: 'histogram2d', + nums: [ + 'x: Jan 2001', + 'y: Jan 2002', + 'z: 0' + ].join('\n') + }); + }) + .then(function() { _hover(800, 150); }) + .then(function() { + assertHoverLabelContent({ + name: 'hist2dcontour', + nums: [ + 'x: Jan 2003', + 'y: Jan 2003', + 'z: 3' + ].join('\n') + }); + }) + + .catch(failTest) + .then(done); + }); + + it('shows hover info and hovertemplate for bar and scatter traces using (start | middle | end) alignments and different periods', function(done) { + Plotly.newPlot(gd, require('@mocks/period_positioning4.json')) + .then(function() { _hover(65, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M1)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function() { _hover(65, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M1)', + nums: '(Jan 15, 2001, 2)' + }); + }) + .then(function() { _hover(100, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M1)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function() { _hover(100, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M1)', + nums: '(Jan 15, 2001, 2)' + }); + }) + .then(function() { _hover(135, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M1)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function() { _hover(135, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M1)', + nums: '(Jan 15, 2001, 2)' + }); + }) + + .then(function() { _hover(65, 205); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M2)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function() { _hover(65, 175); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M2)', + nums: '(Feb 1, 2001, 2)' + }); + }) + .then(function() { _hover(100, 205); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M2)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function() { _hover(100, 175); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M2)', + nums: '(Feb 1, 2001, 2)' + }); + }) + .then(function() { _hover(135, 205); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M2)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function() { _hover(135, 175); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M2)', + nums: '(Feb 1, 2001, 2)' + }); + }) + + .then(function() { _hover(345, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M3)', + nums: '(Q1, 1)' + }); + }) + .then(function() { _hover(345, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M3)', + nums: '(Q1, 2)' + }); + }) + .then(function() { _hover(380, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M3)', + nums: '(Q1, 1)' + }); + }) + .then(function() { _hover(380, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M3)', + nums: '(Q1, 2)' + }); + }) + .then(function() { _hover(415, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M3)', + nums: '(Q1, 1)' + }); + }) + .then(function() { _hover(415, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M3)', + nums: '(Q1, 2)' + }); + }) + + .then(function() { _hover(630, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M12)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function() { _hover(630, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (M12)', + nums: '(Jul 2001, 2)' + }); + }) + .then(function() { _hover(665, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M12)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function() { _hover(665, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (M12)', + nums: '(Jul 2001, 2)' + }); + }) + .then(function() { _hover(700, 425); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M12)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function() { _hover(700, 395); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (M12)', + nums: '(Jul 2001, 2)' + }); + }) + + .then(function() { _hover(630, 205); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (W1)', + nums: '(W01, 1)' + }); + }) + .then(function() { _hover(630, 175); }) + .then(function() { + assertHoverLabelContent({ + name: 'start (W1)', + nums: '(W01, 2)' + }); + }) + .then(function() { _hover(665, 205); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (W1)', + nums: 'Monday, 1' + }); + }) + .then(function() { _hover(665, 175); }) + .then(function() { + assertHoverLabelContent({ + name: 'middle (W1)', + nums: 'Friday, 2' + }); + }) + .then(function() { _hover(700, 205); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (W1)', + nums: '(W01, 1)' + }); + }) + .then(function() { _hover(700, 175); }) + .then(function() { + assertHoverLabelContent({ + name: 'end (W1)', + nums: '(W01, 2)' + }); + }) + + .catch(failTest) + .then(done); + }); +}); + describe('Hover on axes with rangebreaks', function() { var gd; var eventData; diff --git a/test/jasmine/tests/mock_test.js b/test/jasmine/tests/mock_test.js index d68c3604bd2..790efb43f4e 100644 --- a/test/jasmine/tests/mock_test.js +++ b/test/jasmine/tests/mock_test.js @@ -415,6 +415,7 @@ var list = [ 'gl2d_parcoords_select_first_last_enum', 'gl2d_parcoords_style_labels', 'gl2d_parcoords_tick_format', + 'gl2d_period_positioning', 'gl2d_point-selection', 'gl2d_pointcloud-basic', 'gl2d_rgb_dont_accept_alpha_scattergl', @@ -753,6 +754,14 @@ var list = [ 'parcats_reordered', 'parcats_unbundled', 'percent_error_bar', + 'period_positioning', + 'period_positioning2', + 'period_positioning3', + 'period_positioning4', + 'period_positioning5', + 'period_positioning6', + 'period_positioning7', + 'period_positioning8', 'picnic_heatmap', 'pie_aggregated', 'pie_automargin', @@ -1467,6 +1476,7 @@ figs['gl2d_parcoords_rgba_colorscale'] = require('@mocks/gl2d_parcoords_rgba_col // figs['gl2d_parcoords_select_first_last_enum'] = require('@mocks/gl2d_parcoords_select_first_last_enum'); figs['gl2d_parcoords_style_labels'] = require('@mocks/gl2d_parcoords_style_labels'); figs['gl2d_parcoords_tick_format'] = require('@mocks/gl2d_parcoords_tick_format'); +figs['gl2d_period_positioning'] = require('@mocks/gl2d_period_positioning'); figs['gl2d_point-selection'] = require('@mocks/gl2d_point-selection'); // figs['gl2d_pointcloud-basic'] = require('@mocks/gl2d_pointcloud-basic'); // figs['gl2d_rgb_dont_accept_alpha_scattergl'] = require('@mocks/gl2d_rgb_dont_accept_alpha_scattergl'); @@ -1805,6 +1815,14 @@ figs['parcats_numeric_sort'] = require('@mocks/parcats_numeric_sort'); figs['parcats_reordered'] = require('@mocks/parcats_reordered'); figs['parcats_unbundled'] = require('@mocks/parcats_unbundled'); figs['percent_error_bar'] = require('@mocks/percent_error_bar'); +figs['period_positioning'] = require('@mocks/period_positioning'); +figs['period_positioning2'] = require('@mocks/period_positioning2'); +figs['period_positioning3'] = require('@mocks/period_positioning3'); +figs['period_positioning4'] = require('@mocks/period_positioning4'); +figs['period_positioning5'] = require('@mocks/period_positioning5'); +figs['period_positioning6'] = require('@mocks/period_positioning6'); +figs['period_positioning7'] = require('@mocks/period_positioning7'); +figs['period_positioning8'] = require('@mocks/period_positioning8'); figs['picnic_heatmap'] = require('@mocks/picnic_heatmap'); figs['pie_aggregated'] = require('@mocks/pie_aggregated'); figs['pie_automargin'] = require('@mocks/pie_automargin'); @@ -2092,7 +2110,6 @@ figs['waterfall-offsetgroups'] = require('@mocks/waterfall-offsetgroups'); // figs['yiorrd_heatmap'] = require('@mocks/yiorrd_heatmap'); figs['zsmooth_methods'] = require('@mocks/zsmooth_methods'); - describe('@noCI mock validation', function() { list.forEach(function(name) { var figure = figs[name];