windbarb.src.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /* *
  2. * Wind barb series module
  3. *
  4. * (c) 2010-2019 Torstein Honsi
  5. *
  6. * License: www.highcharts.com/license
  7. */
  8. 'use strict';
  9. import H from '../parts/Globals.js';
  10. import onSeriesMixin from '../mixins/on-series.js';
  11. var noop = H.noop,
  12. seriesType = H.seriesType;
  13. /**
  14. * @private
  15. * @class
  16. * @name Highcharts.seriesTypes.windbarb
  17. *
  18. * @augments Highcharts.Series
  19. */
  20. seriesType('windbarb', 'column'
  21. /**
  22. * Wind barbs are a convenient way to represent wind speed and direction in one
  23. * graphical form. Wind direction is given by the stem direction, and wind speed
  24. * by the number and shape of barbs.
  25. *
  26. * @sample {highcharts|highstock} highcharts/demo/windbarb-series/
  27. * Wind barb series
  28. *
  29. * @extends plotOptions.column
  30. * @excluding boostThreshold, marker, connectEnds, connectNulls,
  31. * cropThreshold, dashStyle, gapSize, gapUnit, dataGrouping,
  32. * linecap, shadow, stacking, step
  33. * @since 6.0.0
  34. * @product highcharts highstock
  35. * @optionparent plotOptions.windbarb
  36. */
  37. , {
  38. /**
  39. * The line width of the wind barb symbols.
  40. */
  41. lineWidth: 2,
  42. /**
  43. * The id of another series in the chart that the wind barbs are projected
  44. * on. When `null`, the wind symbols are drawn on the X axis, but offset up
  45. * or down by the `yOffset` setting.
  46. *
  47. * @sample {highcharts|highstock} highcharts/plotoptions/windbarb-onseries
  48. * Projected on area series
  49. *
  50. * @type {string|null}
  51. */
  52. onSeries: null,
  53. states: {
  54. hover: {
  55. lineWidthPlus: 0
  56. }
  57. },
  58. tooltip: {
  59. /**
  60. * The default point format for the wind barb tooltip. Note the
  61. * `point.beaufort` property that refers to the Beaufort wind scale. The
  62. * names can be internationalized by modifying
  63. * `Highcharts.seriesTypes.windbarb.prototype.beaufortNames`.
  64. */
  65. pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.value}</b> ({point.beaufort})<br/>'
  66. },
  67. /**
  68. * Pixel length of the stems.
  69. */
  70. vectorLength: 20,
  71. /**
  72. * Vertical offset from the cartesian position, in pixels. The default value
  73. * makes sure the symbols don't overlap the X axis when `onSeries` is
  74. * `null`, and that they don't overlap the linked series when `onSeries` is
  75. * given.
  76. */
  77. yOffset: -20,
  78. /**
  79. * Horizontal offset from the cartesian position, in pixels. When the chart
  80. * is inverted, this option allows translation like
  81. * [yOffset](#plotOptions.windbarb.yOffset) in non inverted charts.
  82. *
  83. * @since 6.1.0
  84. */
  85. xOffset: 0
  86. }, {
  87. pointArrayMap: ['value', 'direction'],
  88. parallelArrays: ['x', 'value', 'direction'],
  89. beaufortName: ['Calm', 'Light air', 'Light breeze',
  90. 'Gentle breeze', 'Moderate breeze', 'Fresh breeze',
  91. 'Strong breeze', 'Near gale', 'Gale', 'Strong gale', 'Storm',
  92. 'Violent storm', 'Hurricane'],
  93. beaufortFloor: [0, 0.3, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8,
  94. 24.5, 28.5, 32.7],
  95. trackerGroups: ['markerGroup'],
  96. // Get presentational attributes.
  97. pointAttribs: function (point, state) {
  98. var options = this.options,
  99. stroke = point.color || this.color,
  100. strokeWidth = this.options.lineWidth;
  101. if (state) {
  102. stroke = options.states[state].color || stroke;
  103. strokeWidth =
  104. (options.states[state].lineWidth || strokeWidth) +
  105. (options.states[state].lineWidthPlus || 0);
  106. }
  107. return {
  108. 'stroke': stroke,
  109. 'stroke-width': strokeWidth
  110. };
  111. },
  112. markerAttribs: function () {
  113. return undefined;
  114. },
  115. getPlotBox: onSeriesMixin.getPlotBox,
  116. // Create a single wind arrow. It is later rotated around the zero
  117. // centerpoint.
  118. windArrow: function (point) {
  119. var knots = point.value * 1.943844,
  120. level = point.beaufortLevel,
  121. path,
  122. barbs,
  123. u = this.options.vectorLength / 20,
  124. pos = -10;
  125. if (point.isNull) {
  126. return [];
  127. }
  128. if (level === 0) {
  129. return this.chart.renderer.symbols.circle(
  130. -10 * u,
  131. -10 * u,
  132. 20 * u,
  133. 20 * u
  134. );
  135. }
  136. // The stem and the arrow head
  137. path = [
  138. 'M', 0, 7 * u, // base of arrow
  139. 'L', -1.5 * u, 7 * u,
  140. 0, 10 * u,
  141. 1.5 * u, 7 * u,
  142. 0, 7 * u,
  143. 0, -10 * u// top
  144. ];
  145. // For each full 50 knots, add a pennant
  146. barbs = (knots - knots % 50) / 50; // pennants
  147. if (barbs > 0) {
  148. while (barbs--) {
  149. path.push(
  150. pos === -10 ? 'L' : 'M',
  151. 0,
  152. pos * u,
  153. 'L',
  154. 5 * u,
  155. pos * u + 2,
  156. 'L',
  157. 0,
  158. pos * u + 4
  159. );
  160. // Substract from the rest and move position for next
  161. knots -= 50;
  162. pos += 7;
  163. }
  164. }
  165. // For each full 10 knots, add a full barb
  166. barbs = (knots - knots % 10) / 10;
  167. if (barbs > 0) {
  168. while (barbs--) {
  169. path.push(
  170. pos === -10 ? 'L' : 'M',
  171. 0,
  172. pos * u,
  173. 'L',
  174. 7 * u,
  175. pos * u
  176. );
  177. knots -= 10;
  178. pos += 3;
  179. }
  180. }
  181. // For each full 5 knots, add a half barb
  182. barbs = (knots - knots % 5) / 5; // half barbs
  183. if (barbs > 0) {
  184. while (barbs--) {
  185. path.push(
  186. pos === -10 ? 'L' : 'M',
  187. 0,
  188. pos * u,
  189. 'L',
  190. 4 * u,
  191. pos * u
  192. );
  193. knots -= 5;
  194. pos += 3;
  195. }
  196. }
  197. return path;
  198. },
  199. translate: function () {
  200. var beaufortFloor = this.beaufortFloor,
  201. beaufortName = this.beaufortName;
  202. onSeriesMixin.translate.call(this);
  203. this.points.forEach(function (point) {
  204. var level = 0;
  205. // Find the beaufort level (zero based)
  206. for (; level < beaufortFloor.length; level++) {
  207. if (beaufortFloor[level] > point.value) {
  208. break;
  209. }
  210. }
  211. point.beaufortLevel = level - 1;
  212. point.beaufort = beaufortName[level - 1];
  213. });
  214. },
  215. drawPoints: function () {
  216. var chart = this.chart,
  217. yAxis = this.yAxis,
  218. inverted = chart.inverted,
  219. shapeOffset = this.options.vectorLength / 2;
  220. this.points.forEach(function (point) {
  221. var plotX = point.plotX,
  222. plotY = point.plotY;
  223. // Check if it's inside the plot area, but only for the X
  224. // dimension.
  225. if (chart.isInsidePlot(plotX, 0, false)) {
  226. // Create the graphic the first time
  227. if (!point.graphic) {
  228. point.graphic = this.chart.renderer
  229. .path()
  230. .add(this.markerGroup);
  231. }
  232. // Position the graphic
  233. point.graphic
  234. .attr({
  235. d: this.windArrow(point),
  236. translateX: plotX + this.options.xOffset,
  237. translateY: plotY + this.options.yOffset,
  238. rotation: point.direction
  239. })
  240. .attr(this.pointAttribs(point));
  241. } else if (point.graphic) {
  242. point.graphic = point.graphic.destroy();
  243. }
  244. // Set the tooltip anchor position
  245. point.tooltipPos = [
  246. plotX + this.options.xOffset + (inverted && !this.onSeries ?
  247. shapeOffset : 0),
  248. plotY + this.options.yOffset - (inverted ? 0 :
  249. shapeOffset + yAxis.pos - chart.plotTop)
  250. ]; // #6327
  251. }, this);
  252. },
  253. // Fade in the arrows on initiating series.
  254. animate: function (init) {
  255. if (init) {
  256. this.markerGroup.attr({
  257. opacity: 0.01
  258. });
  259. } else {
  260. this.markerGroup.animate({
  261. opacity: 1
  262. }, H.animObject(this.options.animation));
  263. this.animate = null;
  264. }
  265. },
  266. // Don't invert the marker group (#4960)
  267. invertGroups: noop
  268. }, {
  269. isValid: function () {
  270. return H.isNumber(this.value) && this.value >= 0;
  271. }
  272. });
  273. /**
  274. * A `windbarb` series. If the [type](#series.windbarb.type) option is not
  275. * specified, it is inherited from [chart.type](#chart.type).
  276. *
  277. * @extends series,plotOptions.windbarb
  278. * @excluding dataParser, dataURL
  279. * @product highcharts highstock
  280. * @apioption series.windbarb
  281. */
  282. /**
  283. * An array of data points for the series. For the `windbarb` series type,
  284. * points can be given in the following ways:
  285. *
  286. * 1. An array of arrays with 3 values. In this case, the values correspond to
  287. * `x,value,direction`. If the first value is a string, it is applied as the
  288. * name of the point, and the `x` value is inferred.
  289. * ```js
  290. * data: [
  291. * [Date.UTC(2017, 0, 1, 0), 3.3, 90],
  292. * [Date.UTC(2017, 0, 1, 1), 12.1, 180],
  293. * [Date.UTC(2017, 0, 1, 2), 11.1, 270]
  294. * ]
  295. * ```
  296. *
  297. * 2. An array of objects with named values. The following snippet shows only a
  298. * few settings, see the complete options set below. If the total number of
  299. * data points exceeds the series'
  300. * [turboThreshold](#series.area.turboThreshold), this option is not
  301. * available.
  302. * ```js
  303. * data: [{
  304. * x: Date.UTC(2017, 0, 1, 0),
  305. * value: 12.1,
  306. * direction: 90
  307. * }, {
  308. * x: Date.UTC(2017, 0, 1, 1),
  309. * value: 11.1,
  310. * direction: 270
  311. * }]
  312. * ```
  313. *
  314. * @sample {highcharts} highcharts/chart/reflow-true/
  315. * Numerical values
  316. * @sample {highcharts} highcharts/series/data-array-of-arrays/
  317. * Arrays of numeric x and y
  318. * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/
  319. * Arrays of datetime x and y
  320. * @sample {highcharts} highcharts/series/data-array-of-name-value/
  321. * Arrays of point.name and y
  322. * @sample {highcharts} highcharts/series/data-array-of-objects/
  323. * Config objects
  324. *
  325. * @type {Array<Array<(number|string),number,number>|*>}
  326. * @extends series.line.data
  327. * @product highcharts highstock
  328. * @apioption series.windbarb.data
  329. */
  330. /**
  331. * The wind speed in meters per second.
  332. *
  333. * @type {number}
  334. * @product highcharts highstock
  335. * @apioption series.windbarb.data.value
  336. */
  337. /**
  338. * The wind direction in degrees, where 0 is north (pointing towards south).
  339. *
  340. * @type {number}
  341. * @product highcharts highstock
  342. * @apioption series.windbarb.data.direction
  343. */