Skip to content

Dates as dates #1078

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

Merged
merged 29 commits into from
Nov 10, 2016
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d4bb89d
improve dateTime2ms edge cases and standardize BADNUM and FP_SAFE con…
alexcjohnson Oct 14, 2016
9e27955
switch date axes to use date strings for range and tick0
alexcjohnson Oct 14, 2016
00138aa
Merge branch 'master' into dates-as-dates
alexcjohnson Oct 14, 2016
9784b3b
axe parseDate - moved to streambed
alexcjohnson Oct 15, 2016
1db4448
refactor dates handling to use date strings as much as possible
alexcjohnson Oct 24, 2016
5ff9722
Merge branch 'master' into dates-as-dates
alexcjohnson Oct 24, 2016
c045bc5
wider acceptance range for mapbox plot size test so it works locally
alexcjohnson Oct 25, 2016
3dd294f
fix for range slider tests with new axis range machinery
alexcjohnson Oct 25, 2016
451ee24
fix date axis ranges and date interactions in gl2d
alexcjohnson Oct 25, 2016
9ccde7e
Merge branch 'master' into dates-as-dates
alexcjohnson Oct 26, 2016
51a0563
lib/constants -> constants/numerical
alexcjohnson Oct 26, 2016
5ccd083
td -> gd once and for all
alexcjohnson Oct 26, 2016
e63ea3b
test and fix shape default positioning
alexcjohnson Oct 26, 2016
2e9dbad
fix date and log axis manual tick0/dtick handling
alexcjohnson Oct 28, 2016
37d52fe
fix handling of default tick0 with dtick for date axes
alexcjohnson Oct 28, 2016
55b313c
get rid of '2012-01-22 12h' tick format and smarter date suffix handling
alexcjohnson Oct 29, 2016
b7121c3
fix #1058 - make sure no date ever returns sub-100microsec precision
alexcjohnson Oct 29, 2016
03b4a0c
Merge branch 'master' into dates-as-dates
alexcjohnson Oct 29, 2016
dd0940a
fix _forceTick0 error
alexcjohnson Oct 29, 2016
f629f71
update baseline with new date tick format
alexcjohnson Oct 29, 2016
a29ce33
fix images on date axes, and standardize axis object names
alexcjohnson Nov 9, 2016
065ac8a
fix annotation drag on date axes, and test date/log annotations
alexcjohnson Nov 9, 2016
4a0a866
Merge branch 'master' into dates-as-dates
alexcjohnson Nov 9, 2016
7679483
update jasmine test of annotation visibility & autorange
alexcjohnson Nov 9, 2016
0d61fcc
Merge branch 'master' into dates-as-dates
alexcjohnson Nov 9, 2016
5e11571
merge lint
alexcjohnson Nov 9, 2016
d807259
add ax.r2p and ax.p2r
alexcjohnson Nov 10, 2016
05c762d
date interval milliseconds constants
alexcjohnson Nov 10, 2016
1b0e133
documentation updates
alexcjohnson Nov 10, 2016
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
21 changes: 15 additions & 6 deletions src/plots/cartesian/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,34 +137,43 @@ module.exports = {
].join(' ')
},
tick0: {
valType: 'number',
valType: 'any',
dflt: 0,
role: 'style',
description: [
'Sets the placement of the first tick on this axis.',
'Use with `dtick`.',
'If the axis `type` is *log*, then you must take the log of your starting tick',
'(e.g. to set the starting tick to 100, set the `tick0` to 2).',
'(e.g. to set the starting tick to 100, set the `tick0` to 2)',
'except when `dtick`=*L<f>* (see `dtick` for more info).',
'If the axis `type` is *date*, it should be a date string, like date data.',
'If the axis `type` is *category*, it should be a number, using the scale where',
'each category is assigned a serial number from zero in the order it appears.'
].join(' ')
},
dtick: {
valType: 'any',
dflt: 1,
role: 'style',
description: [
'Sets the step in-between ticks on this axis',
'Use with `tick0`.',
'Sets the step in-between ticks on this axis. Use with `tick0`.',
'Must be a positive number, or special strings available to *log* and *date* axes.',
'If the axis `type` is *log*, then ticks are set every 10^(n*dtick) where n',
'is the tick number. For example,',
'to set a tick mark at 1, 10, 100, 1000, ... set dtick to 1.',
'To set tick marks at 1, 100, 10000, ... set dtick to 2.',
'To set tick marks at 1, 5, 25, 125, 625, 3125, ... set dtick to log_10(5), or 0.69897000433.',
'*log* has several special values; *L<f>*, where `f` is a positive number,',
'gives ticks linearly spaced in value (but not position).',
'For example `tick0` = 0.1, `dtick` = *L0.5* will put ticks at 0.1, 0.6, 1.1, 1.6 etc.',
'To show powers of 10 plus small digits between, use *D1* (all digits) or *D2* (only 2 and 5).',
'`tick0` is ignored for *D1* and *D2*.',
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice. Is this a new or a current (but hidden) feature?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These have always existed behind the scenes but I think there were some bugs if you tried to use them yourself as opposed to getting them automatically. Now they work manually, and are tested at the level of supplyDefaults (which is where the bugs were before, I think) though at some point it might be worth adding tests of the functionality when used manually.

'If the axis `type` is *date*, then you must convert the time to milliseconds.',
'For example, to set the interval between ticks to one day,',
'set `dtick` to 86400000.0.'
'set `dtick` to 86400000.0.',
'*date* also has special values *M<n>* gives ticks spaced by a number of months.',
'`n` must be a positive integer.',
'To set ticks on the 15th of every third month, set `tick0` to *2000-01-15* and `dtick` to *M3*.',
'To set ticks every 4 years, set `dtick` to *M48*'
].join(' ')
},
tickvals: {
Expand Down
39 changes: 35 additions & 4 deletions src/plots/cartesian/tick_value_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,53 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe
}

if(Array.isArray(containerIn.tickvals)) tickmodeDefault = 'array';
else if(containerIn.dtick && isNumeric(containerIn.dtick)) {
else if(containerIn.dtick) {
Copy link
Contributor

@etpinard etpinard Nov 10, 2016

Choose a reason for hiding this comment

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

ha. I guess a new feature. 😌

Copy link
Contributor

Choose a reason for hiding this comment

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

... and probably one of the most useful new features of the passed few months 🏆

tickmodeDefault = 'linear';
}
var tickmode = coerce('tickmode', tickmodeDefault);

if(tickmode === 'auto') coerce('nticks');
else if(tickmode === 'linear') {
var tick0 = coerce('tick0');
coerce('dtick');
// dtick is usually a positive number, but there are some
// special strings available for log or date axes
// default is 1 day for dates, otherwise 1
var dtickDflt = (axType === 'date') ? 86400000 : 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

that 1 day in ms 8640000 would fit well in the new src/constants/numerical

Copy link
Contributor

@etpinard etpinard Nov 10, 2016

Choose a reason for hiding this comment

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

blocking change

var dtick = coerce('dtick', dtickDflt);
if(isNumeric(dtick)) {
containerOut.dtick = (dtick > 0) ? Number(dtick) : dtickDflt;
}
else if(typeof dtick !== 'string') {
containerOut.dtick = dtickDflt;
}
else {
// date and log special cases are all one character plus a number
var prefix = dtick.charAt(0),
dtickNum = dtick.substr(1);

dtickNum = isNumeric(dtickNum) ? Number(dtickNum) : 0;
if((dtickNum <= 0) || !(
// "M<n>" gives ticks every (integer) n months
(axType === 'date' && prefix === 'M' && dtickNum === Math.round(dtickNum)) ||
// "L<f>" gives ticks linearly spaced in data (not in position) every (float) f
(axType === 'log' && prefix === 'L') ||
// "D1" gives powers of 10 with all small digits between, "D2" gives only 2 and 5
(axType === 'log' && prefix === 'D' && (dtickNum === 1 || dtickNum === 2))
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice. This is going to make a lot of users happy. You wouldn't believe how many people have ask for custom date tick labels. This is way better then tickvals + ticktext!

)) {
containerOut.dtick = dtickDflt;
}
}

// tick0 can have different valType for different axis types, so
// validate that now. Also for dates, change milliseconds to date strings
var tick0 = coerce('tick0');
if(axType === 'date') {
containerOut.tick0 = Lib.cleanDate(tick0, '2000-01-01');
}
else if(!isNumeric(tick0)) {
// Aside from date axes, dtick must be numeric; D1 and D2 modes ignore tick0 entirely
else if(isNumeric(tick0) && dtick !== 'D1' && dtick !== 'D2') {
containerOut.tick0 = Number(tick0);
}
else {
containerOut.tick0 = 0;
}
}
Expand Down
79 changes: 79 additions & 0 deletions test/jasmine/tests/axes_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,85 @@ describe('Test axes', function() {
expect(axOut.dtick).toBe(0.00159);
});

it('should handle tick0 and dtick for date axes', function() {
var someMs = 123456789,
someMsDate = Lib.ms2DateTime(someMs),
oneDay = 24 * 3600 * 1000,
axIn = {tick0: someMs, dtick: String(3 * oneDay)},
axOut = {};
mockSupplyDefaults(axIn, axOut, 'date');
expect(axOut.tick0).toBe(someMsDate);
expect(axOut.dtick).toBe(3 * oneDay);

var someDate = '2011-12-15 13:45:56';
axIn = {tick0: someDate, dtick: 'M15'};
axOut = {};
mockSupplyDefaults(axIn, axOut, 'date');
expect(axOut.tick0).toBe(someDate);
expect(axOut.dtick).toBe('M15');

// now some stuff that shouldn't work, should give defaults
[
['next thursday', -1],
['123-45', 'L1'],
['', 'M0.5'],
['', 'M-1'],
['', '2000-01-01']
].forEach(function(v) {
axIn = {tick0: v[0], dtick: v[1]};
axOut = {};
mockSupplyDefaults(axIn, axOut, 'date');
expect(axOut.tick0).toBe('2000-01-01');
expect(axOut.dtick).toBe(oneDay);
});
});

it('should handle tick0 and dtick for log axes', function() {
var axIn = {tick0: '0.2', dtick: 0.3},
axOut = {};
mockSupplyDefaults(axIn, axOut, 'log');
expect(axOut.tick0).toBe(0.2);
expect(axOut.dtick).toBe(0.3);

['D1', 'D2'].forEach(function(v) {
axIn = {tick0: -1, dtick: v};
axOut = {};
mockSupplyDefaults(axIn, axOut, 'log');
// tick0 gets ignored for D<n>
expect(axOut.tick0).toBe(0);
expect(axOut.dtick).toBe(v);
});

[
[-1, 'L3'],
['0.2', 'L0.3'],
[-1, 3],
['0.1234', '0.69238473']
].forEach(function(v) {
axIn = {tick0: v[0], dtick: v[1]};
axOut = {};
mockSupplyDefaults(axIn, axOut, 'log');
expect(axOut.tick0).toBe(Number(v[0]));
expect(axOut.dtick).toBe((+v[1]) ? Number(v[1]) : v[1]);
});

// now some stuff that should not work, should give defaults
[
['', -1],
['D1', 'D3'],
['', 'D0'],
['2011-01-01', 'L0'],
['', 'L-1']
].forEach(function(v) {
axIn = {tick0: v[0], dtick: v[1]};
axOut = {};
mockSupplyDefaults(axIn, axOut, 'log');
expect(axOut.tick0).toBe(0);
expect(axOut.dtick).toBe(1);
});

});

it('should set tickvals and ticktext iff tickmode=array', function() {
var axIn = {tickmode: 'auto', tickvals: [1, 2, 3], ticktext: ['4', '5', '6']},
axOut = {};
Expand Down