| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 | /* * * Wind barb series module * * (c) 2010-2019 Torstein Honsi * * License: www.highcharts.com/license */'use strict';import H from '../parts/Globals.js';import onSeriesMixin from '../mixins/on-series.js';var noop = H.noop,    seriesType = H.seriesType;/** * @private * @class * @name Highcharts.seriesTypes.windbarb * * @augments Highcharts.Series */seriesType('windbarb', 'column'    /** * Wind barbs are a convenient way to represent wind speed and direction in one * graphical form. Wind direction is given by the stem direction, and wind speed * by the number and shape of barbs. * * @sample {highcharts|highstock} highcharts/demo/windbarb-series/ *         Wind barb series * * @extends      plotOptions.column * @excluding    boostThreshold, marker, connectEnds, connectNulls, *               cropThreshold, dashStyle, gapSize, gapUnit, dataGrouping, *               linecap, shadow, stacking, step * @since        6.0.0 * @product      highcharts highstock * @optionparent plotOptions.windbarb */    , {    /**     * The line width of the wind barb symbols.     */        lineWidth: 2,        /**     * The id of another series in the chart that the wind barbs are projected     * on. When `null`, the wind symbols are drawn on the X axis, but offset up     * or down by the `yOffset` setting.     *     * @sample {highcharts|highstock} highcharts/plotoptions/windbarb-onseries     *         Projected on area series     *     * @type {string|null}     */        onSeries: null,        states: {            hover: {                lineWidthPlus: 0            }        },        tooltip: {        /**         * The default point format for the wind barb tooltip. Note the         * `point.beaufort` property that refers to the Beaufort wind scale. The         * names can be internationalized by modifying         * `Highcharts.seriesTypes.windbarb.prototype.beaufortNames`.         */            pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.value}</b> ({point.beaufort})<br/>'        },        /**     * Pixel length of the stems.     */        vectorLength: 20,        /**     * Vertical offset from the cartesian position, in pixels. The default value     * makes sure the symbols don't overlap the X axis when `onSeries` is     * `null`, and that they don't overlap the linked series when `onSeries` is     * given.     */        yOffset: -20,        /**     * Horizontal offset from the cartesian position, in pixels. When the chart     * is inverted, this option allows translation like     * [yOffset](#plotOptions.windbarb.yOffset) in non inverted charts.     *     * @since 6.1.0     */        xOffset: 0    }, {        pointArrayMap: ['value', 'direction'],        parallelArrays: ['x', 'value', 'direction'],        beaufortName: ['Calm', 'Light air', 'Light breeze',            'Gentle breeze', 'Moderate breeze', 'Fresh breeze',            'Strong breeze', 'Near gale', 'Gale', 'Strong gale', 'Storm',            'Violent storm', 'Hurricane'],        beaufortFloor: [0, 0.3, 1.6, 3.4, 5.5, 8.0, 10.8, 13.9, 17.2, 20.8,            24.5, 28.5, 32.7],        trackerGroups: ['markerGroup'],        // Get presentational attributes.        pointAttribs: function (point, state) {            var options = this.options,                stroke = point.color || this.color,                strokeWidth = this.options.lineWidth;            if (state) {                stroke = options.states[state].color || stroke;                strokeWidth =                (options.states[state].lineWidth || strokeWidth) +                (options.states[state].lineWidthPlus || 0);            }            return {                'stroke': stroke,                'stroke-width': strokeWidth            };        },        markerAttribs: function () {            return undefined;        },        getPlotBox: onSeriesMixin.getPlotBox,        // Create a single wind arrow. It is later rotated around the zero        // centerpoint.        windArrow: function (point) {            var knots = point.value * 1.943844,                level = point.beaufortLevel,                path,                barbs,                u = this.options.vectorLength / 20,                pos = -10;            if (point.isNull) {                return [];            }            if (level === 0) {                return this.chart.renderer.symbols.circle(                    -10 * u,                    -10 * u,                    20 * u,                    20 * u                );            }            // The stem and the arrow head            path = [                'M', 0, 7 * u, // base of arrow                'L', -1.5 * u, 7 * u,                0, 10 * u,                1.5 * u, 7 * u,                0, 7 * u,                0, -10 * u// top            ];            // For each full 50 knots, add a pennant            barbs = (knots - knots % 50) / 50; // pennants            if (barbs > 0) {                while (barbs--) {                    path.push(                        pos === -10 ? 'L' : 'M',                        0,                        pos * u,                        'L',                        5 * u,                        pos * u + 2,                        'L',                        0,                        pos * u + 4                    );                    // Substract from the rest and move position for next                    knots -= 50;                    pos += 7;                }            }            // For each full 10 knots, add a full barb            barbs = (knots - knots % 10) / 10;            if (barbs > 0) {                while (barbs--) {                    path.push(                        pos === -10 ? 'L' : 'M',                        0,                        pos * u,                        'L',                        7 * u,                        pos * u                    );                    knots -= 10;                    pos += 3;                }            }            // For each full 5 knots, add a half barb            barbs = (knots - knots % 5) / 5; // half barbs            if (barbs > 0) {                while (barbs--) {                    path.push(                        pos === -10 ? 'L' : 'M',                        0,                        pos * u,                        'L',                        4 * u,                        pos * u                    );                    knots -= 5;                    pos += 3;                }            }            return path;        },        translate: function () {            var beaufortFloor = this.beaufortFloor,                beaufortName = this.beaufortName;            onSeriesMixin.translate.call(this);            this.points.forEach(function (point) {                var level = 0;                // Find the beaufort level (zero based)                for (; level < beaufortFloor.length; level++) {                    if (beaufortFloor[level] > point.value) {                        break;                    }                }                point.beaufortLevel = level - 1;                point.beaufort = beaufortName[level - 1];            });        },        drawPoints: function () {            var chart = this.chart,                yAxis = this.yAxis,                inverted = chart.inverted,                shapeOffset = this.options.vectorLength / 2;            this.points.forEach(function (point) {                var plotX = point.plotX,                    plotY = point.plotY;                // Check if it's inside the plot area, but only for the X                // dimension.                if (chart.isInsidePlot(plotX, 0, false)) {                    // Create the graphic the first time                    if (!point.graphic) {                        point.graphic = this.chart.renderer                            .path()                            .add(this.markerGroup);                    }                    // Position the graphic                    point.graphic                        .attr({                            d: this.windArrow(point),                            translateX: plotX + this.options.xOffset,                            translateY: plotY + this.options.yOffset,                            rotation: point.direction                        })                        .attr(this.pointAttribs(point));                } else if (point.graphic) {                    point.graphic = point.graphic.destroy();                }                // Set the tooltip anchor position                point.tooltipPos = [                    plotX + this.options.xOffset + (inverted && !this.onSeries ?                        shapeOffset : 0),                    plotY + this.options.yOffset - (inverted ? 0 :                        shapeOffset + yAxis.pos - chart.plotTop)                ]; // #6327            }, this);        },        // Fade in the arrows on initiating series.        animate: function (init) {            if (init) {                this.markerGroup.attr({                    opacity: 0.01                });            } else {                this.markerGroup.animate({                    opacity: 1                }, H.animObject(this.options.animation));                this.animate = null;            }        },        // Don't invert the marker group (#4960)        invertGroups: noop    }, {        isValid: function () {            return H.isNumber(this.value) && this.value >= 0;        }    });/** * A `windbarb` series. If the [type](#series.windbarb.type) option is not * specified, it is inherited from [chart.type](#chart.type). * * @extends   series,plotOptions.windbarb * @excluding dataParser, dataURL * @product   highcharts highstock * @apioption series.windbarb *//** * An array of data points for the series. For the `windbarb` series type, * points can be given in the following ways: * * 1. An array of arrays with 3 values. In this case, the values correspond to *    `x,value,direction`. If the first value is a string, it is applied as the *    name of the point, and the `x` value is inferred. *    ```js *       data: [ *           [Date.UTC(2017, 0, 1, 0), 3.3, 90], *           [Date.UTC(2017, 0, 1, 1), 12.1, 180], *           [Date.UTC(2017, 0, 1, 2), 11.1, 270] *       ] *    ``` * * 2. An array of objects with named values. The following snippet shows only a *    few settings, see the complete options set below. If the total number of *    data points exceeds the series' *    [turboThreshold](#series.area.turboThreshold), this option is not *    available. *    ```js *       data: [{ *           x: Date.UTC(2017, 0, 1, 0), *           value: 12.1, *           direction: 90 *       }, { *           x: Date.UTC(2017, 0, 1, 1), *           value: 11.1, *           direction: 270 *       }] *    ``` * * @sample {highcharts} highcharts/chart/reflow-true/ *         Numerical values * @sample {highcharts} highcharts/series/data-array-of-arrays/ *         Arrays of numeric x and y * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/ *         Arrays of datetime x and y * @sample {highcharts} highcharts/series/data-array-of-name-value/ *         Arrays of point.name and y * @sample {highcharts} highcharts/series/data-array-of-objects/ *         Config objects * * @type      {Array<Array<(number|string),number,number>|*>} * @extends   series.line.data * @product   highcharts highstock * @apioption series.windbarb.data *//** * The wind speed in meters per second. * * @type      {number} * @product   highcharts highstock * @apioption series.windbarb.data.value *//** * The wind direction in degrees, where 0 is north (pointing towards south). * * @type      {number} * @product   highcharts highstock * @apioption series.windbarb.data.direction */
 |