From 62fe4cd34aaf8ef7a1e80209a2aa67a338006a27 Mon Sep 17 00:00:00 2001 From: Jisk Attema Date: Mon, 29 Jan 2018 16:40:04 +0100 Subject: [PATCH] Add scientific notation option and also deal with a few other corner cases on linear axis on Chartjs plots. Closes issue #142 --- src/widgets/chartjs-scilinear-formatter.js | 59 ++++++++++++++++++++++ src/widgets/view-factory.js | 4 ++ 2 files changed, 63 insertions(+) create mode 100644 src/widgets/chartjs-scilinear-formatter.js diff --git a/src/widgets/chartjs-scilinear-formatter.js b/src/widgets/chartjs-scilinear-formatter.js new file mode 100644 index 00000000..140c1a7d --- /dev/null +++ b/src/widgets/chartjs-scilinear-formatter.js @@ -0,0 +1,59 @@ +/** + * Formatter for tick labels on a linear axis in chartjs + * + * Override the default behavious such that it: + * * switches to scientific notation for large numbers with a lot of 'trailing zeros': 1e25 + * * only prints the trailing (least significant) digits when zoomin in: 100000000000.1 prints as '..000.1' + * + * see issue #142 + */ +module.exports = function (tickValue, index, ticks) { + // Find the proper tick spacing + // if we have lots of ticks, don't use the ones + var delta = Math.abs(ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0]); + + if (tickValue === 0) { + return '0'; + } + + // Find the order of magnitude of the least significant digit + var leastSignificantOOM = Math.floor(Math.log10(delta)); + + // Find the order magnitude of the most significant digit + var logTicks = []; + ticks.forEach(function (value, i) { + if (value !== 0) { + logTicks.push(Math.log10(Math.abs(value))); + } + }); + var mostSignificantOOM = Math.floor(Math.max.apply(Math, logTicks)); + + // We can chose between 3 different notations for '12.34': + // fixed precision: 12.34 + // scientific notation: 1.234e1 + // and truncated ..34 + // find how long each notation would be, and chose the optimal one + + // when using scientific notation (1,2303e2), how many digits would it take? + var totalNumberOfDigits = mostSignificantOOM + 1 - leastSignificantOOM; + + // when using fixed notation (123.03), how many digits would it take? + var fixedNotationDitigts = 1 + + Math.max(0, mostSignificantOOM) + // digits before '.' + Math.max(0, Math.abs(leastSignificantOOM)); // digits after '.' + + // when truncating the string to the last 5 digits, it is of course 5 digits + + var tickString = ''; + if (fixedNotationDitigts < 9) { + var numDecimal = Math.max(Math.min(-1 * leastSignificantOOM, 20), 0); // toFixed has a max of 20 decimal places + tickString = tickValue.toFixed(numDecimal); + } else if (totalNumberOfDigits < 9) { + tickString = tickValue.toExponential(totalNumberOfDigits - 1); + } else { + tickString = tickValue.toFixed(Math.max(0, -1 * leastSignificantOOM)); + tickString = '..' + tickString.substring(tickString.length - 5, tickString.length); + } + + return tickString; +}; diff --git a/src/widgets/view-factory.js b/src/widgets/view-factory.js index 4bd4f2cb..daf7da7f 100644 --- a/src/widgets/view-factory.js +++ b/src/widgets/view-factory.js @@ -14,6 +14,10 @@ extendWithErrorBar(Chart, 'horizontalBar', 'horizontalBarError'); var extendWithDurationScale = require('./chartjs-duration-scale'); extendWithDurationScale(Chart); +// replace the default linear scale with a smarter formatter +var SciLinearFormatter = require('./chartjs-scilinear-formatter'); +Chart.scaleService.updateScaleDefaults('linear', { ticks: { callback: SciLinearFormatter } }); + var widgetEntry = AmpersandModel.extend({ props: { modelType: {type: 'string', required: true},