windbarb.src.js 24 KB

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