Skip to content

Commit

Permalink
Merge pull request #4993 from plotly/fix4911-ticklable-positioning
Browse files Browse the repository at this point in the history
Implement period ticklabelmode on cartesian date axes
  • Loading branch information
archmoj authored Jul 24, 2020
2 parents edcb1a4 + 83f5cdd commit 6b6b475
Show file tree
Hide file tree
Showing 10 changed files with 391 additions and 1 deletion.
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: 89 additions & 1 deletion src/plots/cartesian/axes.js
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,15 @@ axes.calcTicks = function calcTicks(ax, opts) {

generateTicks();

var isPeriod = ax.ticklabelmode === 'period';
if(isPeriod) {
// add one label to show pre tick0 period
tickVals.unshift({
minor: false,
value: axes.tickIncrement(tickVals[0].value, ax.dtick, !axrev, ax.caldendar)
});
}

if(ax.rangebreaks) {
// replace ticks inside breaks that would get a tick
// and reduce ticks
Expand Down Expand Up @@ -681,6 +690,32 @@ 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 removedPreTick0Label = false;
var ticksOut = new Array(tickVals.length);
for(var i = 0; i < tickVals.length; i++) {
var _minor = tickVals[i].minor;
Expand All @@ -692,6 +727,46 @@ 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(i < tickVals.length - 1) {
a = i;
b = i + 1;
} else {
a = i - 1;
b = i;
}

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

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

ticksOut[i].periodX = v;

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

if(removedPreTick0Label && ticksOut.length > 1) {
// redo tick0 text
ax._prevDateHead = '';
ticksOut[1].text = axes.tickText(ax, tickVals[1].value).text;
}

ax._inCalcTicks = false;
Expand Down Expand Up @@ -1785,6 +1860,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 +1982,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 +2292,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 +2598,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.'
].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.
Binary file added test/image/baselines/date_axes_period2.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"}
}
}
Loading

0 comments on commit 6b6b475

Please sign in to comment.