parallel-coordinates.src.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /**
  2. * @license Highcharts JS v8.1.2 (2020-06-16)
  3. *
  4. * Support for parallel coordinates in Highcharts
  5. *
  6. * (c) 2010-2019 Pawel Fus
  7. *
  8. * License: www.highcharts.com/license
  9. */
  10. 'use strict';
  11. (function (factory) {
  12. if (typeof module === 'object' && module.exports) {
  13. factory['default'] = factory;
  14. module.exports = factory;
  15. } else if (typeof define === 'function' && define.amd) {
  16. define('highcharts/modules/parallel-coordinates', ['highcharts'], function (Highcharts) {
  17. factory(Highcharts);
  18. factory.Highcharts = Highcharts;
  19. return factory;
  20. });
  21. } else {
  22. factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
  23. }
  24. }(function (Highcharts) {
  25. var _modules = Highcharts ? Highcharts._modules : {};
  26. function _registerModule(obj, path, args, fn) {
  27. if (!obj.hasOwnProperty(path)) {
  28. obj[path] = fn.apply(null, args);
  29. }
  30. }
  31. _registerModule(_modules, 'modules/parallel-coordinates.src.js', [_modules['parts/Axis.js'], _modules['parts/Chart.js'], _modules['parts/Globals.js'], _modules['parts/Utilities.js']], function (Axis, Chart, H, U) {
  32. /* *
  33. *
  34. * Parallel coordinates module
  35. *
  36. * (c) 2010-2020 Pawel Fus
  37. *
  38. * License: www.highcharts.com/license
  39. *
  40. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  41. *
  42. * */
  43. var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, defined = U.defined, erase = U.erase, extend = U.extend, format = U.format, merge = U.merge, pick = U.pick, setOptions = U.setOptions, splat = U.splat, wrap = U.wrap;
  44. // Extensions for parallel coordinates plot.
  45. var ChartProto = Chart.prototype;
  46. var defaultXAxisOptions = {
  47. lineWidth: 0,
  48. tickLength: 0,
  49. opposite: true,
  50. type: 'category'
  51. };
  52. /* eslint-disable valid-jsdoc */
  53. /**
  54. * @optionparent chart
  55. */
  56. var defaultParallelOptions = {
  57. /**
  58. * Flag to render charts as a parallel coordinates plot. In a parallel
  59. * coordinates plot (||-coords) by default all required yAxes are generated
  60. * and the legend is disabled. This feature requires
  61. * `modules/parallel-coordinates.js`.
  62. *
  63. * @sample {highcharts} /highcharts/demo/parallel-coordinates/
  64. * Parallel coordinates demo
  65. * @sample {highcharts} highcharts/parallel-coordinates/polar/
  66. * Star plot, multivariate data in a polar chart
  67. *
  68. * @since 6.0.0
  69. * @product highcharts
  70. * @requires modules/parallel-coordinates
  71. */
  72. parallelCoordinates: false,
  73. /**
  74. * Common options for all yAxes rendered in a parallel coordinates plot.
  75. * This feature requires `modules/parallel-coordinates.js`.
  76. *
  77. * The default options are:
  78. * ```js
  79. * parallelAxes: {
  80. * lineWidth: 1, // classic mode only
  81. * gridlinesWidth: 0, // classic mode only
  82. * title: {
  83. * text: '',
  84. * reserveSpace: false
  85. * },
  86. * labels: {
  87. * x: 0,
  88. * y: 0,
  89. * align: 'center',
  90. * reserveSpace: false
  91. * },
  92. * offset: 0
  93. * }
  94. * ```
  95. *
  96. * @sample {highcharts} highcharts/parallel-coordinates/parallelaxes/
  97. * Set the same tickAmount for all yAxes
  98. *
  99. * @extends yAxis
  100. * @since 6.0.0
  101. * @product highcharts
  102. * @excluding alternateGridColor, breaks, id, gridLineColor,
  103. * gridLineDashStyle, gridLineWidth, minorGridLineColor,
  104. * minorGridLineDashStyle, minorGridLineWidth, plotBands,
  105. * plotLines, angle, gridLineInterpolation, maxColor, maxZoom,
  106. * minColor, scrollbar, stackLabels, stops
  107. * @requires modules/parallel-coordinates
  108. */
  109. parallelAxes: {
  110. lineWidth: 1,
  111. /**
  112. * Titles for yAxes are taken from
  113. * [xAxis.categories](#xAxis.categories). All options for `xAxis.labels`
  114. * applies to parallel coordinates titles. For example, to style
  115. * categories, use [xAxis.labels.style](#xAxis.labels.style).
  116. *
  117. * @excluding align, enabled, margin, offset, position3d, reserveSpace,
  118. * rotation, skew3d, style, text, useHTML, x, y
  119. */
  120. title: {
  121. text: '',
  122. reserveSpace: false
  123. },
  124. labels: {
  125. x: 0,
  126. y: 4,
  127. align: 'center',
  128. reserveSpace: false
  129. },
  130. offset: 0
  131. }
  132. };
  133. setOptions({
  134. chart: defaultParallelOptions
  135. });
  136. /* eslint-disable no-invalid-this */
  137. // Initialize parallelCoordinates
  138. addEvent(Chart, 'init', function (e) {
  139. var options = e.args[0], defaultYAxis = splat(options.yAxis || {}), newYAxes = [];
  140. var yAxisLength = defaultYAxis.length;
  141. /**
  142. * Flag used in parallel coordinates plot to check if chart has ||-coords
  143. * (parallel coords).
  144. *
  145. * @requires module:modules/parallel-coordinates
  146. *
  147. * @name Highcharts.Chart#hasParallelCoordinates
  148. * @type {boolean}
  149. */
  150. this.hasParallelCoordinates = options.chart &&
  151. options.chart.parallelCoordinates;
  152. if (this.hasParallelCoordinates) {
  153. this.setParallelInfo(options);
  154. // Push empty yAxes in case user did not define them:
  155. for (; yAxisLength <= this.parallelInfo.counter; yAxisLength++) {
  156. newYAxes.push({});
  157. }
  158. if (!options.legend) {
  159. options.legend = {};
  160. }
  161. if (typeof options.legend.enabled === 'undefined') {
  162. options.legend.enabled = false;
  163. }
  164. merge(true, options,
  165. // Disable boost
  166. {
  167. boost: {
  168. seriesThreshold: Number.MAX_VALUE
  169. },
  170. plotOptions: {
  171. series: {
  172. boostThreshold: Number.MAX_VALUE
  173. }
  174. }
  175. });
  176. options.yAxis = defaultYAxis.concat(newYAxes);
  177. options.xAxis = merge(defaultXAxisOptions, // docs
  178. splat(options.xAxis || {})[0]);
  179. }
  180. });
  181. // Initialize parallelCoordinates
  182. addEvent(Chart, 'update', function (e) {
  183. var options = e.options;
  184. if (options.chart) {
  185. if (defined(options.chart.parallelCoordinates)) {
  186. this.hasParallelCoordinates = options.chart.parallelCoordinates;
  187. }
  188. this.options.chart.parallelAxes = merge(this.options.chart.parallelAxes, options.chart.parallelAxes);
  189. }
  190. if (this.hasParallelCoordinates) {
  191. // (#10081)
  192. if (options.series) {
  193. this.setParallelInfo(options);
  194. }
  195. this.yAxis.forEach(function (axis) {
  196. axis.update({}, false);
  197. });
  198. }
  199. });
  200. /* eslint-disable valid-jsdoc */
  201. extend(ChartProto, /** @lends Highcharts.Chart.prototype */ {
  202. /**
  203. * Define how many parellel axes we have according to the longest dataset.
  204. * This is quite heavy - loop over all series and check series.data.length
  205. * Consider:
  206. *
  207. * - make this an option, so user needs to set this to get better
  208. * performance
  209. *
  210. * - check only first series for number of points and assume the rest is the
  211. * same
  212. *
  213. * @private
  214. * @function Highcharts.Chart#setParallelInfo
  215. * @param {Highcharts.Options} options
  216. * User options
  217. * @return {void}
  218. * @requires modules/parallel-coordinates
  219. */
  220. setParallelInfo: function (options) {
  221. var chart = this, seriesOptions = options.series;
  222. chart.parallelInfo = {
  223. counter: 0
  224. };
  225. seriesOptions.forEach(function (series) {
  226. if (series.data) {
  227. chart.parallelInfo.counter = Math.max(chart.parallelInfo.counter, series.data.length - 1);
  228. }
  229. });
  230. }
  231. });
  232. // Bind each series to each yAxis. yAxis needs a reference to all series to
  233. // calculate extremes.
  234. addEvent(H.Series, 'bindAxes', function (e) {
  235. if (this.chart.hasParallelCoordinates) {
  236. var series = this;
  237. this.chart.axes.forEach(function (axis) {
  238. series.insert(axis.series);
  239. axis.isDirty = true;
  240. });
  241. series.xAxis = this.chart.xAxis[0];
  242. series.yAxis = this.chart.yAxis[0];
  243. e.preventDefault();
  244. }
  245. });
  246. // Translate each point using corresponding yAxis.
  247. addEvent(H.Series, 'afterTranslate', function () {
  248. var series = this, chart = this.chart, points = series.points, dataLength = points && points.length, closestPointRangePx = Number.MAX_VALUE, lastPlotX, point, i;
  249. if (this.chart.hasParallelCoordinates) {
  250. for (i = 0; i < dataLength; i++) {
  251. point = points[i];
  252. if (defined(point.y)) {
  253. if (chart.polar) {
  254. point.plotX = chart.yAxis[i].angleRad || 0;
  255. }
  256. else if (chart.inverted) {
  257. point.plotX = (chart.plotHeight -
  258. chart.yAxis[i].top +
  259. chart.plotTop);
  260. }
  261. else {
  262. point.plotX = chart.yAxis[i].left - chart.plotLeft;
  263. }
  264. point.clientX = point.plotX;
  265. point.plotY = chart.yAxis[i]
  266. .translate(point.y, false, true, null, true);
  267. if (typeof lastPlotX !== 'undefined') {
  268. closestPointRangePx = Math.min(closestPointRangePx, Math.abs(point.plotX - lastPlotX));
  269. }
  270. lastPlotX = point.plotX;
  271. point.isInside = chart.isInsidePlot(point.plotX, point.plotY, chart.inverted);
  272. }
  273. else {
  274. point.isNull = true;
  275. }
  276. }
  277. this.closestPointRangePx = closestPointRangePx;
  278. }
  279. }, { order: 1 });
  280. // On destroy, we need to remove series from each axis.series
  281. addEvent(H.Series, 'destroy', function () {
  282. if (this.chart.hasParallelCoordinates) {
  283. (this.chart.axes || []).forEach(function (axis) {
  284. if (axis && axis.series) {
  285. erase(axis.series, this);
  286. axis.isDirty = axis.forceRedraw = true;
  287. }
  288. }, this);
  289. }
  290. });
  291. /**
  292. * @private
  293. */
  294. function addFormattedValue(proceed) {
  295. var chart = this.series && this.series.chart, config = proceed.apply(this, Array.prototype.slice.call(arguments, 1)), formattedValue, yAxisOptions, labelFormat, yAxis;
  296. if (chart &&
  297. chart.hasParallelCoordinates &&
  298. !defined(config.formattedValue)) {
  299. yAxis = chart.yAxis[this.x];
  300. yAxisOptions = yAxis.options;
  301. labelFormat = pick(
  302. /**
  303. * Parallel coordinates only. Format that will be used for point.y
  304. * and available in [tooltip.pointFormat](#tooltip.pointFormat) as
  305. * `{point.formattedValue}`. If not set, `{point.formattedValue}`
  306. * will use other options, in this order:
  307. *
  308. * 1. [yAxis.labels.format](#yAxis.labels.format) will be used if
  309. * set
  310. *
  311. * 2. If yAxis is a category, then category name will be displayed
  312. *
  313. * 3. If yAxis is a datetime, then value will use the same format as
  314. * yAxis labels
  315. *
  316. * 4. If yAxis is linear/logarithmic type, then simple value will be
  317. * used
  318. *
  319. * @sample {highcharts}
  320. * /highcharts/parallel-coordinates/tooltipvalueformat/
  321. * Different tooltipValueFormats's
  322. *
  323. * @type {string}
  324. * @default undefined
  325. * @since 6.0.0
  326. * @product highcharts
  327. * @requires modules/parallel-coordinates
  328. * @apioption yAxis.tooltipValueFormat
  329. */
  330. yAxisOptions.tooltipValueFormat, yAxisOptions.labels.format);
  331. if (labelFormat) {
  332. formattedValue = format(labelFormat, extend(this, { value: this.y }), chart);
  333. }
  334. else if (yAxis.dateTime) {
  335. formattedValue = chart.time.dateFormat(chart.time.resolveDTLFormat(yAxisOptions.dateTimeLabelFormats[yAxis.tickPositions.info.unitName]).main, this.y);
  336. }
  337. else if (yAxisOptions.categories) {
  338. formattedValue = yAxisOptions.categories[this.y];
  339. }
  340. else {
  341. formattedValue = this.y;
  342. }
  343. config.formattedValue = config.point.formattedValue = formattedValue;
  344. }
  345. return config;
  346. }
  347. ['line', 'spline'].forEach(function (seriesName) {
  348. wrap(H.seriesTypes[seriesName].prototype.pointClass.prototype, 'getLabelConfig', addFormattedValue);
  349. });
  350. /**
  351. * Support for parallel axes.
  352. * @private
  353. * @class
  354. */
  355. var ParallelAxisAdditions = /** @class */ (function () {
  356. /* *
  357. *
  358. * Constructors
  359. *
  360. * */
  361. function ParallelAxisAdditions(axis) {
  362. this.axis = axis;
  363. }
  364. /* *
  365. *
  366. * Functions
  367. *
  368. * */
  369. /**
  370. * Set predefined left+width and top+height (inverted) for yAxes.
  371. * This method modifies options param.
  372. *
  373. * @private
  374. *
  375. * @param {Array<string>} axisPosition
  376. * ['left', 'width', 'height', 'top'] or ['top', 'height', 'width', 'left']
  377. * for an inverted chart.
  378. *
  379. * @param {Highcharts.AxisOptions} options
  380. * Axis options.
  381. */
  382. ParallelAxisAdditions.prototype.setPosition = function (axisPosition, options) {
  383. var parallel = this, axis = parallel.axis, chart = axis.chart, fraction = ((parallel.position || 0) + 0.5) / (chart.parallelInfo.counter + 1);
  384. if (chart.polar) {
  385. options.angle = 360 * fraction;
  386. }
  387. else {
  388. options[axisPosition[0]] = 100 * fraction + '%';
  389. axis[axisPosition[1]] = options[axisPosition[1]] = 0;
  390. // In case of chart.update(inverted), remove old options:
  391. axis[axisPosition[2]] = options[axisPosition[2]] = null;
  392. axis[axisPosition[3]] = options[axisPosition[3]] = null;
  393. }
  394. };
  395. return ParallelAxisAdditions;
  396. }());
  397. /**
  398. * Axis with parallel support.
  399. * @private
  400. */
  401. var ParallelAxis;
  402. (function (ParallelAxis) {
  403. /**
  404. * Adds support for parallel axes.
  405. * @private
  406. */
  407. function compose(AxisClass) {
  408. /* eslint-disable no-invalid-this */
  409. // On update, keep parallel additions.
  410. AxisClass.keepProps.push('parallel');
  411. addEvent(AxisClass, 'init', onInit);
  412. addEvent(AxisClass, 'afterSetOptions', onAfterSetOptions);
  413. addEvent(AxisClass, 'getSeriesExtremes', onGetSeriesExtremes);
  414. }
  415. ParallelAxis.compose = compose;
  416. /**
  417. * Update default options with predefined for a parallel coords.
  418. * @private
  419. */
  420. function onAfterSetOptions(e) {
  421. var axis = this, chart = axis.chart, parallelCoordinates = axis.parallelCoordinates;
  422. var axisPosition = ['left', 'width', 'height', 'top'];
  423. if (chart.hasParallelCoordinates) {
  424. if (chart.inverted) {
  425. axisPosition = axisPosition.reverse();
  426. }
  427. if (axis.isXAxis) {
  428. axis.options = merge(axis.options, defaultXAxisOptions, e.userOptions);
  429. }
  430. else {
  431. var axisIndex = chart.yAxis.indexOf(axis); // #13608
  432. axis.options = merge(axis.options, axis.chart.options.chart.parallelAxes, e.userOptions);
  433. parallelCoordinates.position = pick(parallelCoordinates.position, axisIndex >= 0 ? axisIndex : chart.yAxis.length);
  434. parallelCoordinates.setPosition(axisPosition, axis.options);
  435. }
  436. }
  437. }
  438. /**
  439. * Each axis should gather extremes from points on a particular position in
  440. * series.data. Not like the default one, which gathers extremes from all
  441. * series bind to this axis. Consider using series.points instead of
  442. * series.yData.
  443. * @private
  444. */
  445. function onGetSeriesExtremes(e) {
  446. var axis = this;
  447. var chart = axis.chart;
  448. var parallelCoordinates = axis.parallelCoordinates;
  449. if (!parallelCoordinates) {
  450. return;
  451. }
  452. if (chart && chart.hasParallelCoordinates && !axis.isXAxis) {
  453. var index = parallelCoordinates.position, currentPoints = [];
  454. axis.series.forEach(function (series) {
  455. if (series.visible &&
  456. defined(series.yData[index])) {
  457. // We need to use push() beacause of null points
  458. currentPoints.push(series.yData[index]);
  459. }
  460. });
  461. axis.dataMin = arrayMin(currentPoints);
  462. axis.dataMax = arrayMax(currentPoints);
  463. e.preventDefault();
  464. }
  465. }
  466. /**
  467. * Add parallel addition
  468. * @private
  469. */
  470. function onInit() {
  471. var axis = this;
  472. if (!axis.parallelCoordinates) {
  473. axis.parallelCoordinates = new ParallelAxisAdditions(axis);
  474. }
  475. }
  476. })(ParallelAxis || (ParallelAxis = {}));
  477. ParallelAxis.compose(Axis);
  478. return ParallelAxis;
  479. });
  480. _registerModule(_modules, 'masters/modules/parallel-coordinates.src.js', [], function () {
  481. });
  482. }));