Skip to content

Commit 55b313c

Browse files
committed
get rid of '2012-01-22 12h' tick format and smarter date suffix handling
1 parent 37d52fe commit 55b313c

File tree

2 files changed

+152
-36
lines changed

2 files changed

+152
-36
lines changed

src/plots/cartesian/axes.js

Lines changed: 68 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -658,9 +658,18 @@ axes.calcTicks = function calcTicks(ax) {
658658
// show the exponent only on the last one
659659
ax._tmax = vals[vals.length - 1];
660660

661+
// for showing date suffixes: ax._prevSuffix holds what we showed most
662+
// recently. Start with it cleared and mark that we're in calcTicks (ie
663+
// calculating a whole string of these so we should care what the previous
664+
// suffix was!)
665+
ax._prevSuffix = '';
666+
ax._inCalcTicks = true;
667+
661668
var ticksOut = new Array(vals.length);
662669
for(var i = 0; i < vals.length; i++) ticksOut[i] = axes.tickText(ax, vals[i]);
663670

671+
ax._inCalcTicks = false;
672+
664673
return ticksOut;
665674
};
666675

@@ -815,6 +824,11 @@ axes.autoTicks = function(ax, roughDTick) {
815824
}
816825
};
817826

827+
var ONEDAY = 86400000,
828+
ONEHOUR = 3600000,
829+
ONEMIN = 60000,
830+
ONESEC = 1000;
831+
818832
// after dtick is already known, find tickround = precision
819833
// to display in tick labels
820834
// for numeric ticks, integer # digits after . to round to
@@ -831,33 +845,43 @@ function autoTickRound(ax) {
831845
if(ax.type === 'category') {
832846
ax._tickround = null;
833847
}
848+
if(ax.type === 'date') {
849+
// If tick0 is unusual, give tickround a bit more information
850+
// not necessarily *all* the information in tick0 though, if it's really odd
851+
// minimal string length for tick0: 'd' is 10, 'M' is 16, 'S' is 19
852+
// take off a leading minus (year < 0 so length is consistent)
853+
var tick0str = Lib.ms2DateTime(Lib.dateTime2ms(ax.tick0)).replace(/^-/, ''),
854+
tick0len = tick0str.length;
855+
856+
if(String(dtick).charAt(0) === 'M') {
857+
// any tick0 more specific than a year: alway show the full date
858+
if(tick0len > 10 || tick0str.substr(5) !== '01-01') ax._tickround = 'd';
859+
// show the month unless ticks are full multiples of a year
860+
else ax._tickround = (+(dtick.substr(1)) % 12 === 0) ? 'y' : 'm';
861+
}
862+
else if((dtick >= ONEDAY && tick0len <= 10) || (dtick >= ONEDAY * 15)) ax._tickround = 'd';
863+
else if((dtick >= ONEMIN && tick0len <= 16) || (dtick >= ONEHOUR)) ax._tickround = 'M';
864+
else if((dtick >= ONESEC && tick0len <= 19) || (dtick >= ONEMIN)) ax._tickround = 'S';
865+
else ax._tickround = Math.max(3 - Math.round(Math.log(dtick / 2) / Math.LN10), tick0len - 20);
866+
}
834867
else if(isNumeric(dtick) || dtick.charAt(0) === 'L') {
835-
if(ax.type === 'date') {
836-
if(dtick >= 86400000) ax._tickround = 'd';
837-
else if(dtick >= 3600000) ax._tickround = 'H';
838-
else if(dtick >= 60000) ax._tickround = 'M';
839-
else if(dtick >= 1000) ax._tickround = 'S';
840-
else ax._tickround = 3 - Math.round(Math.log(dtick / 2) / Math.LN10);
841-
}
842-
else {
843-
// linear or log
844-
var rng = ax.range.map(ax.r2d || Number);
845-
if(!isNumeric(dtick)) dtick = Number(dtick.substr(1));
846-
// 2 digits past largest digit of dtick
847-
ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01);
848-
849-
var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1]));
850-
851-
var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
852-
if(Math.abs(rangeexp) > 3) {
853-
if(ax.exponentformat === 'SI' || ax.exponentformat === 'B') {
854-
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
855-
}
856-
else ax._tickexponent = rangeexp;
868+
// linear or log (except D1, D2)
869+
var rng = ax.range.map(ax.r2d || Number);
870+
if(!isNumeric(dtick)) dtick = Number(dtick.substr(1));
871+
// 2 digits past largest digit of dtick
872+
ax._tickround = 2 - Math.floor(Math.log(dtick) / Math.LN10 + 0.01);
873+
874+
var maxend = Math.max(Math.abs(rng[0]), Math.abs(rng[1]));
875+
876+
var rangeexp = Math.floor(Math.log(maxend) / Math.LN10 + 0.01);
877+
if(Math.abs(rangeexp) > 3) {
878+
if(ax.exponentformat === 'SI' || ax.exponentformat === 'B') {
879+
ax._tickexponent = 3 * Math.round((rangeexp - 1) / 3);
857880
}
881+
else ax._tickexponent = rangeexp;
858882
}
859883
}
860-
else if(dtick.charAt(0) === 'M') ax._tickround = (dtick.length === 2) ? 'm' : 'y';
884+
// D1 or D2 (log)
861885
else ax._tickround = null;
862886
}
863887

@@ -961,7 +985,7 @@ axes.tickFirst = function(ax) {
961985
var yearFormat = d3.time.format('%Y'),
962986
monthFormat = d3.time.format('%b %Y'),
963987
dayFormat = d3.time.format('%b %-d'),
964-
hourFormat = d3.time.format('%b %-d %Hh'),
988+
yearMonthDayFormat = d3.time.format('%b %-d, %Y'),
965989
minuteFormat = d3.time.format('%H:%M'),
966990
secondFormat = d3.time.format(':%S');
967991

@@ -1053,10 +1077,12 @@ function tickTextObj(ax, x, text) {
10531077
function formatDate(ax, out, hover, extraPrecision) {
10541078
var x = out.x,
10551079
tr = ax._tickround,
1080+
trOriginal = tr,
10561081
d = new Date(x),
10571082
// suffix completes the full date info, to be included
1058-
// with only the first tick
1059-
suffix = '',
1083+
// with only the first tick or if any info before what's
1084+
// shown has changed
1085+
suffix,
10601086
tt;
10611087
if(hover && ax.hoverformat) {
10621088
tt = modDateFormat(ax.hoverformat, x);
@@ -1069,21 +1095,18 @@ function formatDate(ax, out, hover, extraPrecision) {
10691095
else {
10701096
if(extraPrecision) {
10711097
if(isNumeric(tr)) tr += 2;
1072-
else tr = {y: 'm', m: 'd', d: 'H', H: 'M', M: 'S', S: 2}[tr];
1098+
else tr = {y: 'm', m: 'd', d: 'M', M: 'S', S: 2}[tr];
10731099
}
10741100
if(tr === 'y') tt = yearFormat(d);
10751101
else if(tr === 'm') tt = monthFormat(d);
10761102
else {
1077-
if(x === ax._tmin && !hover) {
1078-
suffix = '<br>' + yearFormat(d);
1079-
}
1103+
if(tr === 'd') {
1104+
if(!hover) suffix = '<br>' + yearFormat(d);
10801105

1081-
if(tr === 'd') tt = dayFormat(d);
1082-
else if(tr === 'H') tt = hourFormat(d);
1106+
tt = dayFormat(d);
1107+
}
10831108
else {
1084-
if(x === ax._tmin && !hover) {
1085-
suffix = '<br>' + dayFormat(d) + ', ' + yearFormat(d);
1086-
}
1109+
if(!hover) suffix = '<br>' + yearMonthDayFormat(d);
10871110

10881111
tt = minuteFormat(d);
10891112
if(tr !== 'M') {
@@ -1093,10 +1116,19 @@ function formatDate(ax, out, hover, extraPrecision) {
10931116
.substr(1);
10941117
}
10951118
}
1119+
else if(trOriginal === 'd') {
1120+
// for hover on axes with day ticks, minuteFormat (which
1121+
// only includes %H:%M) isn't enough, you want the date too
1122+
tt = dayFormat(d) + ' ' + tt;
1123+
}
10961124
}
10971125
}
10981126
}
1099-
out.text = tt + suffix;
1127+
if(suffix && (!ax._inCalcTicks || (suffix !== ax._prevSuffix))) {
1128+
tt += suffix;
1129+
ax._prevSuffix = suffix;
1130+
}
1131+
out.text = tt;
11001132
}
11011133

11021134
function formatLog(ax, out, hover, extraPrecision, hideexp) {

test/jasmine/tests/axes_test.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,4 +1392,88 @@ describe('Test axes', function() {
13921392
expect(ax._max).toEqual([{val: 6, pad: 15}]);
13931393
});
13941394
});
1395+
1396+
describe('calcTicks', function() {
1397+
function mockCalc(ax) {
1398+
Axes.setConvert(ax);
1399+
ax.tickfont = {};
1400+
return Axes.calcTicks(ax).map(function(v) { return v.text; });
1401+
}
1402+
1403+
it('provides a new date suffix whenever the suffix changes', function() {
1404+
var textOut = mockCalc({
1405+
type: 'date',
1406+
tickmode: 'linear',
1407+
tick0: '2000-01-01',
1408+
dtick: 14 * 24 * 3600 * 1000, // 14 days
1409+
range: ['1999-12-01', '2000-02-15']
1410+
});
1411+
1412+
var expectedText = [
1413+
'Dec 4<br>1999',
1414+
'Dec 18',
1415+
'Jan 1<br>2000',
1416+
'Jan 15',
1417+
'Jan 29',
1418+
'Feb 12'
1419+
];
1420+
expect(textOut).toEqual(expectedText);
1421+
1422+
textOut = mockCalc({
1423+
type: 'date',
1424+
tickmode: 'linear',
1425+
tick0: '2000-01-01',
1426+
dtick: 12 * 3600 * 1000, // 12 hours
1427+
range: ['2000-01-03 11:00', '2000-01-06']
1428+
});
1429+
1430+
expectedText = [
1431+
'12:00<br>Jan 3, 2000',
1432+
'00:00<br>Jan 4, 2000',
1433+
'12:00',
1434+
'00:00<br>Jan 5, 2000',
1435+
'12:00',
1436+
'00:00<br>Jan 6, 2000'
1437+
];
1438+
expect(textOut).toEqual(expectedText);
1439+
1440+
textOut = mockCalc({
1441+
type: 'date',
1442+
tickmode: 'linear',
1443+
tick0: '2000-01-01',
1444+
dtick: 1000, // 1 sec
1445+
range: ['2000-02-03 23:59:57', '2000-02-04 00:00:02']
1446+
});
1447+
1448+
expectedText = [
1449+
'23:59:57<br>Feb 3, 2000',
1450+
'23:59:58',
1451+
'23:59:59',
1452+
'00:00:00<br>Feb 4, 2000',
1453+
'00:00:01',
1454+
'00:00:02'
1455+
];
1456+
expect(textOut).toEqual(expectedText);
1457+
});
1458+
1459+
it('should give dates extra precision if tick0 is weird', function() {
1460+
var textOut = mockCalc({
1461+
type: 'date',
1462+
tickmode: 'linear',
1463+
tick0: '2000-01-01 00:05',
1464+
dtick: 14 * 24 * 3600 * 1000, // 14 days
1465+
range: ['1999-12-01', '2000-02-15']
1466+
});
1467+
1468+
var expectedText = [
1469+
'00:05<br>Dec 4, 1999',
1470+
'00:05<br>Dec 18, 1999',
1471+
'00:05<br>Jan 1, 2000',
1472+
'00:05<br>Jan 15, 2000',
1473+
'00:05<br>Jan 29, 2000',
1474+
'00:05<br>Feb 12, 2000'
1475+
];
1476+
expect(textOut).toEqual(expectedText);
1477+
});
1478+
});
13951479
});

0 commit comments

Comments
 (0)