windbarb.src.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /**
  2. * @license Highcharts JS v8.2.0 (2020-08-20)
  3. *
  4. * Wind barb series module
  5. *
  6. * (c) 2010-2019 Torstein Honsi
  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/windbarb', ['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, 'Mixins/OnSeries.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js']], function (H, U) {
  32. /* *
  33. *
  34. * (c) 2010-2020 Torstein Honsi
  35. *
  36. * License: www.highcharts.com/license
  37. *
  38. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  39. *
  40. * */
  41. var defined = U.defined,
  42. stableSort = U.stableSort;
  43. var seriesTypes = H.seriesTypes;
  44. /**
  45. * @private
  46. * @mixin onSeriesMixin
  47. */
  48. var onSeriesMixin = {
  49. /* eslint-disable valid-jsdoc */
  50. /**
  51. * Override getPlotBox. If the onSeries option is valid,
  52. return the plot box
  53. * of the onSeries,
  54. otherwise proceed as usual.
  55. *
  56. * @private
  57. * @function onSeriesMixin.getPlotBox
  58. * @return {Highcharts.SeriesPlotBoxObject}
  59. */
  60. getPlotBox: function () {
  61. return H.Series.prototype.getPlotBox.call((this.options.onSeries &&
  62. this.chart.get(this.options.onSeries)) || this);
  63. },
  64. /**
  65. * Extend the translate method by placing the point on the related series
  66. *
  67. * @private
  68. * @function onSeriesMixin.translate
  69. * @return {void}
  70. */
  71. translate: function () {
  72. seriesTypes.column.prototype.translate.apply(this);
  73. var series = this,
  74. options = series.options,
  75. chart = series.chart,
  76. points = series.points,
  77. cursor = points.length - 1,
  78. point,
  79. lastPoint,
  80. optionsOnSeries = options.onSeries,
  81. onSeries = (optionsOnSeries &&
  82. chart.get(optionsOnSeries)),
  83. onKey = options.onKey || 'y',
  84. step = onSeries && onSeries.options.step,
  85. onData = (onSeries && onSeries.points),
  86. i = onData && onData.length,
  87. inverted = chart.inverted,
  88. xAxis = series.xAxis,
  89. yAxis = series.yAxis,
  90. xOffset = 0,
  91. leftPoint,
  92. lastX,
  93. rightPoint,
  94. currentDataGrouping,
  95. distanceRatio;
  96. // relate to a master series
  97. if (onSeries && onSeries.visible && i) {
  98. xOffset = (onSeries.pointXOffset || 0) + (onSeries.barW || 0) / 2;
  99. currentDataGrouping = onSeries.currentDataGrouping;
  100. lastX = (onData[i - 1].x +
  101. (currentDataGrouping ? currentDataGrouping.totalRange : 0)); // #2374
  102. // sort the data points
  103. stableSort(points, function (a, b) {
  104. return (a.x - b.x);
  105. });
  106. onKey = 'plot' + onKey[0].toUpperCase() + onKey.substr(1);
  107. while (i-- && points[cursor]) {
  108. leftPoint = onData[i];
  109. point = points[cursor];
  110. point.y = leftPoint.y;
  111. if (leftPoint.x <= point.x &&
  112. typeof leftPoint[onKey] !== 'undefined') {
  113. if (point.x <= lastX) { // #803
  114. point.plotY = leftPoint[onKey];
  115. // interpolate between points, #666
  116. if (leftPoint.x < point.x &&
  117. !step) {
  118. rightPoint = onData[i + 1];
  119. if (rightPoint &&
  120. typeof rightPoint[onKey] !== 'undefined') {
  121. // the distance ratio, between 0 and 1
  122. distanceRatio =
  123. (point.x - leftPoint.x) /
  124. (rightPoint.x - leftPoint.x);
  125. point.plotY +=
  126. distanceRatio *
  127. // the plotY distance
  128. (rightPoint[onKey] - leftPoint[onKey]);
  129. point.y +=
  130. distanceRatio *
  131. (rightPoint.y - leftPoint.y);
  132. }
  133. }
  134. }
  135. cursor--;
  136. i++; // check again for points in the same x position
  137. if (cursor < 0) {
  138. break;
  139. }
  140. }
  141. }
  142. }
  143. // Add plotY position and handle stacking
  144. points.forEach(function (point, i) {
  145. var stackIndex;
  146. point.plotX += xOffset; // #2049
  147. // Undefined plotY means the point is either on axis, outside series
  148. // range or hidden series. If the series is outside the range of the
  149. // x axis it should fall through with an undefined plotY, but then
  150. // we must remove the shapeArgs (#847). For inverted charts, we need
  151. // to calculate position anyway, because series.invertGroups is not
  152. // defined
  153. if (typeof point.plotY === 'undefined' || inverted) {
  154. if (point.plotX >= 0 &&
  155. point.plotX <= xAxis.len) {
  156. // We're inside xAxis range
  157. if (inverted) {
  158. point.plotY = xAxis.translate(point.x, 0, 1, 0, 1);
  159. point.plotX = defined(point.y) ?
  160. yAxis.translate(point.y, 0, 0, 0, 1) :
  161. 0;
  162. }
  163. else {
  164. point.plotY = (xAxis.opposite ? 0 : series.yAxis.len) +
  165. xAxis.offset; // For the windbarb demo
  166. }
  167. }
  168. else {
  169. point.shapeArgs = {}; // 847
  170. }
  171. }
  172. // if multiple flags appear at the same x, order them into a stack
  173. lastPoint = points[i - 1];
  174. if (lastPoint && lastPoint.plotX === point.plotX) {
  175. if (typeof lastPoint.stackIndex === 'undefined') {
  176. lastPoint.stackIndex = 0;
  177. }
  178. stackIndex = lastPoint.stackIndex + 1;
  179. }
  180. point.stackIndex = stackIndex; // #3639
  181. });
  182. this.onSeries = onSeries;
  183. }
  184. /* eslint-enable valid-jsdoc */
  185. };
  186. return onSeriesMixin;
  187. });
  188. _registerModule(_modules, 'Series/WindbarbSeries.js', [_modules['Core/Globals.js'], _modules['Core/Utilities.js'], _modules['Mixins/OnSeries.js']], function (H, U, onSeriesMixin) {
  189. /* *
  190. *
  191. * Wind barb series module
  192. *
  193. * (c) 2010-2020 Torstein Honsi
  194. *
  195. * License: www.highcharts.com/license
  196. *
  197. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  198. *
  199. * */
  200. var animObject = U.animObject,
  201. isNumber = U.isNumber,
  202. pick = U.pick,
  203. seriesType = U.seriesType;
  204. var noop = H.noop;
  205. // eslint-disable-next-line valid-jsdoc
  206. /**
  207. * Once off, register the windbarb approximation for data grouping. This can be
  208. * called anywhere (not necessarily in the translate function), but must happen
  209. * after the data grouping module is loaded and before the wind barb series uses
  210. * it.
  211. * @private
  212. */
  213. function registerApproximation() {
  214. if (H.approximations && !H.approximations.windbarb) {
  215. H.approximations.windbarb = function (values, directions) {
  216. var vectorX = 0,
  217. vectorY = 0,
  218. i,
  219. len = values.length;
  220. for (i = 0; i < len; i++) {
  221. vectorX += values[i] * Math.cos(directions[i] * H.deg2rad);
  222. vectorY += values[i] * Math.sin(directions[i] * H.deg2rad);
  223. }
  224. return [
  225. // Wind speed
  226. values.reduce(function (sum, value) {
  227. return sum + value;
  228. }, 0) / values.length,
  229. // Wind direction
  230. Math.atan2(vectorY, vectorX) / H.deg2rad
  231. ];
  232. };
  233. }
  234. }
  235. registerApproximation();
  236. /**
  237. * @private
  238. * @class
  239. * @name Highcharts.seriesTypes.windbarb
  240. *
  241. * @augments Highcharts.Series
  242. */
  243. seriesType('windbarb', 'column'
  244. /**
  245. * Wind barbs are a convenient way to represent wind speed and direction in
  246. * one graphical form. Wind direction is given by the stem direction, and
  247. * wind speed by the number and shape of barbs.
  248. *
  249. * @sample {highcharts|highstock} highcharts/demo/windbarb-series/
  250. * Wind barb series
  251. *
  252. * @extends plotOptions.column
  253. * @excluding boostThreshold, marker, connectEnds, connectNulls,
  254. * cropThreshold, dashStyle, dragDrop, gapSize, gapUnit,
  255. * linecap, shadow, stacking, step, boostBlending
  256. * @since 6.0.0
  257. * @product highcharts highstock
  258. * @requires modules/windbarb
  259. * @optionparent plotOptions.windbarb
  260. */
  261. , {
  262. /**
  263. * Data grouping options for the wind barbs. In Highcharts, this
  264. * requires the `modules/datagrouping.js` module to be loaded. In
  265. * Highstock, data grouping is included.
  266. *
  267. * @sample highcharts/plotoptions/windbarb-datagrouping
  268. * Wind barb with data grouping
  269. *
  270. * @since 7.1.0
  271. * @product highcharts highstock
  272. */
  273. dataGrouping: {
  274. /**
  275. * Whether to enable data grouping.
  276. *
  277. * @product highcharts highstock
  278. */
  279. enabled: true,
  280. /**
  281. * Approximation function for the data grouping. The default
  282. * returns an average of wind speed and a vector average direction
  283. * weighted by wind speed.
  284. *
  285. * @product highcharts highstock
  286. *
  287. * @type {string|Function}
  288. */
  289. approximation: 'windbarb',
  290. /**
  291. * The approximate data group width.
  292. *
  293. * @product highcharts highstock
  294. */
  295. groupPixelWidth: 30
  296. },
  297. /**
  298. * The line width of the wind barb symbols.
  299. */
  300. lineWidth: 2,
  301. /**
  302. * The id of another series in the chart that the wind barbs are
  303. * projected on. When `null`, the wind symbols are drawn on the X axis,
  304. * but offset up or down by the `yOffset` setting.
  305. *
  306. * @sample {highcharts|highstock} highcharts/plotoptions/windbarb-onseries
  307. * Projected on area series
  308. *
  309. * @type {string|null}
  310. */
  311. onSeries: null,
  312. states: {
  313. hover: {
  314. lineWidthPlus: 0
  315. }
  316. },
  317. tooltip: {
  318. /**
  319. * The default point format for the wind barb tooltip. Note the
  320. * `point.beaufort` property that refers to the Beaufort wind scale.
  321. * The names can be internationalized by modifying
  322. * `Highcharts.seriesTypes.windbarb.prototype.beaufortNames`.
  323. */
  324. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.value}</b> ({point.beaufort})<br/>'
  325. },
  326. /**
  327. * Pixel length of the stems.
  328. */
  329. vectorLength: 20,
  330. /**
  331. * @default value
  332. */
  333. colorKey: 'value',
  334. /**
  335. * Vertical offset from the cartesian position, in pixels. The default
  336. * value makes sure the symbols don't overlap the X axis when `onSeries`
  337. * is `null`, and that they don't overlap the linked series when
  338. * `onSeries` is given.
  339. */
  340. yOffset: -20,
  341. /**
  342. * Horizontal offset from the cartesian position, in pixels. When the
  343. * chart is inverted, this option allows translation like
  344. * [yOffset](#plotOptions.windbarb.yOffset) in non inverted charts.
  345. *
  346. * @since 6.1.0
  347. */
  348. xOffset: 0
  349. }, {
  350. pointArrayMap: ['value', 'direction'],
  351. parallelArrays: ['x', 'value', 'direction'],
  352. beaufortName: ['Calm', 'Light air', 'Light breeze',
  353. 'Gentle breeze', 'Moderate breeze', 'Fresh breeze',
  354. 'Strong breeze', 'Near gale', 'Gale', 'Strong gale', 'Storm',
  355. 'Violent storm', 'Hurricane'],
  356. beaufortFloor: [0, 0.3, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8,
  357. 24.5, 28.5, 32.7],
  358. trackerGroups: ['markerGroup'],
  359. init: function (chart, options) {
  360. registerApproximation();
  361. H.Series.prototype.init.call(this, chart, options);
  362. },
  363. // Get presentational attributes.
  364. pointAttribs: function (point, state) {
  365. var options = this.options,
  366. stroke = point.color || this.color,
  367. strokeWidth = this.options.lineWidth;
  368. if (state) {
  369. stroke = options.states[state].color || stroke;
  370. strokeWidth =
  371. (options.states[state].lineWidth || strokeWidth) +
  372. (options.states[state].lineWidthPlus || 0);
  373. }
  374. return {
  375. 'stroke': stroke,
  376. 'stroke-width': strokeWidth
  377. };
  378. },
  379. markerAttribs: function () {
  380. return;
  381. },
  382. getPlotBox: onSeriesMixin.getPlotBox,
  383. // Create a single wind arrow. It is later rotated around the zero
  384. // centerpoint.
  385. windArrow: function (point) {
  386. var knots = point.value * 1.943844,
  387. level = point.beaufortLevel,
  388. path,
  389. barbs,
  390. u = this.options.vectorLength / 20,
  391. pos = -10;
  392. if (point.isNull) {
  393. return [];
  394. }
  395. if (level === 0) {
  396. return this.chart.renderer.symbols.circle(-10 * u, -10 * u, 20 * u, 20 * u);
  397. }
  398. // The stem and the arrow head
  399. path = [
  400. ['M', 0, 7 * u],
  401. ['L', -1.5 * u, 7 * u],
  402. ['L', 0, 10 * u],
  403. ['L', 1.5 * u, 7 * u],
  404. ['L', 0, 7 * u],
  405. ['L', 0, -10 * u] // top
  406. ];
  407. // For each full 50 knots, add a pennant
  408. barbs = (knots - knots % 50) / 50; // pennants
  409. if (barbs > 0) {
  410. while (barbs--) {
  411. path.push(pos === -10 ? ['L', 0, pos * u] : ['M', 0, pos * u], ['L', 5 * u, pos * u + 2], ['L', 0, pos * u + 4]);
  412. // Substract from the rest and move position for next
  413. knots -= 50;
  414. pos += 7;
  415. }
  416. }
  417. // For each full 10 knots, add a full barb
  418. barbs = (knots - knots % 10) / 10;
  419. if (barbs > 0) {
  420. while (barbs--) {
  421. path.push(pos === -10 ? ['L', 0, pos * u] : ['M', 0, pos * u], ['L', 7 * u, pos * u]);
  422. knots -= 10;
  423. pos += 3;
  424. }
  425. }
  426. // For each full 5 knots, add a half barb
  427. barbs = (knots - knots % 5) / 5; // half barbs
  428. if (barbs > 0) {
  429. while (barbs--) {
  430. path.push(pos === -10 ? ['L', 0, pos * u] : ['M', 0, pos * u], ['L', 4 * u, pos * u]);
  431. knots -= 5;
  432. pos += 3;
  433. }
  434. }
  435. return path;
  436. },
  437. translate: function () {
  438. var beaufortFloor = this.beaufortFloor,
  439. beaufortName = this.beaufortName;
  440. onSeriesMixin.translate.call(this);
  441. this.points.forEach(function (point) {
  442. var level = 0;
  443. // Find the beaufort level (zero based)
  444. for (; level < beaufortFloor.length; level++) {
  445. if (beaufortFloor[level] > point.value) {
  446. break;
  447. }
  448. }
  449. point.beaufortLevel = level - 1;
  450. point.beaufort = beaufortName[level - 1];
  451. });
  452. },
  453. drawPoints: function () {
  454. var chart = this.chart,
  455. yAxis = this.yAxis,
  456. inverted = chart.inverted,
  457. shapeOffset = this.options.vectorLength / 2;
  458. this.points.forEach(function (point) {
  459. var plotX = point.plotX,
  460. plotY = point.plotY;
  461. // Check if it's inside the plot area, but only for the X
  462. // dimension.
  463. if (this.options.clip === false ||
  464. chart.isInsidePlot(plotX, 0, false)) {
  465. // Create the graphic the first time
  466. if (!point.graphic) {
  467. point.graphic = this.chart.renderer
  468. .path()
  469. .add(this.markerGroup)
  470. .addClass('highcharts-point ' +
  471. 'highcharts-color-' +
  472. pick(point.colorIndex, point.series.colorIndex));
  473. }
  474. // Position the graphic
  475. point.graphic
  476. .attr({
  477. d: this.windArrow(point),
  478. translateX: plotX + this.options.xOffset,
  479. translateY: plotY + this.options.yOffset,
  480. rotation: point.direction
  481. });
  482. if (!this.chart.styledMode) {
  483. point.graphic
  484. .attr(this.pointAttribs(point));
  485. }
  486. }
  487. else if (point.graphic) {
  488. point.graphic = point.graphic.destroy();
  489. }
  490. // Set the tooltip anchor position
  491. point.tooltipPos = [
  492. plotX + this.options.xOffset +
  493. (inverted && !this.onSeries ? shapeOffset : 0),
  494. plotY + this.options.yOffset -
  495. (inverted ?
  496. 0 :
  497. shapeOffset + yAxis.pos - chart.plotTop)
  498. ]; // #6327
  499. }, this);
  500. },
  501. // Fade in the arrows on initializing series.
  502. animate: function (init) {
  503. if (init) {
  504. this.markerGroup.attr({
  505. opacity: 0.01
  506. });
  507. }
  508. else {
  509. this.markerGroup.animate({
  510. opacity: 1
  511. }, animObject(this.options.animation));
  512. }
  513. },
  514. // Don't invert the marker group (#4960)
  515. invertGroups: noop,
  516. // No data extremes for the Y axis
  517. getExtremes: function () { return ({}); }
  518. }, {
  519. isValid: function () {
  520. return isNumber(this.value) && this.value >= 0;
  521. }
  522. });
  523. /**
  524. * A `windbarb` series. If the [type](#series.windbarb.type) option is not
  525. * specified, it is inherited from [chart.type](#chart.type).
  526. *
  527. * @extends series,plotOptions.windbarb
  528. * @excluding dataParser, dataURL, boostThreshold, boostBlending
  529. * @product highcharts highstock
  530. * @requires modules/windbarb
  531. * @apioption series.windbarb
  532. */
  533. /**
  534. * An array of data points for the series. For the `windbarb` series type,
  535. * points can be given in the following ways:
  536. *
  537. * 1. An array of arrays with 3 values. In this case, the values correspond to
  538. * `x,value,direction`. If the first value is a string, it is applied as the
  539. * name of the point, and the `x` value is inferred.
  540. * ```js
  541. * data: [
  542. * [Date.UTC(2017, 0, 1, 0), 3.3, 90],
  543. * [Date.UTC(2017, 0, 1, 1), 12.1, 180],
  544. * [Date.UTC(2017, 0, 1, 2), 11.1, 270]
  545. * ]
  546. * ```
  547. *
  548. * 2. An array of objects with named values. The following snippet shows only a
  549. * few settings, see the complete options set below. If the total number of
  550. * data points exceeds the series'
  551. * [turboThreshold](#series.area.turboThreshold), this option is not
  552. * available.
  553. * ```js
  554. * data: [{
  555. * x: Date.UTC(2017, 0, 1, 0),
  556. * value: 12.1,
  557. * direction: 90
  558. * }, {
  559. * x: Date.UTC(2017, 0, 1, 1),
  560. * value: 11.1,
  561. * direction: 270
  562. * }]
  563. * ```
  564. *
  565. * @sample {highcharts} highcharts/chart/reflow-true/
  566. * Numerical values
  567. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  568. * Arrays of numeric x and y
  569. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  570. * Arrays of datetime x and y
  571. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  572. * Arrays of point.name and y
  573. * @sample {highcharts} highcharts/series/data-array-of-objects/
  574. * Config objects
  575. *
  576. * @type {Array<Array<(number|string),number,number>|*>}
  577. * @extends series.line.data
  578. * @product highcharts highstock
  579. * @apioption series.windbarb.data
  580. */
  581. /**
  582. * The wind speed in meters per second.
  583. *
  584. * @type {number|null}
  585. * @product highcharts highstock
  586. * @apioption series.windbarb.data.value
  587. */
  588. /**
  589. * The wind direction in degrees, where 0 is north (pointing towards south).
  590. *
  591. * @type {number}
  592. * @product highcharts highstock
  593. * @apioption series.windbarb.data.direction
  594. */
  595. ''; // adds doclets above to transpiled file
  596. });
  597. _registerModule(_modules, 'masters/modules/windbarb.src.js', [], function () {
  598. });
  599. }));