windbarb.src.js 19 KB

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