Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement period ticklabelmode on cartesian date axes #4993

Merged
merged 13 commits into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/colorbar/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,7 @@ function mockColorBarAxis(gd, opts, zrange) {
font: fullLayout.font,
noHover: true,
noTickson: true,
noTicklabelmode: true,
calendar: fullLayout.calendar // not really necessary (yet?)
};

Expand Down
90 changes: 88 additions & 2 deletions src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,6 @@ axes.calcTicks = function calcTicks(ax, opts) {
}

var isDLog = (ax.type === 'log') && !(isNumeric(ax.dtick) || ax.dtick.charAt(0) === 'L');

var tickVals;
function generateTicks() {
var xPrevious = null;
Expand Down Expand Up @@ -620,6 +619,16 @@ axes.calcTicks = function calcTicks(ax, opts) {

generateTicks();

var isPeriod = ax.ticklabelmode === 'period';
var addOneTickToStart = isPeriod && tickVals.length > 2;

if(addOneTickToStart) {
tickVals.push({
minor: false,
value: 2 * tickVals[0].value - tickVals[1].value
});
alexcjohnson marked this conversation as resolved.
Show resolved Hide resolved
}

if(ax.rangebreaks) {
// replace ticks inside breaks that would get a tick
// and reduce ticks
Expand Down Expand Up @@ -681,6 +690,31 @@ axes.calcTicks = function calcTicks(ax, opts) {
ax._prevDateHead = '';
ax._inCalcTicks = true;

var minRange = Math.min(rng[0], rng[1]);
var maxRange = Math.max(rng[0], rng[1]);

var definedDelta;
if(isPeriod && ax.tickformat) {
var _has = function(str) {
return ax.tickformat.indexOf(str) !== -1;
};

if(
!_has('%f') &&
!_has('%H') &&
!_has('%I') &&
!_has('%L') &&
!_has('%Q') &&
!_has('%S') &&
!_has('%s') &&
!_has('%X')
) {
if(_has('%x') || _has('%d') || _has('%e') || _has('%j')) definedDelta = ONEDAY;
else if(_has('%B') || _has('%b') || _has('%m')) definedDelta = ONEAVGMONTH;
else if(_has('%Y') || _has('%y')) definedDelta = ONEAVGYEAR;
}
}

var ticksOut = new Array(tickVals.length);
for(var i = 0; i < tickVals.length; i++) {
var _minor = tickVals[i].minor;
Expand All @@ -692,6 +726,45 @@ axes.calcTicks = function calcTicks(ax, opts) {
false, // hover
_minor // noSuffixPrefix
);

if(isPeriod) {
var v = tickVals[i].value;

var a = i;
var b = i + 1;
if(addOneTickToStart) {
if(i === tickVals.length - 1) {
a = i;
b = 0;
} else if(i === tickVals.length - 2) {
a = i - 1;
b = i;
}
} else {
if(i === tickVals.length - 1) {
a = i - 1;
b = i;
}
}

var A = tickVals[a].value;
var B = tickVals[b].value;

var delta = definedDelta || Math.abs(B - A);
if(delta >= ONEDAY * 365) { // Years could have days less than ONEAVGYEAR period
v += ONEAVGYEAR / 2;
} else if(delta >= ONEDAY * 28) { // Months could have days less than ONEAVGMONTH period
v += ONEAVGMONTH / 2;
} else if(delta >= ONEDAY) {
v += ONEDAY / 2;
}

ticksOut[i].periodX = v;

if(v > maxRange || v < minRange) { // hide label if outside the range
ticksOut[i].text = '';
}
}
}

ax._inCalcTicks = false;
Expand Down Expand Up @@ -1785,6 +1858,10 @@ axes.drawOne = function(gd, ax, opts) {
if(!ax.visible) return;

var transFn = axes.makeTransFn(ax);
var transTickLabelFn = ax.ticklabelmode === 'period' ?
axes.makeTransPeriodFn(ax) :
axes.makeTransFn(ax);

var tickVals;
// We remove zero lines, grid lines, and inside ticks if they're within 1px of the end
// The key case here is removing zero lines when the axis bound is zero
Expand Down Expand Up @@ -1903,7 +1980,7 @@ axes.drawOne = function(gd, ax, opts) {
return axes.drawLabels(gd, ax, {
vals: vals,
layer: mainAxLayer,
transFn: transFn,
transFn: transTickLabelFn,
labelFns: axes.makeLabelFns(ax, mainLinePosition)
});
});
Expand Down Expand Up @@ -2213,6 +2290,14 @@ axes.makeTransFn = function(ax) {
function(d) { return 'translate(0,' + (offset + ax.l2p(d.x)) + ')'; };
};

axes.makeTransPeriodFn = function(ax) {
var axLetter = ax._id.charAt(0);
var offset = ax._offset;
return axLetter === 'x' ?
function(d) { return 'translate(' + (offset + ax.l2p(d.periodX)) + ',0)'; } :
function(d) { return 'translate(0,' + (offset + ax.l2p(d.periodX)) + ')'; };
};

/**
* Make axis tick path string
*
Expand Down Expand Up @@ -2511,6 +2596,7 @@ axes.drawLabels = function(gd, ax, opts) {
var axLetter = axId.charAt(0);
var cls = opts.cls || axId + 'tick';
var vals = opts.vals;

var labelFns = opts.labelFns;
var tickAngle = opts.secondary ? 0 : ax.tickangle;
var prevAngle = (ax._prevTickAngles || {})[cls];
Expand Down
2 changes: 2 additions & 0 deletions src/plots/cartesian/axis_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ module.exports = function handleAxisDefaults(containerIn, containerOut, coerce,
}

if(axType === 'date') {
if(!options.noTicklabelmode) coerce('ticklabelmode');

handleArrayContainerDefaults(containerIn, containerOut, {
name: 'rangebreaks',
inclusionAttr: 'enabled',
Expand Down
14 changes: 14 additions & 0 deletions src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,20 @@ module.exports = {
'to the left/bottom of labels.'
].join(' ')
},
ticklabelmode: {
valType: 'enumerated',
values: ['instant', 'period'],
dflt: 'instant',
role: 'info',
editType: 'ticks',
description: [
'Determines where tick labels are drawn with respect to their',
'corresponding ticks and grid lines.',
'Only has an effect for axes of `type` *date*',
'When set to *period*, tick labels are drawn in the middle of the period',
'between ticks.'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is actually not quite the desired behaviour: if the ticks are spaced every 2 months and the labels are Jan and Mar, the Jan label should appear on Jan 15, not on Feb 1.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's an example... https://codepen.io/nicolaskruchten/pen/ZEQRBWw?editors=0010

In "instant" mode we see 4 "month" labels spaced two months apart. In "period" mode we probably should see only 3 labels, but they should remain Jan/Mar/May, and they should be positioned on Jan 15, Mar 15, May 15

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a clearer codepen in the original issue

].join(' ')
},
mirror: {
valType: 'enumerated',
values: [true, 'ticks', false, 'all', 'allticks'],
Expand Down
1 change: 1 addition & 0 deletions src/plots/gl3d/layout/axis_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, options) {
data: options.data,
showGrid: true,
noTickson: true,
noTicklabelmode: true,
bgColor: options.bgColor,
calendar: options.calendar
},
Expand Down
Binary file added test/image/baselines/date_axes_period.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 91 additions & 0 deletions test/image/mocks/date_axes_period.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
{
"data": [
{
"x": [
"1900-01-01",
"2000-01-01",
"2100-01-01"
],
"y": [1, 3, 2]
},
{
"x": [
"2013-05-01",
"2013-09-01",
"2014-01-01"
],
"y": [1, 3, 2],
"xaxis": "x2",
"yaxis": "y2"
},
{
"x": [
"2013-11-17",
"2013-12-15",
"2014-01-12"
],
"y": [1, 3, 2],
"xaxis": "x3",
"yaxis": "y3"
},
{
"x": [
"2013-01-01",
"2013-01-02",
"2013-01-03"
],
"y": [1, 3, 2],
"xaxis": "x4",
"yaxis": "y4"
},
{
"x": [
"2013-07-01 18:00",
"2013-07-02 00:00",
"2013-07-02 06:00"
],
"y": [1, 3, 2],
"xaxis": "x5",
"yaxis": "y5"
},
{
"x": [
"2013-01-01 23:59",
"2013-01-02 00:00",
"2013-01-02 00:01"
],
"y": [1, 3, 2],
"xaxis": "x6",
"yaxis": "y6"
},
{
"x": [
"2013-07-01 23:59:59",
"2013-07-02 00:00:00",
"2013-07-02 00:00:01"
],
"y": [1, 3, 2],
"xaxis": "x7",
"yaxis": "y7"
}
],
"layout": {
"showlegend": false,
"width": 600,
"height": 500,
"yaxis": {"domain": [0, 0.04]},
"yaxis2": {"domain": [0.16, 0.2]},
"yaxis3": {"domain": [0.32, 0.36]},
"yaxis4": {"domain": [0.48, 0.52]},
"yaxis5": {"domain": [0.64, 0.68]},
"yaxis6": {"domain": [0.80, 0.84]},
"yaxis7": {"domain": [0.96, 1]},
"xaxis": {"ticklabelmode": "period"},
"xaxis2": {"ticklabelmode": "period", "anchor": "y2"},
"xaxis3": {"ticklabelmode": "period", "anchor": "y3"},
"xaxis4": {"ticklabelmode": "period", "anchor": "y4"},
"xaxis5": {"ticklabelmode": "period", "anchor": "y5"},
"xaxis6": {"ticklabelmode": "period", "anchor": "y6"},
"xaxis7": {"ticklabelmode": "period", "anchor": "y7"}
}
}
2 changes: 2 additions & 0 deletions test/jasmine/tests/mock_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ var list = [
'custom_colorscale',
'custom_size_subplot',
'date_axes',
'date_axes_period',
'date_histogram',
'dendrogram',
'display-text_zero-number',
Expand Down Expand Up @@ -1296,6 +1297,7 @@ figs['contour-heatmap-coloring-set-contours'] = require('@mocks/contour-heatmap-
figs['custom_colorscale'] = require('@mocks/custom_colorscale');
figs['custom_size_subplot'] = require('@mocks/custom_size_subplot');
figs['date_axes'] = require('@mocks/date_axes');
figs['date_axes_period'] = require('@mocks/date_axes_period');
figs['date_histogram'] = require('@mocks/date_histogram');
// figs['dendrogram'] = require('@mocks/dendrogram');
figs['display-text_zero-number'] = require('@mocks/display-text_zero-number');
Expand Down