chore: version inicial del codigo fuente del LMS Estafeta
This commit is contained in:
@@ -0,0 +1,624 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Point from '../Core/Series/Point.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var defined = U.defined, extend = U.extend, isArray = U.isArray, isNumber = U.isNumber, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Series.js';
|
||||
var noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes, seriesProto = Series.prototype, pointProto = Point.prototype;
|
||||
/**
|
||||
* The area range series is a carteseian series with higher and lower values for
|
||||
* each point along an X axis, where the area between the values is shaded.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/arearange/
|
||||
* Area range chart
|
||||
* @sample {highstock} stock/demo/arearange/
|
||||
* Area range chart
|
||||
*
|
||||
* @extends plotOptions.area
|
||||
* @product highcharts highstock
|
||||
* @excluding stack, stacking
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.arearange
|
||||
*/
|
||||
seriesType('arearange', 'area', {
|
||||
/**
|
||||
* Whether to apply a drop shadow to the graph line. Since 2.3 the shadow
|
||||
* can be an object configuration containing `color`, `offsetX`, `offsetY`,
|
||||
* `opacity` and `width`.
|
||||
*
|
||||
* @type {boolean|Highcharts.ShadowOptionsObject}
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.arearange.shadow
|
||||
*/
|
||||
/**
|
||||
* @default low
|
||||
* @apioption plotOptions.arearange.colorKey
|
||||
*/
|
||||
/**
|
||||
* Pixel width of the arearange graph line.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
lineWidth: 1,
|
||||
threshold: null,
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{series.color}">\u25CF</span> ' +
|
||||
'{series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'
|
||||
},
|
||||
/**
|
||||
* Whether the whole area or just the line should respond to mouseover
|
||||
* tooltips and other mouse or touch events.
|
||||
*
|
||||
* @since 2.3.0
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
trackByArea: true,
|
||||
/**
|
||||
* Extended data labels for range series types. Range series data labels use
|
||||
* no `x` and `y` options. Instead, they have `xLow`, `xHigh`, `yLow` and
|
||||
* `yHigh` options to allow the higher and lower data label sets
|
||||
* individually.
|
||||
*
|
||||
* @declare Highcharts.SeriesAreaRangeDataLabelsOptionsObject
|
||||
* @exclude x, y
|
||||
* @since 2.3.0
|
||||
* @product highcharts highstock
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dataLabels: {
|
||||
align: void 0,
|
||||
verticalAlign: void 0,
|
||||
/**
|
||||
* X offset of the lower data labels relative to the point value.
|
||||
*
|
||||
* @sample highcharts/plotoptions/arearange-datalabels/
|
||||
* Data labels on range series
|
||||
* @sample highcharts/plotoptions/arearange-datalabels/
|
||||
* Data labels on range series
|
||||
*/
|
||||
xLow: 0,
|
||||
/**
|
||||
* X offset of the higher data labels relative to the point value.
|
||||
*
|
||||
* @sample highcharts/plotoptions/arearange-datalabels/
|
||||
* Data labels on range series
|
||||
*/
|
||||
xHigh: 0,
|
||||
/**
|
||||
* Y offset of the lower data labels relative to the point value.
|
||||
*
|
||||
* @sample highcharts/plotoptions/arearange-datalabels/
|
||||
* Data labels on range series
|
||||
*/
|
||||
yLow: 0,
|
||||
/**
|
||||
* Y offset of the higher data labels relative to the point value.
|
||||
*
|
||||
* @sample highcharts/plotoptions/arearange-datalabels/
|
||||
* Data labels on range series
|
||||
*/
|
||||
yHigh: 0
|
||||
}
|
||||
// Prototype members
|
||||
}, {
|
||||
pointArrayMap: ['low', 'high'],
|
||||
pointValKey: 'low',
|
||||
deferTranslatePolar: true,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
toYData: function (point) {
|
||||
return [point.low, point.high];
|
||||
},
|
||||
/**
|
||||
* Translate a point's plotHigh from the internal angle and radius measures
|
||||
* to true plotHigh coordinates. This is an addition of the toXY method
|
||||
* found in Polar.js, because it runs too early for arearanges to be
|
||||
* considered (#3419).
|
||||
* @private
|
||||
*/
|
||||
highToXY: function (point) {
|
||||
// Find the polar plotX and plotY
|
||||
var chart = this.chart, xy = this.xAxis.postTranslate(point.rectPlotX, this.yAxis.len - point.plotHigh);
|
||||
point.plotHighX = xy.x - chart.plotLeft;
|
||||
point.plotHigh = xy.y - chart.plotTop;
|
||||
point.plotLowX = point.plotX;
|
||||
},
|
||||
/**
|
||||
* Translate data points from raw values x and y to plotX and plotY.
|
||||
* @private
|
||||
*/
|
||||
translate: function () {
|
||||
var series = this, yAxis = series.yAxis, hasModifyValue = !!series.modifyValue;
|
||||
seriesTypes.area.prototype.translate.apply(series);
|
||||
// Set plotLow and plotHigh
|
||||
series.points.forEach(function (point) {
|
||||
var high = point.high, plotY = point.plotY;
|
||||
if (point.isNull) {
|
||||
point.plotY = null;
|
||||
}
|
||||
else {
|
||||
point.plotLow = plotY;
|
||||
point.plotHigh = yAxis.translate(hasModifyValue ?
|
||||
series.modifyValue(high, point) :
|
||||
high, 0, 1, 0, 1);
|
||||
if (hasModifyValue) {
|
||||
point.yBottom = point.plotHigh;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Postprocess plotHigh
|
||||
if (this.chart.polar) {
|
||||
this.points.forEach(function (point) {
|
||||
series.highToXY(point);
|
||||
point.tooltipPos = [
|
||||
(point.plotHighX + point.plotLowX) / 2,
|
||||
(point.plotHigh + point.plotLow) / 2
|
||||
];
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Extend the line series' getSegmentPath method by applying the segment
|
||||
* path to both lower and higher values of the range.
|
||||
* @private
|
||||
*/
|
||||
getGraphPath: function (points) {
|
||||
var highPoints = [], highAreaPoints = [], i, getGraphPath = seriesTypes.area.prototype.getGraphPath, point, pointShim, linePath, lowerPath, options = this.options, polar = this.chart.polar, connectEnds = polar && options.connectEnds !== false, connectNulls = options.connectNulls, step = options.step, higherPath, higherAreaPath;
|
||||
points = points || this.points;
|
||||
// Create the top line and the top part of the area fill. The area fill
|
||||
// compensates for null points by drawing down to the lower graph,
|
||||
// moving across the null gap and starting again at the lower graph.
|
||||
i = points.length;
|
||||
while (i--) {
|
||||
point = points[i];
|
||||
// Support for polar
|
||||
var highAreaPoint = polar ? {
|
||||
plotX: point.rectPlotX,
|
||||
plotY: point.yBottom,
|
||||
doCurve: false // #5186, gaps in areasplinerange fill
|
||||
} : {
|
||||
plotX: point.plotX,
|
||||
plotY: point.plotY,
|
||||
doCurve: false // #5186, gaps in areasplinerange fill
|
||||
};
|
||||
if (!point.isNull &&
|
||||
!connectEnds &&
|
||||
!connectNulls &&
|
||||
(!points[i + 1] || points[i + 1].isNull)) {
|
||||
highAreaPoints.push(highAreaPoint);
|
||||
}
|
||||
pointShim = {
|
||||
polarPlotY: point.polarPlotY,
|
||||
rectPlotX: point.rectPlotX,
|
||||
yBottom: point.yBottom,
|
||||
// plotHighX is for polar charts
|
||||
plotX: pick(point.plotHighX, point.plotX),
|
||||
plotY: point.plotHigh,
|
||||
isNull: point.isNull
|
||||
};
|
||||
highAreaPoints.push(pointShim);
|
||||
highPoints.push(pointShim);
|
||||
if (!point.isNull &&
|
||||
!connectEnds &&
|
||||
!connectNulls &&
|
||||
(!points[i - 1] || points[i - 1].isNull)) {
|
||||
highAreaPoints.push(highAreaPoint);
|
||||
}
|
||||
}
|
||||
// Get the paths
|
||||
lowerPath = getGraphPath.call(this, points);
|
||||
if (step) {
|
||||
if (step === true) {
|
||||
step = 'left';
|
||||
}
|
||||
options.step = {
|
||||
left: 'right',
|
||||
center: 'center',
|
||||
right: 'left'
|
||||
}[step]; // swap for reading in getGraphPath
|
||||
}
|
||||
higherPath = getGraphPath.call(this, highPoints);
|
||||
higherAreaPath = getGraphPath.call(this, highAreaPoints);
|
||||
options.step = step;
|
||||
// Create a line on both top and bottom of the range
|
||||
linePath = []
|
||||
.concat(lowerPath, higherPath);
|
||||
// For the area path, we need to change the 'move' statement
|
||||
// into 'lineTo'
|
||||
if (!this.chart.polar && higherAreaPath[0] && higherAreaPath[0][0] === 'M') {
|
||||
// This probably doesn't work for spline
|
||||
higherAreaPath[0] = ['L', higherAreaPath[0][1], higherAreaPath[0][2]];
|
||||
}
|
||||
this.graphPath = linePath;
|
||||
this.areaPath = lowerPath.concat(higherAreaPath);
|
||||
// Prepare for sideways animation
|
||||
linePath.isArea = true;
|
||||
linePath.xMap = lowerPath.xMap;
|
||||
this.areaPath.xMap = lowerPath.xMap;
|
||||
return linePath;
|
||||
},
|
||||
/**
|
||||
* Extend the basic drawDataLabels method by running it for both lower and
|
||||
* higher values.
|
||||
* @private
|
||||
*/
|
||||
drawDataLabels: function () {
|
||||
var data = this.points, length = data.length, i, originalDataLabels = [], dataLabelOptions = this.options.dataLabels, point, up, inverted = this.chart.inverted, upperDataLabelOptions, lowerDataLabelOptions;
|
||||
// Split into upper and lower options. If data labels is an array, the
|
||||
// first element is the upper label, the second is the lower.
|
||||
//
|
||||
// TODO: We want to change this and allow multiple labels for both upper
|
||||
// and lower values in the future - introducing some options for which
|
||||
// point value to use as Y for the dataLabel, so that this could be
|
||||
// handled in Series.drawDataLabels. This would also improve performance
|
||||
// since we now have to loop over all the points multiple times to work
|
||||
// around the data label logic.
|
||||
if (isArray(dataLabelOptions)) {
|
||||
if (dataLabelOptions.length > 1) {
|
||||
upperDataLabelOptions = dataLabelOptions[0];
|
||||
lowerDataLabelOptions = dataLabelOptions[1];
|
||||
}
|
||||
else {
|
||||
upperDataLabelOptions = dataLabelOptions[0];
|
||||
lowerDataLabelOptions = { enabled: false };
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Make copies
|
||||
upperDataLabelOptions = extend({}, dataLabelOptions);
|
||||
upperDataLabelOptions.x = dataLabelOptions.xHigh;
|
||||
upperDataLabelOptions.y = dataLabelOptions.yHigh;
|
||||
lowerDataLabelOptions = extend({}, dataLabelOptions);
|
||||
lowerDataLabelOptions.x = dataLabelOptions.xLow;
|
||||
lowerDataLabelOptions.y = dataLabelOptions.yLow;
|
||||
}
|
||||
// Draw upper labels
|
||||
if (upperDataLabelOptions.enabled || this._hasPointLabels) {
|
||||
// Set preliminary values for plotY and dataLabel
|
||||
// and draw the upper labels
|
||||
i = length;
|
||||
while (i--) {
|
||||
point = data[i];
|
||||
if (point) {
|
||||
up = upperDataLabelOptions.inside ?
|
||||
point.plotHigh < point.plotLow :
|
||||
point.plotHigh > point.plotLow;
|
||||
point.y = point.high;
|
||||
point._plotY = point.plotY;
|
||||
point.plotY = point.plotHigh;
|
||||
// Store original data labels and set preliminary label
|
||||
// objects to be picked up in the uber method
|
||||
originalDataLabels[i] = point.dataLabel;
|
||||
point.dataLabel = point.dataLabelUpper;
|
||||
// Set the default offset
|
||||
point.below = up;
|
||||
if (inverted) {
|
||||
if (!upperDataLabelOptions.align) {
|
||||
upperDataLabelOptions.align = up ? 'right' : 'left';
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!upperDataLabelOptions.verticalAlign) {
|
||||
upperDataLabelOptions.verticalAlign = up ?
|
||||
'top' :
|
||||
'bottom';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.options.dataLabels = upperDataLabelOptions;
|
||||
if (seriesProto.drawDataLabels) {
|
||||
// #1209:
|
||||
seriesProto.drawDataLabels.apply(this, arguments);
|
||||
}
|
||||
// Reset state after the upper labels were created. Move
|
||||
// it to point.dataLabelUpper and reassign the originals.
|
||||
// We do this here to support not drawing a lower label.
|
||||
i = length;
|
||||
while (i--) {
|
||||
point = data[i];
|
||||
if (point) {
|
||||
point.dataLabelUpper = point.dataLabel;
|
||||
point.dataLabel = originalDataLabels[i];
|
||||
delete point.dataLabels;
|
||||
point.y = point.low;
|
||||
point.plotY = point._plotY;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Draw lower labels
|
||||
if (lowerDataLabelOptions.enabled || this._hasPointLabels) {
|
||||
i = length;
|
||||
while (i--) {
|
||||
point = data[i];
|
||||
if (point) {
|
||||
up = lowerDataLabelOptions.inside ?
|
||||
point.plotHigh < point.plotLow :
|
||||
point.plotHigh > point.plotLow;
|
||||
// Set the default offset
|
||||
point.below = !up;
|
||||
if (inverted) {
|
||||
if (!lowerDataLabelOptions.align) {
|
||||
lowerDataLabelOptions.align = up ? 'left' : 'right';
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!lowerDataLabelOptions.verticalAlign) {
|
||||
lowerDataLabelOptions.verticalAlign = up ?
|
||||
'bottom' :
|
||||
'top';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.options.dataLabels = lowerDataLabelOptions;
|
||||
if (seriesProto.drawDataLabels) {
|
||||
seriesProto.drawDataLabels.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
// Merge upper and lower into point.dataLabels for later destroying
|
||||
if (upperDataLabelOptions.enabled) {
|
||||
i = length;
|
||||
while (i--) {
|
||||
point = data[i];
|
||||
if (point) {
|
||||
point.dataLabels = [
|
||||
point.dataLabelUpper,
|
||||
point.dataLabel
|
||||
].filter(function (label) {
|
||||
return !!label;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reset options
|
||||
this.options.dataLabels = dataLabelOptions;
|
||||
},
|
||||
alignDataLabel: function () {
|
||||
seriesTypes.column.prototype.alignDataLabel
|
||||
.apply(this, arguments);
|
||||
},
|
||||
drawPoints: function () {
|
||||
var series = this, pointLength = series.points.length, point, i;
|
||||
// Draw bottom points
|
||||
seriesProto.drawPoints
|
||||
.apply(series, arguments);
|
||||
// Prepare drawing top points
|
||||
i = 0;
|
||||
while (i < pointLength) {
|
||||
point = series.points[i];
|
||||
// Save original props to be overridden by temporary props for top
|
||||
// points
|
||||
point.origProps = {
|
||||
plotY: point.plotY,
|
||||
plotX: point.plotX,
|
||||
isInside: point.isInside,
|
||||
negative: point.negative,
|
||||
zone: point.zone,
|
||||
y: point.y
|
||||
};
|
||||
point.lowerGraphic = point.graphic;
|
||||
point.graphic = point.upperGraphic;
|
||||
point.plotY = point.plotHigh;
|
||||
if (defined(point.plotHighX)) {
|
||||
point.plotX = point.plotHighX;
|
||||
}
|
||||
point.y = point.high;
|
||||
point.negative = point.high < (series.options.threshold || 0);
|
||||
point.zone = (series.zones.length && point.getZone());
|
||||
if (!series.chart.polar) {
|
||||
point.isInside = point.isTopInside = (typeof point.plotY !== 'undefined' &&
|
||||
point.plotY >= 0 &&
|
||||
point.plotY <= series.yAxis.len && // #3519
|
||||
point.plotX >= 0 &&
|
||||
point.plotX <= series.xAxis.len);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
// Draw top points
|
||||
seriesProto.drawPoints.apply(series, arguments);
|
||||
// Reset top points preliminary modifications
|
||||
i = 0;
|
||||
while (i < pointLength) {
|
||||
point = series.points[i];
|
||||
point.upperGraphic = point.graphic;
|
||||
point.graphic = point.lowerGraphic;
|
||||
extend(point, point.origProps);
|
||||
delete point.origProps;
|
||||
i++;
|
||||
}
|
||||
},
|
||||
/* eslint-enable valid-jsdoc */
|
||||
setStackedPoints: noop
|
||||
}, {
|
||||
/**
|
||||
* Range series only. The high or maximum value for each data point.
|
||||
* @name Highcharts.Point#high
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
/**
|
||||
* Range series only. The low or minimum value for each data point.
|
||||
* @name Highcharts.Point#low
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setState: function () {
|
||||
var prevState = this.state, series = this.series, isPolar = series.chart.polar;
|
||||
if (!defined(this.plotHigh)) {
|
||||
// Boost doesn't calculate plotHigh
|
||||
this.plotHigh = series.yAxis.toPixels(this.high, true);
|
||||
}
|
||||
if (!defined(this.plotLow)) {
|
||||
// Boost doesn't calculate plotLow
|
||||
this.plotLow = this.plotY = series.yAxis.toPixels(this.low, true);
|
||||
}
|
||||
if (series.stateMarkerGraphic) {
|
||||
series.lowerStateMarkerGraphic = series.stateMarkerGraphic;
|
||||
series.stateMarkerGraphic = series.upperStateMarkerGraphic;
|
||||
}
|
||||
// Change state also for the top marker
|
||||
this.graphic = this.upperGraphic;
|
||||
this.plotY = this.plotHigh;
|
||||
if (isPolar) {
|
||||
this.plotX = this.plotHighX;
|
||||
}
|
||||
// Top state:
|
||||
pointProto.setState.apply(this, arguments);
|
||||
this.state = prevState;
|
||||
// Now restore defaults
|
||||
this.plotY = this.plotLow;
|
||||
this.graphic = this.lowerGraphic;
|
||||
if (isPolar) {
|
||||
this.plotX = this.plotLowX;
|
||||
}
|
||||
if (series.stateMarkerGraphic) {
|
||||
series.upperStateMarkerGraphic = series.stateMarkerGraphic;
|
||||
series.stateMarkerGraphic = series.lowerStateMarkerGraphic;
|
||||
// Lower marker is stored at stateMarkerGraphic
|
||||
// to avoid reference duplication (#7021)
|
||||
series.lowerStateMarkerGraphic = void 0;
|
||||
}
|
||||
pointProto.setState.apply(this, arguments);
|
||||
},
|
||||
haloPath: function () {
|
||||
var isPolar = this.series.chart.polar, path = [];
|
||||
// Bottom halo
|
||||
this.plotY = this.plotLow;
|
||||
if (isPolar) {
|
||||
this.plotX = this.plotLowX;
|
||||
}
|
||||
if (this.isInside) {
|
||||
path = pointProto.haloPath.apply(this, arguments);
|
||||
}
|
||||
// Top halo
|
||||
this.plotY = this.plotHigh;
|
||||
if (isPolar) {
|
||||
this.plotX = this.plotHighX;
|
||||
}
|
||||
if (this.isTopInside) {
|
||||
path = path.concat(pointProto.haloPath.apply(this, arguments));
|
||||
}
|
||||
return path;
|
||||
},
|
||||
destroyElements: function () {
|
||||
var graphics = ['lowerGraphic', 'upperGraphic'];
|
||||
graphics.forEach(function (graphicName) {
|
||||
if (this[graphicName]) {
|
||||
this[graphicName] =
|
||||
this[graphicName].destroy();
|
||||
}
|
||||
}, this);
|
||||
// Clear graphic for states, removed in the above each:
|
||||
this.graphic = null;
|
||||
return pointProto.destroyElements.apply(this, arguments);
|
||||
},
|
||||
isValid: function () {
|
||||
return isNumber(this.low) && isNumber(this.high);
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* A `arearange` series. If the [type](#series.arearange.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
*
|
||||
* @extends series,plotOptions.arearange
|
||||
* @excluding dataParser, dataURL, stack, stacking
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @apioption series.arearange
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `arearange` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values
|
||||
* correspond to `x,low,high`. If the first value is a string, it is
|
||||
* applied as the name of the point, and the `x` value is inferred.
|
||||
* The `x` value can also be omitted, in which case the inner arrays
|
||||
* should be of length 2\. Then the `x` value is automatically calculated,
|
||||
* either starting at 0 and incremented by 1, or from `pointStart`
|
||||
* and `pointInterval` given in the series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 8, 3],
|
||||
* [1, 1, 1],
|
||||
* [2, 6, 8]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.arearange.turboThreshold),
|
||||
* this option is not available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* low: 9,
|
||||
* high: 0,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* low: 3,
|
||||
* high: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.line.data
|
||||
* @excluding marker, y
|
||||
* @product highcharts highstock
|
||||
* @apioption series.arearange.data
|
||||
*/
|
||||
/**
|
||||
* @extends series.arearange.dataLabels
|
||||
* @product highcharts highstock
|
||||
* @apioption series.arearange.data.dataLabels
|
||||
*/
|
||||
/**
|
||||
* The high or maximum value for each data point.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock
|
||||
* @apioption series.arearange.data.high
|
||||
*/
|
||||
/**
|
||||
* The low or minimum value for each data point.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock
|
||||
* @apioption series.arearange.data.low
|
||||
*/
|
||||
''; // adds doclets above to tranpiled file
|
||||
@@ -0,0 +1,485 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Color from '../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var objectEach = U.objectEach, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Series/Series.js';
|
||||
import '../Core/Options.js';
|
||||
var Series = H.Series;
|
||||
/**
|
||||
* Area series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.area
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('area', 'line',
|
||||
/**
|
||||
* The area series type.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/area-basic/
|
||||
* Area chart
|
||||
* @sample {highstock} stock/demo/area/
|
||||
* Area chart
|
||||
*
|
||||
* @extends plotOptions.line
|
||||
* @excluding useOhlcData
|
||||
* @product highcharts highstock
|
||||
* @optionparent plotOptions.area
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Fill color or gradient for the area. When `null`, the series' `color`
|
||||
* is used with the series' `fillOpacity`.
|
||||
*
|
||||
* In styled mode, the fill color can be set with the `.highcharts-area`
|
||||
* class name.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/area-fillcolor-default/
|
||||
* Null by default
|
||||
* @sample {highcharts} highcharts/plotoptions/area-fillcolor-gradient/
|
||||
* Gradient
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highcharts highstock
|
||||
* @apioption plotOptions.area.fillColor
|
||||
*/
|
||||
/**
|
||||
* Fill opacity for the area. When you set an explicit `fillColor`,
|
||||
* the `fillOpacity` is not applied. Instead, you should define the
|
||||
* opacity in the `fillColor` with an rgba color definition. The
|
||||
* `fillOpacity` setting, also the default setting, overrides the alpha
|
||||
* component of the `color` setting.
|
||||
*
|
||||
* In styled mode, the fill opacity can be set with the
|
||||
* `.highcharts-area` class name.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/area-fillopacity/
|
||||
* Automatic fill color and fill opacity of 0.1
|
||||
*
|
||||
* @type {number}
|
||||
* @default {highcharts} 0.75
|
||||
* @default {highstock} 0.75
|
||||
* @product highcharts highstock
|
||||
* @apioption plotOptions.area.fillOpacity
|
||||
*/
|
||||
/**
|
||||
* A separate color for the graph line. By default the line takes the
|
||||
* `color` of the series, but the lineColor setting allows setting a
|
||||
* separate color for the line without altering the `fillColor`.
|
||||
*
|
||||
* In styled mode, the line stroke can be set with the
|
||||
* `.highcharts-graph` class name.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/area-linecolor/
|
||||
* Dark gray line
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highcharts highstock
|
||||
* @apioption plotOptions.area.lineColor
|
||||
*/
|
||||
/**
|
||||
* A separate color for the negative part of the area.
|
||||
*
|
||||
* In styled mode, a negative color is set with the
|
||||
* `.highcharts-negative` class name.
|
||||
*
|
||||
* @see [negativeColor](#plotOptions.area.negativeColor)
|
||||
*
|
||||
* @sample {highcharts} highcharts/css/series-negative-color/
|
||||
* Negative color in styled mode
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.area.negativeFillColor
|
||||
*/
|
||||
/**
|
||||
* Whether the whole area or just the line should respond to mouseover
|
||||
* tooltips and other mouse or touch events.
|
||||
*
|
||||
* @sample {highcharts|highstock} highcharts/plotoptions/area-trackbyarea/
|
||||
* Display the tooltip when the area is hovered
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 1.1.6
|
||||
* @product highcharts highstock
|
||||
* @apioption plotOptions.area.trackByArea
|
||||
*/
|
||||
/**
|
||||
* The Y axis value to serve as the base for the area, for
|
||||
* distinguishing between values above and below a threshold. The area
|
||||
* between the graph and the threshold is filled.
|
||||
*
|
||||
* * If a number is given, the Y axis will scale to the threshold.
|
||||
* * If `null`, the scaling behaves like a line series with fill between
|
||||
* the graph and the Y axis minimum.
|
||||
* * If `Infinity` or `-Infinity`, the area between the graph and the
|
||||
* corresponding Y axis extreme is filled (since v6.1.0).
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/area-threshold/
|
||||
* A threshold of 100
|
||||
* @sample {highcharts} highcharts/plotoptions/area-threshold-infinity/
|
||||
* A threshold of Infinity
|
||||
*
|
||||
* @type {number|null}
|
||||
* @since 2.0
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
threshold: 0
|
||||
},
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @lends seriesTypes.area.prototype
|
||||
*/
|
||||
{
|
||||
singleStacks: false,
|
||||
/**
|
||||
* Return an array of stacked points, where null and missing points are
|
||||
* replaced by dummy points in order for gaps to be drawn correctly in
|
||||
* stacks.
|
||||
* @private
|
||||
*/
|
||||
getStackPoints: function (points) {
|
||||
var series = this, segment = [], keys = [], xAxis = this.xAxis, yAxis = this.yAxis, stack = yAxis.stacking.stacks[this.stackKey], pointMap = {}, seriesIndex = series.index, yAxisSeries = yAxis.series, seriesLength = yAxisSeries.length, visibleSeries, upOrDown = pick(yAxis.options.reversedStacks, true) ? 1 : -1, i;
|
||||
points = points || this.points;
|
||||
if (this.options.stacking) {
|
||||
for (i = 0; i < points.length; i++) {
|
||||
// Reset after point update (#7326)
|
||||
points[i].leftNull = points[i].rightNull = void 0;
|
||||
// Create a map where we can quickly look up the points by
|
||||
// their X values.
|
||||
pointMap[points[i].x] = points[i];
|
||||
}
|
||||
// Sort the keys (#1651)
|
||||
objectEach(stack, function (stackX, x) {
|
||||
// nulled after switching between
|
||||
// grouping and not (#1651, #2336)
|
||||
if (stackX.total !== null) {
|
||||
keys.push(x);
|
||||
}
|
||||
});
|
||||
keys.sort(function (a, b) {
|
||||
return a - b;
|
||||
});
|
||||
visibleSeries = yAxisSeries.map(function (s) {
|
||||
return s.visible;
|
||||
});
|
||||
keys.forEach(function (x, idx) {
|
||||
var y = 0, stackPoint, stackedValues;
|
||||
if (pointMap[x] && !pointMap[x].isNull) {
|
||||
segment.push(pointMap[x]);
|
||||
// Find left and right cliff. -1 goes left, 1 goes
|
||||
// right.
|
||||
[-1, 1].forEach(function (direction) {
|
||||
var nullName = direction === 1 ?
|
||||
'rightNull' :
|
||||
'leftNull', cliffName = direction === 1 ?
|
||||
'rightCliff' :
|
||||
'leftCliff', cliff = 0, otherStack = stack[keys[idx + direction]];
|
||||
// If there is a stack next to this one,
|
||||
// to the left or to the right...
|
||||
if (otherStack) {
|
||||
i = seriesIndex;
|
||||
// Can go either up or down,
|
||||
// depending on reversedStacks
|
||||
while (i >= 0 && i < seriesLength) {
|
||||
stackPoint = otherStack.points[i];
|
||||
if (!stackPoint) {
|
||||
// If the next point in this series
|
||||
// is missing, mark the point
|
||||
// with point.leftNull or
|
||||
// point.rightNull = true.
|
||||
if (i === seriesIndex) {
|
||||
pointMap[x][nullName] =
|
||||
true;
|
||||
// If there are missing points in
|
||||
// the next stack in any of the
|
||||
// series below this one, we need
|
||||
// to substract the missing values
|
||||
// and add a hiatus to the left or
|
||||
// right.
|
||||
}
|
||||
else if (visibleSeries[i]) {
|
||||
stackedValues =
|
||||
stack[x].points[i];
|
||||
if (stackedValues) {
|
||||
cliff -=
|
||||
stackedValues[1] -
|
||||
stackedValues[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
// When reversedStacks is true, loop up,
|
||||
// else loop down
|
||||
i += upOrDown;
|
||||
}
|
||||
}
|
||||
pointMap[x][cliffName] = cliff;
|
||||
});
|
||||
// There is no point for this X value in this series, so we
|
||||
// insert a dummy point in order for the areas to be drawn
|
||||
// correctly.
|
||||
}
|
||||
else {
|
||||
// Loop down the stack to find the series below this
|
||||
// one that has a value (#1991)
|
||||
i = seriesIndex;
|
||||
while (i >= 0 && i < seriesLength) {
|
||||
stackPoint = stack[x].points[i];
|
||||
if (stackPoint) {
|
||||
y = stackPoint[1];
|
||||
break;
|
||||
}
|
||||
// When reversedStacks is true, loop up, else loop
|
||||
// down
|
||||
i += upOrDown;
|
||||
}
|
||||
y = yAxis.translate(// #6272
|
||||
y, 0, 1, 0, 1);
|
||||
segment.push({
|
||||
isNull: true,
|
||||
plotX: xAxis.translate(// #6272
|
||||
x, 0, 0, 0, 1),
|
||||
x: x,
|
||||
plotY: y,
|
||||
yBottom: y
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return segment;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getGraphPath: function (points) {
|
||||
var getGraphPath = Series.prototype.getGraphPath, graphPath, options = this.options, stacking = options.stacking, yAxis = this.yAxis, topPath, bottomPath, bottomPoints = [], graphPoints = [], seriesIndex = this.index, i, areaPath, plotX, stacks = yAxis.stacking.stacks[this.stackKey], threshold = options.threshold, translatedThreshold = Math.round(// #10909
|
||||
yAxis.getThreshold(options.threshold)), isNull, yBottom, connectNulls = pick(// #10574
|
||||
options.connectNulls, stacking === 'percent'),
|
||||
// To display null points in underlying stacked series, this
|
||||
// series graph must be broken, and the area also fall down to
|
||||
// fill the gap left by the null point. #2069
|
||||
addDummyPoints = function (i, otherI, side) {
|
||||
var point = points[i], stackedValues = stacking &&
|
||||
stacks[point.x].points[seriesIndex], nullVal = point[side + 'Null'] || 0, cliffVal = point[side + 'Cliff'] || 0, top, bottom, isNull = true;
|
||||
if (cliffVal || nullVal) {
|
||||
top = (nullVal ?
|
||||
stackedValues[0] :
|
||||
stackedValues[1]) + cliffVal;
|
||||
bottom = stackedValues[0] + cliffVal;
|
||||
isNull = !!nullVal;
|
||||
}
|
||||
else if (!stacking &&
|
||||
points[otherI] &&
|
||||
points[otherI].isNull) {
|
||||
top = bottom = threshold;
|
||||
}
|
||||
// Add to the top and bottom line of the area
|
||||
if (typeof top !== 'undefined') {
|
||||
graphPoints.push({
|
||||
plotX: plotX,
|
||||
plotY: top === null ?
|
||||
translatedThreshold :
|
||||
yAxis.getThreshold(top),
|
||||
isNull: isNull,
|
||||
isCliff: true
|
||||
});
|
||||
bottomPoints.push({
|
||||
plotX: plotX,
|
||||
plotY: bottom === null ?
|
||||
translatedThreshold :
|
||||
yAxis.getThreshold(bottom),
|
||||
doCurve: false // #1041, gaps in areaspline areas
|
||||
});
|
||||
}
|
||||
};
|
||||
// Find what points to use
|
||||
points = points || this.points;
|
||||
// Fill in missing points
|
||||
if (stacking) {
|
||||
points = this.getStackPoints(points);
|
||||
}
|
||||
for (i = 0; i < points.length; i++) {
|
||||
// Reset after series.update of stacking property (#12033)
|
||||
if (!stacking) {
|
||||
points[i].leftCliff = points[i].rightCliff =
|
||||
points[i].leftNull = points[i].rightNull = void 0;
|
||||
}
|
||||
isNull = points[i].isNull;
|
||||
plotX = pick(points[i].rectPlotX, points[i].plotX);
|
||||
yBottom = stacking ? points[i].yBottom : translatedThreshold;
|
||||
if (!isNull || connectNulls) {
|
||||
if (!connectNulls) {
|
||||
addDummyPoints(i, i - 1, 'left');
|
||||
}
|
||||
// Skip null point when stacking is false and connectNulls
|
||||
// true
|
||||
if (!(isNull && !stacking && connectNulls)) {
|
||||
graphPoints.push(points[i]);
|
||||
bottomPoints.push({
|
||||
x: i,
|
||||
plotX: plotX,
|
||||
plotY: yBottom
|
||||
});
|
||||
}
|
||||
if (!connectNulls) {
|
||||
addDummyPoints(i, i + 1, 'right');
|
||||
}
|
||||
}
|
||||
}
|
||||
topPath = getGraphPath.call(this, graphPoints, true, true);
|
||||
bottomPoints.reversed = true;
|
||||
bottomPath = getGraphPath.call(this, bottomPoints, true, true);
|
||||
var firstBottomPoint = bottomPath[0];
|
||||
if (firstBottomPoint && firstBottomPoint[0] === 'M') {
|
||||
bottomPath[0] = ['L', firstBottomPoint[1], firstBottomPoint[2]];
|
||||
}
|
||||
areaPath = topPath.concat(bottomPath);
|
||||
// TODO: don't set leftCliff and rightCliff when connectNulls?
|
||||
graphPath = getGraphPath
|
||||
.call(this, graphPoints, false, connectNulls);
|
||||
areaPath.xMap = topPath.xMap;
|
||||
this.areaPath = areaPath;
|
||||
return graphPath;
|
||||
},
|
||||
/**
|
||||
* Draw the graph and the underlying area. This method calls the Series
|
||||
* base function and adds the area. The areaPath is calculated in the
|
||||
* getSegmentPath method called from Series.prototype.drawGraph.
|
||||
* @private
|
||||
*/
|
||||
drawGraph: function () {
|
||||
// Define or reset areaPath
|
||||
this.areaPath = [];
|
||||
// Call the base method
|
||||
Series.prototype.drawGraph.apply(this);
|
||||
// Define local variables
|
||||
var series = this, areaPath = this.areaPath, options = this.options, zones = this.zones, props = [[
|
||||
'area',
|
||||
'highcharts-area',
|
||||
this.color,
|
||||
options.fillColor
|
||||
]]; // area name, main color, fill color
|
||||
zones.forEach(function (zone, i) {
|
||||
props.push([
|
||||
'zone-area-' + i,
|
||||
'highcharts-area highcharts-zone-area-' + i + ' ' +
|
||||
zone.className,
|
||||
zone.color || series.color,
|
||||
zone.fillColor || options.fillColor
|
||||
]);
|
||||
});
|
||||
props.forEach(function (prop) {
|
||||
var areaKey = prop[0], area = series[areaKey], verb = area ? 'animate' : 'attr', attribs = {};
|
||||
// Create or update the area
|
||||
if (area) { // update
|
||||
area.endX = series.preventGraphAnimation ?
|
||||
null :
|
||||
areaPath.xMap;
|
||||
area.animate({ d: areaPath });
|
||||
}
|
||||
else { // create
|
||||
attribs.zIndex = 0; // #1069
|
||||
area = series[areaKey] = series.chart.renderer
|
||||
.path(areaPath)
|
||||
.addClass(prop[1])
|
||||
.add(series.group);
|
||||
area.isArea = true;
|
||||
}
|
||||
if (!series.chart.styledMode) {
|
||||
attribs.fill = pick(prop[3], color(prop[2])
|
||||
.setOpacity(pick(options.fillOpacity, 0.75))
|
||||
.get());
|
||||
}
|
||||
area[verb](attribs);
|
||||
area.startX = areaPath.xMap;
|
||||
area.shiftUnit = options.step ? 2 : 1;
|
||||
});
|
||||
},
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle
|
||||
});
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* A `area` series. If the [type](#series.area.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.area
|
||||
* @excluding dataParser, dataURL, useOhlcData
|
||||
* @product highcharts highstock
|
||||
* @apioption series.area
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `area` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` * and `pointInterval` given in the series options. If the
|
||||
* axis has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 9],
|
||||
* [1, 7],
|
||||
* [2, 6]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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: 1,
|
||||
* y: 9,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 6,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.area.data
|
||||
*/
|
||||
''; // adds doclets above to transpilat
|
||||
@@ -0,0 +1,98 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
var seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* The area spline range is a cartesian series type with higher and
|
||||
* lower Y values along an X axis. The area inside the range is colored, and
|
||||
* the graph outlining the area is a smoothed spline.
|
||||
*
|
||||
* @sample {highstock|highstock} stock/demo/areasplinerange/
|
||||
* Area spline range
|
||||
*
|
||||
* @extends plotOptions.arearange
|
||||
* @since 2.3.0
|
||||
* @excluding step, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @apioption plotOptions.areasplinerange
|
||||
*/
|
||||
seriesType('areasplinerange', 'arearange', null, {
|
||||
getPointSpline: seriesTypes.spline.prototype.getPointSpline
|
||||
});
|
||||
/**
|
||||
* A `areasplinerange` series. If the [type](#series.areasplinerange.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.areasplinerange
|
||||
* @excluding dataParser, dataURL, stack, step, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @apioption series.areasplinerange
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `areasplinerange`
|
||||
* series type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,low,high`. If the first value is a string, it is applied as the name
|
||||
* of the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2\. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 0, 5],
|
||||
* [1, 9, 1],
|
||||
* [2, 5, 2]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.areasplinerange.turboThreshold), this option is
|
||||
* not available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* low: 5,
|
||||
* high: 0,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* low: 4,
|
||||
* high: 1,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.arearange.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.areasplinerange.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,119 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
|
||||
import O from '../Core/Options.js';
|
||||
var defaultOptions = O.defaultOptions;
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import './AreaSeries.js';
|
||||
import './SplineSeries.js';
|
||||
var areaProto = H.seriesTypes.area.prototype;
|
||||
/**
|
||||
* AreaSpline series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.areaspline
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('areaspline', 'spline',
|
||||
/**
|
||||
* The area spline series is an area series where the graph between the
|
||||
* points is smoothed into a spline.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/areaspline/
|
||||
* Area spline chart
|
||||
* @sample {highstock} stock/demo/areaspline/
|
||||
* Area spline chart
|
||||
*
|
||||
* @extends plotOptions.area
|
||||
* @excluding step, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @apioption plotOptions.areaspline
|
||||
*/
|
||||
defaultOptions.plotOptions.area, {
|
||||
getStackPoints: areaProto.getStackPoints,
|
||||
getGraphPath: areaProto.getGraphPath,
|
||||
drawGraph: areaProto.drawGraph,
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle
|
||||
});
|
||||
/**
|
||||
* A `areaspline` series. If the [type](#series.areaspline.type) option
|
||||
* is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
*
|
||||
* @extends series,plotOptions.areaspline
|
||||
* @excluding dataParser, dataURL, step, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @apioption series.areaspline
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `areaspline` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 10],
|
||||
* [1, 9],
|
||||
* [2, 3]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.areaspline.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 4,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.areaspline.data
|
||||
*/
|
||||
''; // adds doclets above into transpilat
|
||||
@@ -0,0 +1,120 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import './ColumnSeries.js';
|
||||
/**
|
||||
* Bar series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.bar
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('bar', 'column',
|
||||
/**
|
||||
* A bar series is a special type of column series where the columns are
|
||||
* horizontal.
|
||||
*
|
||||
* @sample highcharts/demo/bar-basic/
|
||||
* Bar chart
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.bar
|
||||
*/
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
null, {
|
||||
inverted: true
|
||||
});
|
||||
/**
|
||||
* A `bar` series. If the [type](#series.bar.type) option is not specified,
|
||||
* it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.bar
|
||||
* @excluding connectNulls, dashStyle, dataParser, dataURL, gapSize, gapUnit,
|
||||
* linecap, lineWidth, marker, connectEnds, step
|
||||
* @product highcharts
|
||||
* @apioption series.bar
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `bar` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 5],
|
||||
* [1, 10],
|
||||
* [2, 3]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.bar.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 1,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 10,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.column.data
|
||||
* @product highcharts
|
||||
* @apioption series.bar.data
|
||||
*/
|
||||
/**
|
||||
* @excluding halo,lineWidth,lineWidthPlus,marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.bar.states.hover
|
||||
*/
|
||||
/**
|
||||
* @excluding halo,lineWidth,lineWidthPlus,marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.bar.states.select
|
||||
*/
|
||||
''; // gets doclets above into transpilat
|
||||
@@ -0,0 +1,144 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Highsoft AS
|
||||
*
|
||||
* Author: Sebastian Domas
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, isNumber = U.isNumber, merge = U.merge, seriesType = U.seriesType;
|
||||
import derivedSeriesMixin from '../Mixins/DerivedSeries.js';
|
||||
/* ************************************************************************** *
|
||||
* BELL CURVE *
|
||||
* ************************************************************************** */
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function mean(data) {
|
||||
var length = data.length, sum = data.reduce(function (sum, value) {
|
||||
return (sum += value);
|
||||
}, 0);
|
||||
return length > 0 && sum / length;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function standardDeviation(data, average) {
|
||||
var len = data.length, sum;
|
||||
average = isNumber(average) ? average : mean(data);
|
||||
sum = data.reduce(function (sum, value) {
|
||||
var diff = value - average;
|
||||
return (sum += diff * diff);
|
||||
}, 0);
|
||||
return len > 1 && Math.sqrt(sum / (len - 1));
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function normalDensity(x, mean, standardDeviation) {
|
||||
var translation = x - mean;
|
||||
return Math.exp(-(translation * translation) /
|
||||
(2 * standardDeviation * standardDeviation)) / (standardDeviation * Math.sqrt(2 * Math.PI));
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
/**
|
||||
* Bell curve class
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.bellcurve
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('bellcurve', 'areaspline'
|
||||
/**
|
||||
* A bell curve is an areaspline series which represents the probability
|
||||
* density function of the normal distribution. It calculates mean and
|
||||
* standard deviation of the base series data and plots the curve according
|
||||
* to the calculated parameters.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/bellcurve/
|
||||
* Bell curve
|
||||
*
|
||||
* @extends plotOptions.areaspline
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @excluding boostThreshold, connectNulls, dragDrop, stacking,
|
||||
* pointInterval, pointIntervalUnit
|
||||
* @requires modules/bellcurve
|
||||
* @optionparent plotOptions.bellcurve
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* This option allows to define the length of the bell curve. A unit of
|
||||
* the length of the bell curve is standard deviation.
|
||||
*
|
||||
* @sample highcharts/plotoptions/bellcurve-intervals-pointsininterval
|
||||
* Intervals and points in interval
|
||||
*/
|
||||
intervals: 3,
|
||||
/**
|
||||
* Defines how many points should be plotted within 1 interval. See
|
||||
* `plotOptions.bellcurve.intervals`.
|
||||
*
|
||||
* @sample highcharts/plotoptions/bellcurve-intervals-pointsininterval
|
||||
* Intervals and points in interval
|
||||
*/
|
||||
pointsInInterval: 3,
|
||||
marker: {
|
||||
enabled: false
|
||||
}
|
||||
}, merge(derivedSeriesMixin, {
|
||||
setMean: function () {
|
||||
this.mean = correctFloat(mean(this.baseSeries.yData));
|
||||
},
|
||||
setStandardDeviation: function () {
|
||||
this.standardDeviation = correctFloat(standardDeviation(this.baseSeries.yData, this.mean));
|
||||
},
|
||||
setDerivedData: function () {
|
||||
if (this.baseSeries.yData.length > 1) {
|
||||
this.setMean();
|
||||
this.setStandardDeviation();
|
||||
this.setData(this.derivedData(this.mean, this.standardDeviation), false);
|
||||
}
|
||||
return (void 0);
|
||||
},
|
||||
derivedData: function (mean, standardDeviation) {
|
||||
var intervals = this.options.intervals, pointsInInterval = this.options.pointsInInterval, x = mean - intervals * standardDeviation, stop = intervals * pointsInInterval * 2 + 1, increment = standardDeviation / pointsInInterval, data = [], i;
|
||||
for (i = 0; i < stop; i++) {
|
||||
data.push([x, normalDensity(x, mean, standardDeviation)]);
|
||||
x += increment;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* A `bellcurve` series. If the [type](#series.bellcurve.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* For options that apply to multiple series, it is recommended to add
|
||||
* them to the [plotOptions.series](#plotOptions.series) options structure.
|
||||
* To apply to all series of this specific type, apply it to
|
||||
* [plotOptions.bellcurve](#plotOptions.bellcurve).
|
||||
*
|
||||
* @extends series,plotOptions.bellcurve
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @excluding dataParser, dataURL, data, boostThreshold, boostBlending
|
||||
* @requires modules/bellcurve
|
||||
* @apioption series.bellcurve
|
||||
*/
|
||||
/**
|
||||
* An integer identifying the index to use for the base series, or a string
|
||||
* representing the id of the series.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @apioption series.bellcurve.baseSeries
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,620 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
var noop = H.noop, seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* The boxplot series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes#boxplot
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
/**
|
||||
* A box plot is a convenient way of depicting groups of data through their
|
||||
* five-number summaries: the smallest observation (sample minimum), lower
|
||||
* quartile (Q1), median (Q2), upper quartile (Q3), and largest observation
|
||||
* (sample maximum).
|
||||
*
|
||||
* @sample highcharts/demo/box-plot/
|
||||
* Box plot
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @excluding borderColor, borderRadius, borderWidth, groupZPadding, states,
|
||||
* boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.boxplot
|
||||
*/
|
||||
seriesType('boxplot', 'column', {
|
||||
threshold: null,
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> <b> ' +
|
||||
'{series.name}</b><br/>' +
|
||||
'Maximum: {point.high}<br/>' +
|
||||
'Upper quartile: {point.q3}<br/>' +
|
||||
'Median: {point.median}<br/>' +
|
||||
'Lower quartile: {point.q1}<br/>' +
|
||||
'Minimum: {point.low}<br/>'
|
||||
},
|
||||
/**
|
||||
* The length of the whiskers, the horizontal lines marking low and
|
||||
* high values. It can be a numerical pixel value, or a percentage
|
||||
* value of the box width. Set `0` to disable whiskers.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* True by default
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
whiskerLength: '50%',
|
||||
/**
|
||||
* The fill color of the box.
|
||||
*
|
||||
* In styled mode, the fill color can be set with the
|
||||
* `.highcharts-boxplot-box` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #ffffff
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
fillColor: '#ffffff',
|
||||
/**
|
||||
* The width of the line surrounding the box. If any of
|
||||
* [stemWidth](#plotOptions.boxplot.stemWidth),
|
||||
* [medianWidth](#plotOptions.boxplot.medianWidth)
|
||||
* or [whiskerWidth](#plotOptions.boxplot.whiskerWidth) are `null`,
|
||||
* the lineWidth also applies to these lines.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/plotoptions/error-bar-styling/
|
||||
* Error bar styling
|
||||
*
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* The color of the median line. If `undefined`, the general series
|
||||
* color applies.
|
||||
*
|
||||
* In styled mode, the median stroke width can be set with the
|
||||
* `.highcharts-boxplot-median` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
* @sample {highcharts} highcharts/plotoptions/error-bar-styling/
|
||||
* Error bar styling
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.medianColor
|
||||
*/
|
||||
/**
|
||||
* The pixel width of the median line. If `null`, the
|
||||
* [lineWidth](#plotOptions.boxplot.lineWidth) is used.
|
||||
*
|
||||
* In styled mode, the median stroke width can be set with the
|
||||
* `.highcharts-boxplot-median` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {number|null}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
medianWidth: 2,
|
||||
/*
|
||||
// States are not working and are removed from docs.
|
||||
// Refer to: #2340
|
||||
states: {
|
||||
hover: {
|
||||
brightness: -0.3
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The color of the stem, the vertical line extending from the box to
|
||||
* the whiskers. If `undefined`, the series color is used.
|
||||
*
|
||||
* In styled mode, the stem stroke can be set with the
|
||||
* `.highcharts-boxplot-stem` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
* @sample {highcharts} highcharts/plotoptions/error-bar-styling/
|
||||
* Error bar styling
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.stemColor
|
||||
*/
|
||||
/**
|
||||
* The dash style of the box.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 8.1.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.boxDashStyle
|
||||
*/
|
||||
/**
|
||||
* The dash style of the median.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 8.1.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.medianDashStyle
|
||||
*/
|
||||
/**
|
||||
* The dash style of the stem, the vertical line extending from the
|
||||
* box to the whiskers.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
* @sample {highcharts} highcharts/plotoptions/error-bar-styling/
|
||||
* Error bar styling
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.stemDashStyle
|
||||
*/
|
||||
/**
|
||||
* The dash style of the whiskers.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 8.1.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.whiskerDashStyle
|
||||
*/
|
||||
/**
|
||||
* The width of the stem, the vertical line extending from the box to
|
||||
* the whiskers. If `undefined`, the width is inherited from the
|
||||
* [lineWidth](#plotOptions.boxplot.lineWidth) option.
|
||||
*
|
||||
* In styled mode, the stem stroke width can be set with the
|
||||
* `.highcharts-boxplot-stem` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
* @sample {highcharts} highcharts/plotoptions/error-bar-styling/
|
||||
* Error bar styling
|
||||
*
|
||||
* @type {number}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.stemWidth
|
||||
*/
|
||||
/**
|
||||
* @default high
|
||||
* @apioption plotOptions.boxplot.colorKey
|
||||
*/
|
||||
/**
|
||||
* The color of the whiskers, the horizontal lines marking low and high
|
||||
* values. When `undefined`, the general series color is used.
|
||||
*
|
||||
* In styled mode, the whisker stroke can be set with the
|
||||
* `.highcharts-boxplot-whisker` class .
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.boxplot.whiskerColor
|
||||
*/
|
||||
/**
|
||||
* The line width of the whiskers, the horizontal lines marking low and
|
||||
* high values. When `undefined`, the general
|
||||
* [lineWidth](#plotOptions.boxplot.lineWidth) applies.
|
||||
*
|
||||
* In styled mode, the whisker stroke width can be set with the
|
||||
* `.highcharts-boxplot-whisker` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
whiskerWidth: 2
|
||||
},
|
||||
/** @lends Highcharts.seriesTypes.boxplot */
|
||||
{
|
||||
// array point configs are mapped to this
|
||||
pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'],
|
||||
// return a plain array for speedy calculation
|
||||
toYData: function (point) {
|
||||
return [point.low, point.q1, point.median, point.q3, point.high];
|
||||
},
|
||||
// defines the top of the tracker
|
||||
pointValKey: 'high',
|
||||
// Get presentational attributes
|
||||
pointAttribs: function () {
|
||||
// No attributes should be set on point.graphic which is the group
|
||||
return {};
|
||||
},
|
||||
// Disable data labels for box plot
|
||||
drawDataLabels: noop,
|
||||
// Translate data points from raw values x and y to plotX and plotY
|
||||
translate: function () {
|
||||
var series = this, yAxis = series.yAxis, pointArrayMap = series.pointArrayMap;
|
||||
seriesTypes.column.prototype.translate.apply(series);
|
||||
// do the translation on each point dimension
|
||||
series.points.forEach(function (point) {
|
||||
pointArrayMap.forEach(function (key) {
|
||||
if (point[key] !== null) {
|
||||
point[key + 'Plot'] = yAxis.translate(point[key], 0, 1, 0, 1);
|
||||
}
|
||||
});
|
||||
point.plotHigh = point.highPlot; // For data label validation
|
||||
});
|
||||
},
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Draw the data points
|
||||
* @private
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, points = series.points, options = series.options, chart = series.chart, renderer = chart.renderer, q1Plot, q3Plot, highPlot, lowPlot, medianPlot, medianPath, crispCorr, crispX = 0, boxPath, width, left, right, halfWidth,
|
||||
// error bar inherits this series type but doesn't do quartiles
|
||||
doQuartiles = series.doQuartiles !== false, pointWiskerLength, whiskerLength = series.options.whiskerLength;
|
||||
points.forEach(function (point) {
|
||||
var graphic = point.graphic, verb = graphic ? 'animate' : 'attr', shapeArgs = point.shapeArgs, boxAttr = {}, stemAttr = {}, whiskersAttr = {}, medianAttr = {}, color = point.color || series.color;
|
||||
if (typeof point.plotY !== 'undefined') {
|
||||
// crisp vector coordinates
|
||||
width = Math.round(shapeArgs.width);
|
||||
left = Math.floor(shapeArgs.x);
|
||||
right = left + width;
|
||||
halfWidth = Math.round(width / 2);
|
||||
q1Plot = Math.floor(doQuartiles ? point.q1Plot : point.lowPlot);
|
||||
q3Plot = Math.floor(doQuartiles ? point.q3Plot : point.lowPlot);
|
||||
highPlot = Math.floor(point.highPlot);
|
||||
lowPlot = Math.floor(point.lowPlot);
|
||||
if (!graphic) {
|
||||
point.graphic = graphic = renderer.g('point')
|
||||
.add(series.group);
|
||||
point.stem = renderer.path()
|
||||
.addClass('highcharts-boxplot-stem')
|
||||
.add(graphic);
|
||||
if (whiskerLength) {
|
||||
point.whiskers = renderer.path()
|
||||
.addClass('highcharts-boxplot-whisker')
|
||||
.add(graphic);
|
||||
}
|
||||
if (doQuartiles) {
|
||||
point.box = renderer.path(boxPath)
|
||||
.addClass('highcharts-boxplot-box')
|
||||
.add(graphic);
|
||||
}
|
||||
point.medianShape = renderer.path(medianPath)
|
||||
.addClass('highcharts-boxplot-median')
|
||||
.add(graphic);
|
||||
}
|
||||
if (!chart.styledMode) {
|
||||
// Stem attributes
|
||||
stemAttr.stroke =
|
||||
point.stemColor || options.stemColor || color;
|
||||
stemAttr['stroke-width'] = pick(point.stemWidth, options.stemWidth, options.lineWidth);
|
||||
stemAttr.dashstyle = (point.stemDashStyle ||
|
||||
options.stemDashStyle ||
|
||||
options.dashStyle);
|
||||
point.stem.attr(stemAttr);
|
||||
// Whiskers attributes
|
||||
if (whiskerLength) {
|
||||
whiskersAttr.stroke = (point.whiskerColor ||
|
||||
options.whiskerColor ||
|
||||
color);
|
||||
whiskersAttr['stroke-width'] = pick(point.whiskerWidth, options.whiskerWidth, options.lineWidth);
|
||||
whiskersAttr.dashstyle = (point.whiskerDashStyle ||
|
||||
options.whiskerDashStyle ||
|
||||
options.dashStyle);
|
||||
point.whiskers.attr(whiskersAttr);
|
||||
}
|
||||
if (doQuartiles) {
|
||||
boxAttr.fill = (point.fillColor ||
|
||||
options.fillColor ||
|
||||
color);
|
||||
boxAttr.stroke = options.lineColor || color;
|
||||
boxAttr['stroke-width'] = options.lineWidth || 0;
|
||||
boxAttr.dashstyle = (point.boxDashStyle ||
|
||||
options.boxDashStyle ||
|
||||
options.dashStyle);
|
||||
point.box.attr(boxAttr);
|
||||
}
|
||||
// Median attributes
|
||||
medianAttr.stroke = (point.medianColor ||
|
||||
options.medianColor ||
|
||||
color);
|
||||
medianAttr['stroke-width'] = pick(point.medianWidth, options.medianWidth, options.lineWidth);
|
||||
medianAttr.dashstyle = (point.medianDashStyle ||
|
||||
options.medianDashStyle ||
|
||||
options.dashStyle);
|
||||
point.medianShape.attr(medianAttr);
|
||||
}
|
||||
var d = void 0;
|
||||
// The stem
|
||||
crispCorr = (point.stem.strokeWidth() % 2) / 2;
|
||||
crispX = left + halfWidth + crispCorr;
|
||||
d = [
|
||||
// stem up
|
||||
['M', crispX, q3Plot],
|
||||
['L', crispX, highPlot],
|
||||
// stem down
|
||||
['M', crispX, q1Plot],
|
||||
['L', crispX, lowPlot]
|
||||
];
|
||||
point.stem[verb]({ d: d });
|
||||
// The box
|
||||
if (doQuartiles) {
|
||||
crispCorr = (point.box.strokeWidth() % 2) / 2;
|
||||
q1Plot = Math.floor(q1Plot) + crispCorr;
|
||||
q3Plot = Math.floor(q3Plot) + crispCorr;
|
||||
left += crispCorr;
|
||||
right += crispCorr;
|
||||
d = [
|
||||
['M', left, q3Plot],
|
||||
['L', left, q1Plot],
|
||||
['L', right, q1Plot],
|
||||
['L', right, q3Plot],
|
||||
['L', left, q3Plot],
|
||||
['Z']
|
||||
];
|
||||
point.box[verb]({ d: d });
|
||||
}
|
||||
// The whiskers
|
||||
if (whiskerLength) {
|
||||
crispCorr = (point.whiskers.strokeWidth() % 2) / 2;
|
||||
highPlot = highPlot + crispCorr;
|
||||
lowPlot = lowPlot + crispCorr;
|
||||
pointWiskerLength = (/%$/).test(whiskerLength) ?
|
||||
halfWidth * parseFloat(whiskerLength) / 100 :
|
||||
whiskerLength / 2;
|
||||
d = [
|
||||
// High whisker
|
||||
['M', crispX - pointWiskerLength, highPlot],
|
||||
['L', crispX + pointWiskerLength, highPlot],
|
||||
// Low whisker
|
||||
['M', crispX - pointWiskerLength, lowPlot],
|
||||
['L', crispX + pointWiskerLength, lowPlot]
|
||||
];
|
||||
point.whiskers[verb]({ d: d });
|
||||
}
|
||||
// The median
|
||||
medianPlot = Math.round(point.medianPlot);
|
||||
crispCorr = (point.medianShape.strokeWidth() % 2) / 2;
|
||||
medianPlot = medianPlot + crispCorr;
|
||||
d = [
|
||||
['M', left, medianPlot],
|
||||
['L', right, medianPlot]
|
||||
];
|
||||
point.medianShape[verb]({ d: d });
|
||||
}
|
||||
});
|
||||
},
|
||||
setStackedPoints: noop // #3890
|
||||
});
|
||||
/**
|
||||
* A `boxplot` series. If the [type](#series.boxplot.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.boxplot
|
||||
* @excluding dataParser, dataURL, marker, stack, stacking, states,
|
||||
* boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-more
|
||||
* @apioption series.boxplot
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `boxplot` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 6 or 5 values. In this case, the values correspond
|
||||
* to `x,low,q1,median,q3,high`. If the first value is a string, it is
|
||||
* applied as the name of the point, and the `x` value is inferred. The `x`
|
||||
* value can also be omitted, in which case the inner arrays should be of
|
||||
* length 5. Then the `x` value is automatically calculated, either starting
|
||||
* at 0 and incremented by 1, or from `pointStart` and `pointInterval` given
|
||||
* in the series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 3, 0, 10, 3, 5],
|
||||
* [1, 7, 8, 7, 2, 9],
|
||||
* [2, 6, 9, 5, 1, 3]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.boxplot.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* low: 4,
|
||||
* q1: 9,
|
||||
* median: 9,
|
||||
* q3: 1,
|
||||
* high: 10,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* low: 5,
|
||||
* q1: 7,
|
||||
* median: 3,
|
||||
* q3: 6,
|
||||
* high: 2,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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,number,number>|Array<(number|string),number,number,number,number,number>|*>}
|
||||
* @extends series.line.data
|
||||
* @excluding marker
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data
|
||||
*/
|
||||
/**
|
||||
* The `high` value for each data point, signifying the highest value
|
||||
* in the sample set. The top whisker is drawn here.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.high
|
||||
*/
|
||||
/**
|
||||
* The `low` value for each data point, signifying the lowest value
|
||||
* in the sample set. The bottom whisker is drawn here.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.low
|
||||
*/
|
||||
/**
|
||||
* The median for each data point. This is drawn as a line through the
|
||||
* middle area of the box.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.median
|
||||
*/
|
||||
/**
|
||||
* The lower quartile for each data point. This is the bottom of the
|
||||
* box.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.q1
|
||||
*/
|
||||
/**
|
||||
* The higher quartile for each data point. This is the top of the box.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.q3
|
||||
*/
|
||||
/**
|
||||
* The dash style of the box.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 8.1.0
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.boxDashStyle
|
||||
*/
|
||||
/**
|
||||
* The dash style of the median.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 8.1.0
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.medianDashStyle
|
||||
*/
|
||||
/**
|
||||
* The dash style of the stem.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 8.1.0
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.stemDashStyle
|
||||
*/
|
||||
/**
|
||||
* The dash style of the whiskers.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
|
||||
* Box plot styling
|
||||
* @sample {highcharts} highcharts/css/boxplot/
|
||||
* Box plot in styled mode
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @default Solid
|
||||
* @since 8.1.0
|
||||
* @product highcharts
|
||||
* @apioption series.boxplot.data.whiskerDashStyle
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,906 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Highsoft AS
|
||||
*
|
||||
* Author: Paweł Potaczek
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Chart from '../../Core/Chart/Chart.js';
|
||||
import Color from '../../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import H from '../../Core/Globals.js';
|
||||
import Legend from '../../Core/Legend.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick, setOptions = U.setOptions, stableSort = U.stableSort, wrap = U.wrap;
|
||||
/**
|
||||
* @interface Highcharts.BubbleLegendFormatterContextObject
|
||||
*/ /**
|
||||
* The center y position of the range.
|
||||
* @name Highcharts.BubbleLegendFormatterContextObject#center
|
||||
* @type {number}
|
||||
*/ /**
|
||||
* The radius of the bubble range.
|
||||
* @name Highcharts.BubbleLegendFormatterContextObject#radius
|
||||
* @type {number}
|
||||
*/ /**
|
||||
* The bubble value.
|
||||
* @name Highcharts.BubbleLegendFormatterContextObject#value
|
||||
* @type {number}
|
||||
*/
|
||||
''; // detach doclets above
|
||||
import './BubbleSeries.js';
|
||||
var Series = H.Series, noop = H.noop;
|
||||
setOptions({
|
||||
legend: {
|
||||
/**
|
||||
* The bubble legend is an additional element in legend which
|
||||
* presents the scale of the bubble series. Individual bubble ranges
|
||||
* can be defined by user or calculated from series. In the case of
|
||||
* automatically calculated ranges, a 1px margin of error is
|
||||
* permitted.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @product highcharts highstock highmaps
|
||||
* @requires highcharts-more
|
||||
* @optionparent legend.bubbleLegend
|
||||
*/
|
||||
bubbleLegend: {
|
||||
/**
|
||||
* The color of the ranges borders, can be also defined for an
|
||||
* individual range.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/similartoseries/
|
||||
* Similat look to the bubble series
|
||||
* @sample highcharts/bubble-legend/bordercolor/
|
||||
* Individual bubble border color
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
borderColor: void 0,
|
||||
/**
|
||||
* The width of the ranges borders in pixels, can be also
|
||||
* defined for an individual range.
|
||||
*/
|
||||
borderWidth: 2,
|
||||
/**
|
||||
* An additional class name to apply to the bubble legend'
|
||||
* circle graphical elements. This option does not replace
|
||||
* default class names of the graphical element.
|
||||
*
|
||||
* @sample {highcharts} highcharts/css/bubble-legend/
|
||||
* Styling by CSS
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
className: void 0,
|
||||
/**
|
||||
* The main color of the bubble legend. Applies to ranges, if
|
||||
* individual color is not defined.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/similartoseries/
|
||||
* Similat look to the bubble series
|
||||
* @sample highcharts/bubble-legend/color/
|
||||
* Individual bubble color
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
color: void 0,
|
||||
/**
|
||||
* An additional class name to apply to the bubble legend's
|
||||
* connector graphical elements. This option does not replace
|
||||
* default class names of the graphical element.
|
||||
*
|
||||
* @sample {highcharts} highcharts/css/bubble-legend/
|
||||
* Styling by CSS
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
connectorClassName: void 0,
|
||||
/**
|
||||
* The color of the connector, can be also defined
|
||||
* for an individual range.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
connectorColor: void 0,
|
||||
/**
|
||||
* The length of the connectors in pixels. If labels are
|
||||
* centered, the distance is reduced to 0.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/connectorandlabels/
|
||||
* Increased connector length
|
||||
*/
|
||||
connectorDistance: 60,
|
||||
/**
|
||||
* The width of the connectors in pixels.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/connectorandlabels/
|
||||
* Increased connector width
|
||||
*/
|
||||
connectorWidth: 1,
|
||||
/**
|
||||
* Enable or disable the bubble legend.
|
||||
*/
|
||||
enabled: false,
|
||||
/**
|
||||
* Options for the bubble legend labels.
|
||||
*/
|
||||
labels: {
|
||||
/**
|
||||
* An additional class name to apply to the bubble legend
|
||||
* label graphical elements. This option does not replace
|
||||
* default class names of the graphical element.
|
||||
*
|
||||
* @sample {highcharts} highcharts/css/bubble-legend/
|
||||
* Styling by CSS
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
className: void 0,
|
||||
/**
|
||||
* Whether to allow data labels to overlap.
|
||||
*/
|
||||
allowOverlap: false,
|
||||
/**
|
||||
* A format string for the bubble legend labels. Available
|
||||
* variables are the same as for `formatter`.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/format/
|
||||
* Add a unit
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
format: '',
|
||||
/**
|
||||
* Available `this` properties are:
|
||||
*
|
||||
* - `this.value`: The bubble value.
|
||||
*
|
||||
* - `this.radius`: The radius of the bubble range.
|
||||
*
|
||||
* - `this.center`: The center y position of the range.
|
||||
*
|
||||
* @type {Highcharts.FormatterCallbackFunction<Highcharts.BubbleLegendFormatterContextObject>}
|
||||
*/
|
||||
formatter: void 0,
|
||||
/**
|
||||
* The alignment of the labels compared to the bubble
|
||||
* legend. Can be one of `left`, `center` or `right`.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/connectorandlabels/
|
||||
* Labels on left
|
||||
*
|
||||
* @type {Highcharts.AlignValue}
|
||||
*/
|
||||
align: 'right',
|
||||
/**
|
||||
* CSS styles for the labels.
|
||||
*
|
||||
* @type {Highcharts.CSSObject}
|
||||
*/
|
||||
style: {
|
||||
/** @ignore-option */
|
||||
fontSize: 10,
|
||||
/** @ignore-option */
|
||||
color: void 0
|
||||
},
|
||||
/**
|
||||
* The x position offset of the label relative to the
|
||||
* connector.
|
||||
*/
|
||||
x: 0,
|
||||
/**
|
||||
* The y position offset of the label relative to the
|
||||
* connector.
|
||||
*/
|
||||
y: 0
|
||||
},
|
||||
/**
|
||||
* Miximum bubble legend range size. If values for ranges are
|
||||
* not specified, the `minSize` and the `maxSize` are calculated
|
||||
* from bubble series.
|
||||
*/
|
||||
maxSize: 60,
|
||||
/**
|
||||
* Minimum bubble legend range size. If values for ranges are
|
||||
* not specified, the `minSize` and the `maxSize` are calculated
|
||||
* from bubble series.
|
||||
*/
|
||||
minSize: 10,
|
||||
/**
|
||||
* The position of the bubble legend in the legend.
|
||||
* @sample highcharts/bubble-legend/connectorandlabels/
|
||||
* Bubble legend as last item in legend
|
||||
*/
|
||||
legendIndex: 0,
|
||||
/**
|
||||
* Options for specific range. One range consists of bubble,
|
||||
* label and connector.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/ranges/
|
||||
* Manually defined ranges
|
||||
* @sample highcharts/bubble-legend/autoranges/
|
||||
* Auto calculated ranges
|
||||
*
|
||||
* @type {Array<*>}
|
||||
*/
|
||||
ranges: {
|
||||
/**
|
||||
* Range size value, similar to bubble Z data.
|
||||
* @type {number}
|
||||
*/
|
||||
value: void 0,
|
||||
/**
|
||||
* The color of the border for individual range.
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
borderColor: void 0,
|
||||
/**
|
||||
* The color of the bubble for individual range.
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
color: void 0,
|
||||
/**
|
||||
* The color of the connector for individual range.
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
connectorColor: void 0
|
||||
},
|
||||
/**
|
||||
* Whether the bubble legend range value should be represented
|
||||
* by the area or the width of the bubble. The default, area,
|
||||
* corresponds best to the human perception of the size of each
|
||||
* bubble.
|
||||
*
|
||||
* @sample highcharts/bubble-legend/ranges/
|
||||
* Size by width
|
||||
*
|
||||
* @type {Highcharts.BubbleSizeByValue}
|
||||
*/
|
||||
sizeBy: 'area',
|
||||
/**
|
||||
* When this is true, the absolute value of z determines the
|
||||
* size of the bubble. This means that with the default
|
||||
* zThreshold of 0, a bubble of value -1 will have the same size
|
||||
* as a bubble of value 1, while a bubble of value 0 will have a
|
||||
* smaller size according to minSize.
|
||||
*/
|
||||
sizeByAbsoluteValue: false,
|
||||
/**
|
||||
* Define the visual z index of the bubble legend.
|
||||
*/
|
||||
zIndex: 1,
|
||||
/**
|
||||
* Ranges with with lower value than zThreshold, are skipped.
|
||||
*/
|
||||
zThreshold: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
/**
|
||||
* BubbleLegend class.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.BubbleLegend
|
||||
* @param {Highcharts.LegendBubbleLegendOptions} options
|
||||
* Bubble legend options
|
||||
* @param {Highcharts.Legend} legend
|
||||
* Legend
|
||||
*/
|
||||
var BubbleLegend = /** @class */ (function () {
|
||||
function BubbleLegend(options, legend) {
|
||||
this.chart = void 0;
|
||||
this.fontMetrics = void 0;
|
||||
this.legend = void 0;
|
||||
this.legendGroup = void 0;
|
||||
this.legendItem = void 0;
|
||||
this.legendItemHeight = void 0;
|
||||
this.legendItemWidth = void 0;
|
||||
this.legendSymbol = void 0;
|
||||
this.maxLabel = void 0;
|
||||
this.movementX = void 0;
|
||||
this.ranges = void 0;
|
||||
this.visible = void 0;
|
||||
this.symbols = void 0;
|
||||
this.options = void 0;
|
||||
this.setState = noop;
|
||||
this.init(options, legend);
|
||||
}
|
||||
/**
|
||||
* Create basic bubbleLegend properties similar to item in legend.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#init
|
||||
* @param {Highcharts.LegendBubbleLegendOptions} options
|
||||
* Bubble legend options
|
||||
* @param {Highcharts.Legend} legend
|
||||
* Legend
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.init = function (options, legend) {
|
||||
this.options = options;
|
||||
this.visible = true;
|
||||
this.chart = legend.chart;
|
||||
this.legend = legend;
|
||||
};
|
||||
/**
|
||||
* Depending on the position option, add bubbleLegend to legend items.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#addToLegend
|
||||
* @param {Array<(Highcharts.Point|Highcharts.Series)>}
|
||||
* All legend items
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.addToLegend = function (items) {
|
||||
// Insert bubbleLegend into legend items
|
||||
items.splice(this.options.legendIndex, 0, this);
|
||||
};
|
||||
/**
|
||||
* Calculate ranges, sizes and call the next steps of bubbleLegend
|
||||
* creation.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#drawLegendSymbol
|
||||
* @param {Highcharts.Legend} legend
|
||||
* Legend instance
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.drawLegendSymbol = function (legend) {
|
||||
var chart = this.chart, options = this.options, size, itemDistance = pick(legend.options.itemDistance, 20), connectorSpace, ranges = options.ranges, radius, maxLabel, connectorDistance = options.connectorDistance;
|
||||
// Predict label dimensions
|
||||
this.fontMetrics = chart.renderer.fontMetrics(options.labels.style.fontSize.toString() + 'px');
|
||||
// Do not create bubbleLegend now if ranges or ranges valeus are not
|
||||
// specified or if are empty array.
|
||||
if (!ranges || !ranges.length || !isNumber(ranges[0].value)) {
|
||||
legend.options.bubbleLegend.autoRanges = true;
|
||||
return;
|
||||
}
|
||||
// Sort ranges to right render order
|
||||
stableSort(ranges, function (a, b) {
|
||||
return b.value - a.value;
|
||||
});
|
||||
this.ranges = ranges;
|
||||
this.setOptions();
|
||||
this.render();
|
||||
// Get max label size
|
||||
maxLabel = this.getMaxLabelSize();
|
||||
radius = this.ranges[0].radius;
|
||||
size = radius * 2;
|
||||
// Space for connectors and labels.
|
||||
connectorSpace =
|
||||
connectorDistance - radius + maxLabel.width;
|
||||
connectorSpace = connectorSpace > 0 ? connectorSpace : 0;
|
||||
this.maxLabel = maxLabel;
|
||||
this.movementX = options.labels.align === 'left' ?
|
||||
connectorSpace : 0;
|
||||
this.legendItemWidth = size + connectorSpace + itemDistance;
|
||||
this.legendItemHeight = size + this.fontMetrics.h / 2;
|
||||
};
|
||||
/**
|
||||
* Set style options for each bubbleLegend range.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#setOptions
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.setOptions = function () {
|
||||
var ranges = this.ranges, options = this.options, series = this.chart.series[options.seriesIndex], baseline = this.legend.baseline, bubbleStyle = {
|
||||
'z-index': options.zIndex,
|
||||
'stroke-width': options.borderWidth
|
||||
}, connectorStyle = {
|
||||
'z-index': options.zIndex,
|
||||
'stroke-width': options.connectorWidth
|
||||
}, labelStyle = this.getLabelStyles(), fillOpacity = series.options.marker.fillOpacity, styledMode = this.chart.styledMode;
|
||||
// Allow to parts of styles be used individually for range
|
||||
ranges.forEach(function (range, i) {
|
||||
if (!styledMode) {
|
||||
bubbleStyle.stroke = pick(range.borderColor, options.borderColor, series.color);
|
||||
bubbleStyle.fill = pick(range.color, options.color, fillOpacity !== 1 ?
|
||||
color(series.color).setOpacity(fillOpacity)
|
||||
.get('rgba') :
|
||||
series.color);
|
||||
connectorStyle.stroke = pick(range.connectorColor, options.connectorColor, series.color);
|
||||
}
|
||||
// Set options needed for rendering each range
|
||||
ranges[i].radius = this.getRangeRadius(range.value);
|
||||
ranges[i] = merge(ranges[i], {
|
||||
center: (ranges[0].radius - ranges[i].radius +
|
||||
baseline)
|
||||
});
|
||||
if (!styledMode) {
|
||||
merge(true, ranges[i], {
|
||||
bubbleStyle: merge(false, bubbleStyle),
|
||||
connectorStyle: merge(false, connectorStyle),
|
||||
labelStyle: labelStyle
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
/**
|
||||
* Merge options for bubbleLegend labels.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#getLabelStyles
|
||||
* @return {Highcharts.CSSObject}
|
||||
*/
|
||||
BubbleLegend.prototype.getLabelStyles = function () {
|
||||
var options = this.options, additionalLabelsStyle = {}, labelsOnLeft = options.labels.align === 'left', rtl = this.legend.options.rtl;
|
||||
// To separate additional style options
|
||||
objectEach(options.labels.style, function (value, key) {
|
||||
if (key !== 'color' &&
|
||||
key !== 'fontSize' &&
|
||||
key !== 'z-index') {
|
||||
additionalLabelsStyle[key] = value;
|
||||
}
|
||||
});
|
||||
return merge(false, additionalLabelsStyle, {
|
||||
'font-size': options.labels.style.fontSize,
|
||||
fill: pick(options.labels.style.color, '#000000'),
|
||||
'z-index': options.zIndex,
|
||||
align: rtl || labelsOnLeft ? 'right' : 'left'
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Calculate radius for each bubble range,
|
||||
* used code from BubbleSeries.js 'getRadius' method.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#getRangeRadius
|
||||
* @param {number} value
|
||||
* Range value
|
||||
* @return {number|null}
|
||||
* Radius for one range
|
||||
*/
|
||||
BubbleLegend.prototype.getRangeRadius = function (value) {
|
||||
var options = this.options, seriesIndex = this.options.seriesIndex, bubbleSeries = this.chart.series[seriesIndex], zMax = options.ranges[0].value, zMin = options.ranges[options.ranges.length - 1].value, minSize = options.minSize, maxSize = options.maxSize;
|
||||
return bubbleSeries.getRadius.call(this, zMin, zMax, minSize, maxSize, value);
|
||||
};
|
||||
/**
|
||||
* Render the legendSymbol group.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#render
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.render = function () {
|
||||
var renderer = this.chart.renderer, zThreshold = this.options.zThreshold;
|
||||
if (!this.symbols) {
|
||||
this.symbols = {
|
||||
connectors: [],
|
||||
bubbleItems: [],
|
||||
labels: []
|
||||
};
|
||||
}
|
||||
// Nesting SVG groups to enable handleOverflow
|
||||
this.legendSymbol = renderer.g('bubble-legend');
|
||||
this.legendItem = renderer.g('bubble-legend-item');
|
||||
// To enable default 'hideOverlappingLabels' method
|
||||
this.legendSymbol.translateX = 0;
|
||||
this.legendSymbol.translateY = 0;
|
||||
this.ranges.forEach(function (range) {
|
||||
if (range.value >= zThreshold) {
|
||||
this.renderRange(range);
|
||||
}
|
||||
}, this);
|
||||
// To use handleOverflow method
|
||||
this.legendSymbol.add(this.legendItem);
|
||||
this.legendItem.add(this.legendGroup);
|
||||
this.hideOverlappingLabels();
|
||||
};
|
||||
/**
|
||||
* Render one range, consisting of bubble symbol, connector and label.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#renderRange
|
||||
* @param {Highcharts.LegendBubbleLegendRangesOptions} range
|
||||
* Range options
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.renderRange = function (range) {
|
||||
var mainRange = this.ranges[0], legend = this.legend, options = this.options, labelsOptions = options.labels, chart = this.chart, renderer = chart.renderer, symbols = this.symbols, labels = symbols.labels, label, elementCenter = range.center, absoluteRadius = Math.abs(range.radius), connectorDistance = options.connectorDistance || 0, labelsAlign = labelsOptions.align, rtl = legend.options.rtl, fontSize = labelsOptions.style.fontSize, connectorLength = rtl || labelsAlign === 'left' ?
|
||||
-connectorDistance : connectorDistance, borderWidth = options.borderWidth, connectorWidth = options.connectorWidth, posX = mainRange.radius || 0, posY = elementCenter - absoluteRadius -
|
||||
borderWidth / 2 + connectorWidth / 2, labelY, labelX, fontMetrics = this.fontMetrics, labelMovement = fontSize / 2 - (fontMetrics.h - fontSize) / 2, crispMovement = (posY % 1 ? 1 : 0.5) -
|
||||
(connectorWidth % 2 ? 0 : 0.5), styledMode = renderer.styledMode;
|
||||
// Set options for centered labels
|
||||
if (labelsAlign === 'center') {
|
||||
connectorLength = 0; // do not use connector
|
||||
options.connectorDistance = 0;
|
||||
range.labelStyle.align = 'center';
|
||||
}
|
||||
labelY = posY + options.labels.y;
|
||||
labelX = posX + connectorLength + options.labels.x;
|
||||
// Render bubble symbol
|
||||
symbols.bubbleItems.push(renderer
|
||||
.circle(posX, elementCenter + crispMovement, absoluteRadius)
|
||||
.attr(styledMode ? {} : range.bubbleStyle)
|
||||
.addClass((styledMode ?
|
||||
'highcharts-color-' +
|
||||
this.options.seriesIndex + ' ' :
|
||||
'') +
|
||||
'highcharts-bubble-legend-symbol ' +
|
||||
(options.className || '')).add(this.legendSymbol));
|
||||
// Render connector
|
||||
symbols.connectors.push(renderer
|
||||
.path(renderer.crispLine([
|
||||
['M', posX, posY],
|
||||
['L', posX + connectorLength, posY]
|
||||
], options.connectorWidth))
|
||||
.attr(styledMode ? {} : range.connectorStyle)
|
||||
.addClass((styledMode ?
|
||||
'highcharts-color-' +
|
||||
this.options.seriesIndex + ' ' : '') +
|
||||
'highcharts-bubble-legend-connectors ' +
|
||||
(options.connectorClassName || '')).add(this.legendSymbol));
|
||||
// Render label
|
||||
label = renderer
|
||||
.text(this.formatLabel(range), labelX, labelY + labelMovement)
|
||||
.attr(styledMode ? {} : range.labelStyle)
|
||||
.addClass('highcharts-bubble-legend-labels ' +
|
||||
(options.labels.className || '')).add(this.legendSymbol);
|
||||
labels.push(label);
|
||||
// To enable default 'hideOverlappingLabels' method
|
||||
label.placed = true;
|
||||
label.alignAttr = {
|
||||
x: labelX,
|
||||
y: labelY + labelMovement
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Get the label which takes up the most space.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#getMaxLabelSize
|
||||
* @return {Highcharts.BBoxObject}
|
||||
*/
|
||||
BubbleLegend.prototype.getMaxLabelSize = function () {
|
||||
var labels = this.symbols.labels, maxLabel, labelSize;
|
||||
labels.forEach(function (label) {
|
||||
labelSize = label.getBBox(true);
|
||||
if (maxLabel) {
|
||||
maxLabel = labelSize.width > maxLabel.width ?
|
||||
labelSize : maxLabel;
|
||||
}
|
||||
else {
|
||||
maxLabel = labelSize;
|
||||
}
|
||||
});
|
||||
return maxLabel || {};
|
||||
};
|
||||
/**
|
||||
* Get formatted label for range.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#formatLabel
|
||||
* @param {Highcharts.LegendBubbleLegendRangesOptions} range
|
||||
* Range options
|
||||
* @return {string}
|
||||
* Range label text
|
||||
*/
|
||||
BubbleLegend.prototype.formatLabel = function (range) {
|
||||
var options = this.options, formatter = options.labels.formatter, format = options.labels.format;
|
||||
var numberFormatter = this.chart.numberFormatter;
|
||||
return format ? U.format(format, range) :
|
||||
formatter ? formatter.call(range) :
|
||||
numberFormatter(range.value, 1);
|
||||
};
|
||||
/**
|
||||
* By using default chart 'hideOverlappingLabels' method, hide or show
|
||||
* labels and connectors.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#hideOverlappingLabels
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.hideOverlappingLabels = function () {
|
||||
var chart = this.chart, allowOverlap = this.options.labels.allowOverlap, symbols = this.symbols;
|
||||
if (!allowOverlap && symbols) {
|
||||
chart.hideOverlappingLabels(symbols.labels);
|
||||
// Hide or show connectors
|
||||
symbols.labels.forEach(function (label, index) {
|
||||
if (!label.newOpacity) {
|
||||
symbols.connectors[index].hide();
|
||||
}
|
||||
else if (label.newOpacity !== label.oldOpacity) {
|
||||
symbols.connectors[index].show();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Calculate ranges from created series.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#getRanges
|
||||
* @return {Array<Highcharts.LegendBubbleLegendRangesOptions>}
|
||||
* Array of range objects
|
||||
*/
|
||||
BubbleLegend.prototype.getRanges = function () {
|
||||
var bubbleLegend = this.legend.bubbleLegend, series = bubbleLegend.chart.series, ranges, rangesOptions = bubbleLegend.options.ranges, zData, minZ = Number.MAX_VALUE, maxZ = -Number.MAX_VALUE;
|
||||
series.forEach(function (s) {
|
||||
// Find the min and max Z, like in bubble series
|
||||
if (s.isBubble && !s.ignoreSeries) {
|
||||
zData = s.zData.filter(isNumber);
|
||||
if (zData.length) {
|
||||
minZ = pick(s.options.zMin, Math.min(minZ, Math.max(arrayMin(zData), s.options.displayNegative === false ?
|
||||
s.options.zThreshold :
|
||||
-Number.MAX_VALUE)));
|
||||
maxZ = pick(s.options.zMax, Math.max(maxZ, arrayMax(zData)));
|
||||
}
|
||||
}
|
||||
});
|
||||
// Set values for ranges
|
||||
if (minZ === maxZ) {
|
||||
// Only one range if min and max values are the same.
|
||||
ranges = [{ value: maxZ }];
|
||||
}
|
||||
else {
|
||||
ranges = [
|
||||
{ value: minZ },
|
||||
{ value: (minZ + maxZ) / 2 },
|
||||
{ value: maxZ, autoRanges: true }
|
||||
];
|
||||
}
|
||||
// Prevent reverse order of ranges after redraw
|
||||
if (rangesOptions.length && rangesOptions[0].radius) {
|
||||
ranges.reverse();
|
||||
}
|
||||
// Merge ranges values with user options
|
||||
ranges.forEach(function (range, i) {
|
||||
if (rangesOptions && rangesOptions[i]) {
|
||||
ranges[i] = merge(false, rangesOptions[i], range);
|
||||
}
|
||||
});
|
||||
return ranges;
|
||||
};
|
||||
/**
|
||||
* Calculate bubble legend sizes from rendered series.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#predictBubbleSizes
|
||||
* @return {Array<number,number>}
|
||||
* Calculated min and max bubble sizes
|
||||
*/
|
||||
BubbleLegend.prototype.predictBubbleSizes = function () {
|
||||
var chart = this.chart, fontMetrics = this.fontMetrics, legendOptions = chart.legend.options, floating = legendOptions.floating, horizontal = legendOptions.layout === 'horizontal', lastLineHeight = horizontal ? chart.legend.lastLineHeight : 0, plotSizeX = chart.plotSizeX, plotSizeY = chart.plotSizeY, bubbleSeries = chart.series[this.options.seriesIndex], minSize = Math.ceil(bubbleSeries.minPxSize), maxPxSize = Math.ceil(bubbleSeries.maxPxSize), maxSize = bubbleSeries.options.maxSize, plotSize = Math.min(plotSizeY, plotSizeX), calculatedSize;
|
||||
// Calculate prediceted max size of bubble
|
||||
if (floating || !(/%$/.test(maxSize))) {
|
||||
calculatedSize = maxPxSize;
|
||||
}
|
||||
else {
|
||||
maxSize = parseFloat(maxSize);
|
||||
calculatedSize = ((plotSize + lastLineHeight -
|
||||
fontMetrics.h / 2) * maxSize / 100) / (maxSize / 100 + 1);
|
||||
// Get maxPxSize from bubble series if calculated bubble legend
|
||||
// size will not affect to bubbles series.
|
||||
if ((horizontal && plotSizeY - calculatedSize >=
|
||||
plotSizeX) || (!horizontal && plotSizeX -
|
||||
calculatedSize >= plotSizeY)) {
|
||||
calculatedSize = maxPxSize;
|
||||
}
|
||||
}
|
||||
return [minSize, Math.ceil(calculatedSize)];
|
||||
};
|
||||
/**
|
||||
* Correct ranges with calculated sizes.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#updateRanges
|
||||
* @param {number} min
|
||||
* @param {number} max
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.updateRanges = function (min, max) {
|
||||
var bubbleLegendOptions = this.legend.options.bubbleLegend;
|
||||
bubbleLegendOptions.minSize = min;
|
||||
bubbleLegendOptions.maxSize = max;
|
||||
bubbleLegendOptions.ranges = this.getRanges();
|
||||
};
|
||||
/**
|
||||
* Because of the possibility of creating another legend line, predicted
|
||||
* bubble legend sizes may differ by a few pixels, so it is necessary to
|
||||
* correct them.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.BubbleLegend#correctSizes
|
||||
* @return {void}
|
||||
*/
|
||||
BubbleLegend.prototype.correctSizes = function () {
|
||||
var legend = this.legend, chart = this.chart, bubbleSeries = chart.series[this.options.seriesIndex], bubbleSeriesSize = bubbleSeries.maxPxSize, bubbleLegendSize = this.options.maxSize;
|
||||
if (Math.abs(Math.ceil(bubbleSeriesSize) - bubbleLegendSize) >
|
||||
1) {
|
||||
this.updateRanges(this.options.minSize, bubbleSeries.maxPxSize);
|
||||
legend.render();
|
||||
}
|
||||
};
|
||||
return BubbleLegend;
|
||||
}());
|
||||
// Start the bubble legend creation process.
|
||||
addEvent(Legend, 'afterGetAllItems', function (e) {
|
||||
var legend = this, bubbleLegend = legend.bubbleLegend, legendOptions = legend.options, options = legendOptions.bubbleLegend, bubbleSeriesIndex = legend.chart.getVisibleBubbleSeriesIndex();
|
||||
// Remove unnecessary element
|
||||
if (bubbleLegend && bubbleLegend.ranges && bubbleLegend.ranges.length) {
|
||||
// Allow change the way of calculating ranges in update
|
||||
if (options.ranges.length) {
|
||||
options.autoRanges =
|
||||
!!options.ranges[0].autoRanges;
|
||||
}
|
||||
// Update bubbleLegend dimensions in each redraw
|
||||
legend.destroyItem(bubbleLegend);
|
||||
}
|
||||
// Create bubble legend
|
||||
if (bubbleSeriesIndex >= 0 &&
|
||||
legendOptions.enabled &&
|
||||
options.enabled) {
|
||||
options.seriesIndex = bubbleSeriesIndex;
|
||||
legend.bubbleLegend = new H.BubbleLegend(options, legend);
|
||||
legend.bubbleLegend.addToLegend(e.allItems);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* Check if there is at least one visible bubble series.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Chart#getVisibleBubbleSeriesIndex
|
||||
* @return {number}
|
||||
* First visible bubble series index
|
||||
*/
|
||||
Chart.prototype.getVisibleBubbleSeriesIndex = function () {
|
||||
var series = this.series, i = 0;
|
||||
while (i < series.length) {
|
||||
if (series[i] &&
|
||||
series[i].isBubble &&
|
||||
series[i].visible &&
|
||||
series[i].zData.length) {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
/**
|
||||
* Calculate height for each row in legend.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Legend#getLinesHeights
|
||||
* @return {Array<Highcharts.Dictionary<number>>}
|
||||
* Informations about line height and items amount
|
||||
*/
|
||||
Legend.prototype.getLinesHeights = function () {
|
||||
var items = this.allItems, lines = [], lastLine, length = items.length, i = 0, j = 0;
|
||||
for (i = 0; i < length; i++) {
|
||||
if (items[i].legendItemHeight) {
|
||||
// for bubbleLegend
|
||||
items[i].itemHeight = items[i].legendItemHeight;
|
||||
}
|
||||
if ( // Line break
|
||||
items[i] === items[length - 1] ||
|
||||
items[i + 1] &&
|
||||
items[i]._legendItemPos[1] !==
|
||||
items[i + 1]._legendItemPos[1]) {
|
||||
lines.push({ height: 0 });
|
||||
lastLine = lines[lines.length - 1];
|
||||
// Find the highest item in line
|
||||
for (j; j <= i; j++) {
|
||||
if (items[j].itemHeight > lastLine.height) {
|
||||
lastLine.height = items[j].itemHeight;
|
||||
}
|
||||
}
|
||||
lastLine.step = i;
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
/**
|
||||
* Correct legend items translation in case of different elements heights.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Legend#retranslateItems
|
||||
* @param {Array<Highcharts.Dictionary<number>>} lines
|
||||
* Informations about line height and items amount
|
||||
* @return {void}
|
||||
*/
|
||||
Legend.prototype.retranslateItems = function (lines) {
|
||||
var items = this.allItems, orgTranslateX, orgTranslateY, movementX, rtl = this.options.rtl, actualLine = 0;
|
||||
items.forEach(function (item, index) {
|
||||
orgTranslateX = item.legendGroup.translateX;
|
||||
orgTranslateY = item._legendItemPos[1];
|
||||
movementX = item.movementX;
|
||||
if (movementX || (rtl && item.ranges)) {
|
||||
movementX = rtl ?
|
||||
orgTranslateX - item.options.maxSize / 2 :
|
||||
orgTranslateX + movementX;
|
||||
item.legendGroup.attr({ translateX: movementX });
|
||||
}
|
||||
if (index > lines[actualLine].step) {
|
||||
actualLine++;
|
||||
}
|
||||
item.legendGroup.attr({
|
||||
translateY: Math.round(orgTranslateY + lines[actualLine].height / 2)
|
||||
});
|
||||
item._legendItemPos[1] = orgTranslateY +
|
||||
lines[actualLine].height / 2;
|
||||
});
|
||||
};
|
||||
// Toggle bubble legend depending on the visible status of bubble series.
|
||||
addEvent(Series, 'legendItemClick', function () {
|
||||
var series = this, chart = series.chart, visible = series.visible, legend = series.chart.legend, status;
|
||||
if (legend && legend.bubbleLegend) {
|
||||
// Temporary correct 'visible' property
|
||||
series.visible = !visible;
|
||||
// Save future status for getRanges method
|
||||
series.ignoreSeries = visible;
|
||||
// Check if at lest one bubble series is visible
|
||||
status = chart.getVisibleBubbleSeriesIndex() >= 0;
|
||||
// Hide bubble legend if all bubble series are disabled
|
||||
if (legend.bubbleLegend.visible !== status) {
|
||||
// Show or hide bubble legend
|
||||
legend.update({
|
||||
bubbleLegend: { enabled: status }
|
||||
});
|
||||
legend.bubbleLegend.visible = status; // Restore default status
|
||||
}
|
||||
series.visible = visible;
|
||||
}
|
||||
});
|
||||
// If ranges are not specified, determine ranges from rendered bubble series
|
||||
// and render legend again.
|
||||
wrap(Chart.prototype, 'drawChartBox', function (proceed, options, callback) {
|
||||
var chart = this, legend = chart.legend, bubbleSeries = chart.getVisibleBubbleSeriesIndex() >= 0, bubbleLegendOptions, bubbleSizes;
|
||||
if (legend && legend.options.enabled && legend.bubbleLegend &&
|
||||
legend.options.bubbleLegend.autoRanges && bubbleSeries) {
|
||||
bubbleLegendOptions = legend.bubbleLegend.options;
|
||||
bubbleSizes = legend.bubbleLegend.predictBubbleSizes();
|
||||
legend.bubbleLegend.updateRanges(bubbleSizes[0], bubbleSizes[1]);
|
||||
// Disable animation on init
|
||||
if (!bubbleLegendOptions.placed) {
|
||||
legend.group.placed = false;
|
||||
legend.allItems.forEach(function (item) {
|
||||
item.legendGroup.translateY = null;
|
||||
});
|
||||
}
|
||||
// Create legend with bubbleLegend
|
||||
legend.render();
|
||||
chart.getMargins();
|
||||
chart.axes.forEach(function (axis) {
|
||||
if (axis.visible) { // #11448
|
||||
axis.render();
|
||||
}
|
||||
if (!bubbleLegendOptions.placed) {
|
||||
axis.setScale();
|
||||
axis.updateNames();
|
||||
// Disable axis animation on init
|
||||
objectEach(axis.ticks, function (tick) {
|
||||
tick.isNew = true;
|
||||
tick.isNewLabel = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
bubbleLegendOptions.placed = true;
|
||||
// After recalculate axes, calculate margins again.
|
||||
chart.getMargins();
|
||||
// Call default 'drawChartBox' method.
|
||||
proceed.call(chart, options, callback);
|
||||
// Check bubble legend sizes and correct them if necessary.
|
||||
legend.bubbleLegend.correctSizes();
|
||||
// Correct items positions with different dimensions in legend.
|
||||
legend.retranslateItems(legend.getLinesHeights());
|
||||
}
|
||||
else {
|
||||
proceed.call(chart, options, callback);
|
||||
// Allow color change on static bubble legend after click on legend
|
||||
if (legend && legend.options.enabled && legend.bubbleLegend) {
|
||||
legend.render();
|
||||
legend.retranslateItems(legend.getLinesHeights());
|
||||
}
|
||||
}
|
||||
});
|
||||
H.BubbleLegend = BubbleLegend;
|
||||
export default H.BubbleLegend;
|
||||
@@ -0,0 +1,563 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
/**
|
||||
* @typedef {"area"|"width"} Highcharts.BubbleSizeByValue
|
||||
*/
|
||||
import Color from '../../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import Point from '../../Core/Series/Point.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, extend = U.extend, isNumber = U.isNumber, pick = U.pick, pInt = U.pInt, seriesType = U.seriesType;
|
||||
import '../../Core/Axis/Axis.js';
|
||||
import '../../Core/Series/Series.js';
|
||||
import '../../Series/ScatterSeries.js';
|
||||
import './BubbleLegend.js';
|
||||
var Axis = H.Axis, noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* A bubble series is a three dimensional series type where each point renders
|
||||
* an X, Y and Z value. Each points is drawn as a bubble where the position
|
||||
* along the X and Y axes mark the X and Y values, and the size of the bubble
|
||||
* relates to the Z value.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/bubble/
|
||||
* Bubble chart
|
||||
*
|
||||
* @extends plotOptions.scatter
|
||||
* @excluding cluster
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.bubble
|
||||
*/
|
||||
seriesType('bubble', 'scatter', {
|
||||
dataLabels: {
|
||||
formatter: function () {
|
||||
return this.point.z;
|
||||
},
|
||||
inside: true,
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
/**
|
||||
* If there are more points in the series than the `animationLimit`, the
|
||||
* animation won't run. Animation affects overall performance and doesn't
|
||||
* work well with heavy data series.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*/
|
||||
animationLimit: 250,
|
||||
/**
|
||||
* Whether to display negative sized bubbles. The threshold is given
|
||||
* by the [zThreshold](#plotOptions.bubble.zThreshold) option, and negative
|
||||
* bubbles can be visualized by setting
|
||||
* [negativeColor](#plotOptions.bubble.negativeColor).
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-negative/
|
||||
* Negative bubbles
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
* @since 3.0
|
||||
* @apioption plotOptions.bubble.displayNegative
|
||||
*/
|
||||
/**
|
||||
* @extends plotOptions.series.marker
|
||||
* @excluding enabled, enabledThreshold, height, radius, width
|
||||
*/
|
||||
marker: {
|
||||
lineColor: null,
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* The fill opacity of the bubble markers.
|
||||
*/
|
||||
fillOpacity: 0.5,
|
||||
/**
|
||||
* In bubble charts, the radius is overridden and determined based on
|
||||
* the point's data value.
|
||||
*
|
||||
* @ignore-option
|
||||
*/
|
||||
radius: null,
|
||||
states: {
|
||||
hover: {
|
||||
radiusPlus: 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* A predefined shape or symbol for the marker. Possible values are
|
||||
* "circle", "square", "diamond", "triangle" and "triangle-down".
|
||||
*
|
||||
* Additionally, the URL to a graphic can be given on the form
|
||||
* `url(graphic.png)`. Note that for the image to be applied to exported
|
||||
* charts, its URL needs to be accessible by the export server.
|
||||
*
|
||||
* Custom callbacks for symbol path generation can also be added to
|
||||
* `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
|
||||
* used by its method name, as shown in the demo.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-symbol/
|
||||
* Bubble chart with various symbols
|
||||
* @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
|
||||
* General chart with predefined, graphic and custom markers
|
||||
*
|
||||
* @type {Highcharts.SymbolKeyValue|string}
|
||||
* @since 5.0.11
|
||||
*/
|
||||
symbol: 'circle'
|
||||
},
|
||||
/**
|
||||
* Minimum bubble size. Bubbles will automatically size between the
|
||||
* `minSize` and `maxSize` to reflect the `z` value of each bubble.
|
||||
* Can be either pixels (when no unit is given), or a percentage of
|
||||
* the smallest one of the plot width and height.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-size/
|
||||
* Bubble size
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 3.0
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
minSize: 8,
|
||||
/**
|
||||
* Maximum bubble size. Bubbles will automatically size between the
|
||||
* `minSize` and `maxSize` to reflect the `z` value of each bubble.
|
||||
* Can be either pixels (when no unit is given), or a percentage of
|
||||
* the smallest one of the plot width and height.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-size/
|
||||
* Bubble size
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 3.0
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
maxSize: '20%',
|
||||
/**
|
||||
* When a point's Z value is below the
|
||||
* [zThreshold](#plotOptions.bubble.zThreshold) setting, this color is used.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-negative/
|
||||
* Negative bubbles
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.bubble.negativeColor
|
||||
*/
|
||||
/**
|
||||
* Whether the bubble's value should be represented by the area or the
|
||||
* width of the bubble. The default, `area`, corresponds best to the
|
||||
* human perception of the size of each bubble.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-sizeby/
|
||||
* Comparison of area and size
|
||||
*
|
||||
* @type {Highcharts.BubbleSizeByValue}
|
||||
* @default area
|
||||
* @since 3.0.7
|
||||
* @apioption plotOptions.bubble.sizeBy
|
||||
*/
|
||||
/**
|
||||
* When this is true, the absolute value of z determines the size of
|
||||
* the bubble. This means that with the default `zThreshold` of 0, a
|
||||
* bubble of value -1 will have the same size as a bubble of value 1,
|
||||
* while a bubble of value 0 will have a smaller size according to
|
||||
* `minSize`.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-sizebyabsolutevalue/
|
||||
* Size by absolute value, various thresholds
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 4.1.9
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.bubble.sizeByAbsoluteValue
|
||||
*/
|
||||
/**
|
||||
* When this is true, the series will not cause the Y axis to cross
|
||||
* the zero plane (or [threshold](#plotOptions.series.threshold) option)
|
||||
* unless the data actually crosses the plane.
|
||||
*
|
||||
* For example, if `softThreshold` is `false`, a series of 0, 1, 2,
|
||||
* 3 will make the Y axis show negative values according to the `minPadding`
|
||||
* option. If `softThreshold` is `true`, the Y axis starts at 0.
|
||||
*
|
||||
* @since 4.1.9
|
||||
* @product highcharts
|
||||
*/
|
||||
softThreshold: false,
|
||||
states: {
|
||||
hover: {
|
||||
halo: {
|
||||
size: 5
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '({point.x}, {point.y}), Size: {point.z}'
|
||||
},
|
||||
turboThreshold: 0,
|
||||
/**
|
||||
* The minimum for the Z value range. Defaults to the highest Z value
|
||||
* in the data.
|
||||
*
|
||||
* @see [zMin](#plotOptions.bubble.zMin)
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-zmin-zmax/
|
||||
* Z has a possible range of 0-100
|
||||
*
|
||||
* @type {number}
|
||||
* @since 4.0.3
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.bubble.zMax
|
||||
*/
|
||||
/**
|
||||
* @default z
|
||||
* @apioption plotOptions.bubble.colorKey
|
||||
*/
|
||||
/**
|
||||
* The minimum for the Z value range. Defaults to the lowest Z value
|
||||
* in the data.
|
||||
*
|
||||
* @see [zMax](#plotOptions.bubble.zMax)
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-zmin-zmax/
|
||||
* Z has a possible range of 0-100
|
||||
*
|
||||
* @type {number}
|
||||
* @since 4.0.3
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.bubble.zMin
|
||||
*/
|
||||
/**
|
||||
* When [displayNegative](#plotOptions.bubble.displayNegative) is `false`,
|
||||
* bubbles with lower Z values are skipped. When `displayNegative`
|
||||
* is `true` and a [negativeColor](#plotOptions.bubble.negativeColor)
|
||||
* is given, points with lower Z is colored.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/bubble-negative/
|
||||
* Negative bubbles
|
||||
*
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
zThreshold: 0,
|
||||
zoneAxis: 'z'
|
||||
// Prototype members
|
||||
}, {
|
||||
pointArrayMap: ['y', 'z'],
|
||||
parallelArrays: ['x', 'y', 'z'],
|
||||
trackerGroups: ['group', 'dataLabelsGroup'],
|
||||
specialGroup: 'group',
|
||||
bubblePadding: true,
|
||||
zoneAxis: 'z',
|
||||
directTouch: true,
|
||||
isBubble: true,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
pointAttribs: function (point, state) {
|
||||
var markerOptions = this.options.marker, fillOpacity = markerOptions.fillOpacity, attr = Series.prototype.pointAttribs.call(this, point, state);
|
||||
if (fillOpacity !== 1) {
|
||||
attr.fill = color(attr.fill)
|
||||
.setOpacity(fillOpacity)
|
||||
.get('rgba');
|
||||
}
|
||||
return attr;
|
||||
},
|
||||
/**
|
||||
* Get the radius for each point based on the minSize, maxSize and each
|
||||
* point's Z value. This must be done prior to Series.translate because
|
||||
* the axis needs to add padding in accordance with the point sizes.
|
||||
* @private
|
||||
*/
|
||||
getRadii: function (zMin, zMax, series) {
|
||||
var len, i, zData = this.zData, yData = this.yData, minSize = series.minPxSize, maxSize = series.maxPxSize, radii = [], value;
|
||||
// Set the shape type and arguments to be picked up in drawPoints
|
||||
for (i = 0, len = zData.length; i < len; i++) {
|
||||
value = zData[i];
|
||||
// Separate method to get individual radius for bubbleLegend
|
||||
radii.push(this.getRadius(zMin, zMax, minSize, maxSize, value, yData[i]));
|
||||
}
|
||||
this.radii = radii;
|
||||
},
|
||||
/**
|
||||
* Get the individual radius for one point.
|
||||
* @private
|
||||
*/
|
||||
getRadius: function (zMin, zMax, minSize, maxSize, value, yValue) {
|
||||
var options = this.options, sizeByArea = options.sizeBy !== 'width', zThreshold = options.zThreshold, zRange = zMax - zMin, pos = 0.5;
|
||||
// #8608 - bubble should be visible when z is undefined
|
||||
if (yValue === null || value === null) {
|
||||
return null;
|
||||
}
|
||||
if (isNumber(value)) {
|
||||
// When sizing by threshold, the absolute value of z determines
|
||||
// the size of the bubble.
|
||||
if (options.sizeByAbsoluteValue) {
|
||||
value = Math.abs(value - zThreshold);
|
||||
zMax = zRange = Math.max(zMax - zThreshold, Math.abs(zMin - zThreshold));
|
||||
zMin = 0;
|
||||
}
|
||||
// Issue #4419 - if value is less than zMin, push a radius that's
|
||||
// always smaller than the minimum size
|
||||
if (value < zMin) {
|
||||
return minSize / 2 - 1;
|
||||
}
|
||||
// Relative size, a number between 0 and 1
|
||||
if (zRange > 0) {
|
||||
pos = (value - zMin) / zRange;
|
||||
}
|
||||
}
|
||||
if (sizeByArea && pos >= 0) {
|
||||
pos = Math.sqrt(pos);
|
||||
}
|
||||
return Math.ceil(minSize + pos * (maxSize - minSize)) / 2;
|
||||
},
|
||||
/**
|
||||
* Perform animation on the bubbles
|
||||
* @private
|
||||
*/
|
||||
animate: function (init) {
|
||||
if (!init &&
|
||||
this.points.length < this.options.animationLimit // #8099
|
||||
) {
|
||||
this.points.forEach(function (point) {
|
||||
var graphic = point.graphic;
|
||||
if (graphic && graphic.width) { // URL symbols don't have width
|
||||
// Start values
|
||||
if (!this.hasRendered) {
|
||||
graphic.attr({
|
||||
x: point.plotX,
|
||||
y: point.plotY,
|
||||
width: 1,
|
||||
height: 1
|
||||
});
|
||||
}
|
||||
// Run animation
|
||||
graphic.animate(this.markerAttribs(point), this.options.animation);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Define hasData function for non-cartesian series.
|
||||
* Returns true if the series has points at all.
|
||||
* @private
|
||||
*/
|
||||
hasData: function () {
|
||||
return !!this.processedXData.length; // != 0
|
||||
},
|
||||
/**
|
||||
* Extend the base translate method to handle bubble size
|
||||
* @private
|
||||
*/
|
||||
translate: function () {
|
||||
var i, data = this.data, point, radius, radii = this.radii;
|
||||
// Run the parent method
|
||||
seriesTypes.scatter.prototype.translate.call(this);
|
||||
// Set the shape type and arguments to be picked up in drawPoints
|
||||
i = data.length;
|
||||
while (i--) {
|
||||
point = data[i];
|
||||
radius = radii ? radii[i] : 0; // #1737
|
||||
if (isNumber(radius) && radius >= this.minPxSize / 2) {
|
||||
// Shape arguments
|
||||
point.marker = extend(point.marker, {
|
||||
radius: radius,
|
||||
width: 2 * radius,
|
||||
height: 2 * radius
|
||||
});
|
||||
// Alignment box for the data label
|
||||
point.dlBox = {
|
||||
x: point.plotX - radius,
|
||||
y: point.plotY - radius,
|
||||
width: 2 * radius,
|
||||
height: 2 * radius
|
||||
};
|
||||
}
|
||||
else { // below zThreshold
|
||||
// #1691
|
||||
point.shapeArgs = point.plotY = point.dlBox = void 0;
|
||||
}
|
||||
}
|
||||
},
|
||||
alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
|
||||
buildKDTree: noop,
|
||||
applyZones: noop
|
||||
// Point class
|
||||
}, {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
haloPath: function (size) {
|
||||
return Point.prototype.haloPath.call(this,
|
||||
// #6067
|
||||
size === 0 ? 0 : (this.marker ? this.marker.radius || 0 : 0) + size);
|
||||
},
|
||||
ttBelow: false
|
||||
});
|
||||
// Add logic to pad each axis with the amount of pixels necessary to avoid the
|
||||
// bubbles to overflow.
|
||||
Axis.prototype.beforePadding = function () {
|
||||
var axis = this, axisLength = this.len, chart = this.chart, pxMin = 0, pxMax = axisLength, isXAxis = this.isXAxis, dataKey = isXAxis ? 'xData' : 'yData', min = this.min, extremes = {}, smallestSize = Math.min(chart.plotWidth, chart.plotHeight), zMin = Number.MAX_VALUE, zMax = -Number.MAX_VALUE, range = this.max - min, transA = axisLength / range, activeSeries = [];
|
||||
// Handle padding on the second pass, or on redraw
|
||||
this.series.forEach(function (series) {
|
||||
var seriesOptions = series.options, zData;
|
||||
if (series.bubblePadding &&
|
||||
(series.visible || !chart.options.chart.ignoreHiddenSeries)) {
|
||||
// Correction for #1673
|
||||
axis.allowZoomOutside = true;
|
||||
// Cache it
|
||||
activeSeries.push(series);
|
||||
if (isXAxis) { // because X axis is evaluated first
|
||||
// For each series, translate the size extremes to pixel values
|
||||
['minSize', 'maxSize'].forEach(function (prop) {
|
||||
var length = seriesOptions[prop], isPercent = /%$/.test(length);
|
||||
length = pInt(length);
|
||||
extremes[prop] = isPercent ?
|
||||
smallestSize * length / 100 :
|
||||
length;
|
||||
});
|
||||
series.minPxSize = extremes.minSize;
|
||||
// Prioritize min size if conflict to make sure bubbles are
|
||||
// always visible. #5873
|
||||
series.maxPxSize = Math.max(extremes.maxSize, extremes.minSize);
|
||||
// Find the min and max Z
|
||||
zData = series.zData.filter(isNumber);
|
||||
if (zData.length) { // #1735
|
||||
zMin = pick(seriesOptions.zMin, clamp(arrayMin(zData), seriesOptions.displayNegative === false ?
|
||||
seriesOptions.zThreshold :
|
||||
-Number.MAX_VALUE, zMin));
|
||||
zMax = pick(seriesOptions.zMax, Math.max(zMax, arrayMax(zData)));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
activeSeries.forEach(function (series) {
|
||||
var data = series[dataKey], i = data.length, radius;
|
||||
if (isXAxis) {
|
||||
series.getRadii(zMin, zMax, series);
|
||||
}
|
||||
if (range > 0) {
|
||||
while (i--) {
|
||||
if (isNumber(data[i]) &&
|
||||
axis.dataMin <= data[i] &&
|
||||
data[i] <= axis.max) {
|
||||
radius = series.radii ? series.radii[i] : 0;
|
||||
pxMin = Math.min(((data[i] - min) * transA) - radius, pxMin);
|
||||
pxMax = Math.max(((data[i] - min) * transA) + radius, pxMax);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// Apply the padding to the min and max properties
|
||||
if (activeSeries.length && range > 0 && !this.logarithmic) {
|
||||
pxMax -= axisLength;
|
||||
transA *= (axisLength +
|
||||
Math.max(0, pxMin) - // #8901
|
||||
Math.min(pxMax, axisLength)) / axisLength;
|
||||
[
|
||||
['min', 'userMin', pxMin],
|
||||
['max', 'userMax', pxMax]
|
||||
].forEach(function (keys) {
|
||||
if (typeof pick(axis.options[keys[0]], axis[keys[1]]) === 'undefined') {
|
||||
axis[keys[0]] += keys[2] / transA;
|
||||
}
|
||||
});
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
};
|
||||
/**
|
||||
* A `bubble` series. If the [type](#series.bubble.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.bubble
|
||||
* @excluding dataParser, dataURL, stack
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @apioption series.bubble
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `bubble` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,y,z`. If the first value is a string, it is applied as the name of
|
||||
* the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2\. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 1, 2],
|
||||
* [1, 5, 5],
|
||||
* [2, 0, 2]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.bubble.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 1,
|
||||
* z: 1,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 5,
|
||||
* z: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts
|
||||
* @apioption series.bubble.data
|
||||
*/
|
||||
/**
|
||||
* @extends series.line.data.marker
|
||||
* @excluding enabledThreshold, height, radius, width
|
||||
* @product highcharts
|
||||
* @apioption series.bubble.data.marker
|
||||
*/
|
||||
/**
|
||||
* The size value for each bubble. The bubbles' diameters are computed
|
||||
* based on the `z`, and controlled by series options like `minSize`,
|
||||
* `maxSize`, `sizeBy`, `zMin` and `zMax`.
|
||||
*
|
||||
* @type {number|null}
|
||||
* @product highcharts
|
||||
* @apioption series.bubble.data.z
|
||||
*/
|
||||
/**
|
||||
* @excluding enabled, enabledThreshold, height, radius, width
|
||||
* @apioption series.bubble.marker
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,308 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Kacper Madej
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var isNumber = U.isNumber, merge = U.merge, pick = U.pick, relativeLength = U.relativeLength, seriesType = U.seriesType;
|
||||
var columnProto = H.seriesTypes.column.prototype;
|
||||
/**
|
||||
* The bullet series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.bullet
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('bullet', 'column'
|
||||
/**
|
||||
* A bullet graph is a variation of a bar graph. The bullet graph features
|
||||
* a single measure, compares it to a target, and displays it in the context
|
||||
* of qualitative ranges of performance that could be set using
|
||||
* [plotBands](#yAxis.plotBands) on [yAxis](#yAxis).
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/bullet-graph/
|
||||
* Bullet graph
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @excluding allAreas, boostThreshold, colorAxis, compare, compareBase,
|
||||
* dataSorting, boostBlending
|
||||
* @requires modules/bullet
|
||||
* @optionparent plotOptions.bullet
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* All options related with look and positiong of targets.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
targetOptions: {
|
||||
/**
|
||||
* The width of the rectangle representing the target. Could be set
|
||||
* as a pixel value or as a percentage of a column width.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
width: '140%',
|
||||
/**
|
||||
* The height of the rectangle representing the target.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
height: 3,
|
||||
/**
|
||||
* The border color of the rectangle representing the target. When
|
||||
* not set, the point's border color is used.
|
||||
*
|
||||
* In styled mode, use class `highcharts-bullet-target` instead.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.bullet.targetOptions.borderColor
|
||||
*/
|
||||
/**
|
||||
* The color of the rectangle representing the target. When not set,
|
||||
* point's color (if set in point's options -
|
||||
* [`color`](#series.bullet.data.color)) or zone of the target value
|
||||
* (if [`zones`](#plotOptions.bullet.zones) or
|
||||
* [`negativeColor`](#plotOptions.bullet.negativeColor) are set)
|
||||
* or the same color as the point has is used.
|
||||
*
|
||||
* In styled mode, use class `highcharts-bullet-target` instead.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.bullet.targetOptions.color
|
||||
*/
|
||||
/**
|
||||
* The border width of the rectangle representing the target.
|
||||
*
|
||||
* In styled mode, use class `highcharts-bullet-target` instead.
|
||||
*
|
||||
* @since 6.0.0
|
||||
*/
|
||||
borderWidth: 0
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{series.color}">\u25CF</span>' +
|
||||
' {series.name}: <b>{point.y}</b>. Target: <b>{point.target}' +
|
||||
'</b><br/>'
|
||||
}
|
||||
}, {
|
||||
pointArrayMap: ['y', 'target'],
|
||||
parallelArrays: ['x', 'y', 'target'],
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Draws the targets. For inverted chart, the `series.group` is rotated,
|
||||
* so the same coordinates apply. This method is based on column series
|
||||
* drawPoints function.
|
||||
*
|
||||
* @ignore
|
||||
* @function Highcharts.Series#drawPoints
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, chart = series.chart, options = series.options, animationLimit = options.animationLimit || 250;
|
||||
columnProto.drawPoints.apply(this);
|
||||
series.points.forEach(function (point) {
|
||||
var pointOptions = point.options, shapeArgs, targetGraphic = point.targetGraphic, targetShapeArgs, targetVal = point.target, pointVal = point.y, width, height, targetOptions, y;
|
||||
if (isNumber(targetVal) && targetVal !== null) {
|
||||
targetOptions = merge(options.targetOptions, pointOptions.targetOptions);
|
||||
height = targetOptions.height;
|
||||
shapeArgs = point.shapeArgs;
|
||||
width = relativeLength(targetOptions.width, shapeArgs.width);
|
||||
y = series.yAxis.translate(targetVal, false, true, false, true) - targetOptions.height / 2 - 0.5;
|
||||
targetShapeArgs = series.crispCol.apply({
|
||||
// Use fake series object to set borderWidth of target
|
||||
chart: chart,
|
||||
borderWidth: targetOptions.borderWidth,
|
||||
options: {
|
||||
crisp: options.crisp
|
||||
}
|
||||
}, [
|
||||
(shapeArgs.x +
|
||||
shapeArgs.width / 2 - width / 2),
|
||||
y,
|
||||
width,
|
||||
height
|
||||
]);
|
||||
if (targetGraphic) {
|
||||
// Update
|
||||
targetGraphic[chart.pointCount < animationLimit ?
|
||||
'animate' :
|
||||
'attr'](targetShapeArgs);
|
||||
// Add or remove tooltip reference
|
||||
if (isNumber(pointVal) && pointVal !== null) {
|
||||
targetGraphic.element.point = point;
|
||||
}
|
||||
else {
|
||||
targetGraphic.element.point = void 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
point.targetGraphic = targetGraphic = chart.renderer
|
||||
.rect()
|
||||
.attr(targetShapeArgs)
|
||||
.add(series.group);
|
||||
}
|
||||
// Presentational
|
||||
if (!chart.styledMode) {
|
||||
targetGraphic.attr({
|
||||
fill: pick(targetOptions.color, pointOptions.color, (series.zones.length && (point.getZone.call({
|
||||
series: series,
|
||||
x: point.x,
|
||||
y: targetVal,
|
||||
options: {}
|
||||
}).color || series.color)) || void 0, point.color, series.color),
|
||||
stroke: pick(targetOptions.borderColor, point.borderColor, series.options.borderColor),
|
||||
'stroke-width': targetOptions.borderWidth
|
||||
});
|
||||
}
|
||||
// Add tooltip reference
|
||||
if (isNumber(pointVal) && pointVal !== null) {
|
||||
targetGraphic.element.point = point;
|
||||
}
|
||||
targetGraphic.addClass(point.getClassName() +
|
||||
' highcharts-bullet-target', true);
|
||||
}
|
||||
else if (targetGraphic) {
|
||||
// #1269:
|
||||
point.targetGraphic = targetGraphic.destroy();
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Includes target values to extend extremes from y values.
|
||||
*
|
||||
* @ignore
|
||||
* @function Highcharts.Series#getExtremes
|
||||
*/
|
||||
getExtremes: function (yData) {
|
||||
var series = this, targetData = series.targetData, yMax, yMin;
|
||||
var dataExtremes = columnProto.getExtremes.call(this, yData);
|
||||
if (targetData && targetData.length) {
|
||||
var targetExtremes = columnProto.getExtremes.call(this, targetData);
|
||||
if (isNumber(targetExtremes.dataMin)) {
|
||||
dataExtremes.dataMin = Math.min(pick(dataExtremes.dataMin, Infinity), targetExtremes.dataMin);
|
||||
}
|
||||
if (isNumber(targetExtremes.dataMax)) {
|
||||
dataExtremes.dataMax = Math.max(pick(dataExtremes.dataMax, -Infinity), targetExtremes.dataMax);
|
||||
}
|
||||
}
|
||||
return dataExtremes;
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
},
|
||||
/** @lends Highcharts.seriesTypes.ohlc.prototype.pointClass.prototype */
|
||||
{
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Destroys target graphic.
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
destroy: function () {
|
||||
if (this.targetGraphic) {
|
||||
this.targetGraphic = this.targetGraphic.destroy();
|
||||
}
|
||||
columnProto.pointClass.prototype.destroy
|
||||
.apply(this, arguments);
|
||||
return;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `bullet` series. If the [type](#series.bullet.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.bullet
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @excluding dataParser, dataURL, marker, dataSorting, boostThreshold,
|
||||
* boostBlending
|
||||
* @requires modules/bullet
|
||||
* @apioption series.bullet
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `bullet` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,y,target`. If the first value is a string, it is applied as the name
|
||||
* of the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2\. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 40, 75],
|
||||
* [1, 50, 50],
|
||||
* [2, 60, 40]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.bullet.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 0,
|
||||
* y: 40,
|
||||
* target: 75,
|
||||
* name: "Point1",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 60,
|
||||
* target: 40,
|
||||
* name: "Point2",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<Array<(number|string),number>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.column.data
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption series.bullet.data
|
||||
*/
|
||||
/**
|
||||
* The target value of a point.
|
||||
*
|
||||
* @type {number}
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption series.bullet.data.target
|
||||
*/
|
||||
/**
|
||||
* Individual target options for each point.
|
||||
*
|
||||
* @extends plotOptions.bullet.targetOptions
|
||||
* @product highcharts
|
||||
* @apioption series.bullet.data.targetOptions
|
||||
*/
|
||||
/**
|
||||
* @product highcharts
|
||||
* @excluding halo, lineWidth, lineWidthPlus, marker
|
||||
* @apioption series.bullet.states.hover
|
||||
*/
|
||||
/**
|
||||
* @product highcharts
|
||||
* @excluding halo, lineWidth, lineWidthPlus, marker
|
||||
* @apioption series.bullet.states.select
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,293 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import O from '../Core/Options.js';
|
||||
var defaultOptions = O.defaultOptions;
|
||||
import U from '../Core/Utilities.js';
|
||||
var merge = U.merge, seriesType = U.seriesType;
|
||||
var seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* A candlestick chart is a style of financial chart used to describe price
|
||||
* movements over time.
|
||||
*
|
||||
* @sample stock/demo/candlestick/
|
||||
* Candlestick chart
|
||||
*
|
||||
* @extends plotOptions.ohlc
|
||||
* @excluding borderColor,borderRadius,borderWidth
|
||||
* @product highstock
|
||||
* @optionparent plotOptions.candlestick
|
||||
*/
|
||||
var candlestickOptions = {
|
||||
/**
|
||||
* The specific line color for up candle sticks. The default is to inherit
|
||||
* the general `lineColor` setting.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/candlestick-linecolor/
|
||||
* Candlestick line colors
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 1.3.6
|
||||
* @product highstock
|
||||
* @apioption plotOptions.candlestick.upLineColor
|
||||
*/
|
||||
/**
|
||||
* @type {Highcharts.DataGroupingApproximationValue|Function}
|
||||
* @default ohlc
|
||||
* @product highstock
|
||||
* @apioption plotOptions.candlestick.dataGrouping.approximation
|
||||
*/
|
||||
states: {
|
||||
/**
|
||||
* @extends plotOptions.column.states.hover
|
||||
* @product highstock
|
||||
*/
|
||||
hover: {
|
||||
/**
|
||||
* The pixel width of the line/border around the candlestick.
|
||||
*
|
||||
* @product highstock
|
||||
*/
|
||||
lineWidth: 2
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @extends plotOptions.ohlc.tooltip
|
||||
*/
|
||||
tooltip: defaultOptions.plotOptions.ohlc.tooltip,
|
||||
/**
|
||||
* @type {number|null}
|
||||
* @product highstock
|
||||
*/
|
||||
threshold: null,
|
||||
/**
|
||||
* The color of the line/border of the candlestick.
|
||||
*
|
||||
* In styled mode, the line stroke can be set with the
|
||||
* `.highcharts-candlestick-series .highcahrts-point` rule.
|
||||
*
|
||||
* @see [upLineColor](#plotOptions.candlestick.upLineColor)
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/candlestick-linecolor/
|
||||
* Candlestick line colors
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #000000
|
||||
* @product highstock
|
||||
*/
|
||||
lineColor: '#000000',
|
||||
/**
|
||||
* The pixel width of the candlestick line/border. Defaults to `1`.
|
||||
*
|
||||
*
|
||||
* In styled mode, the line stroke width can be set with the
|
||||
* `.highcharts-candlestick-series .highcahrts-point` rule.
|
||||
*
|
||||
* @product highstock
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* The fill color of the candlestick when values are rising.
|
||||
*
|
||||
* In styled mode, the up color can be set with the
|
||||
* `.highcharts-candlestick-series .highcharts-point-up` rule.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/candlestick-color/
|
||||
* Custom colors
|
||||
* @sample {highstock} highcharts/css/candlestick/
|
||||
* Colors in styled mode
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #ffffff
|
||||
* @product highstock
|
||||
*/
|
||||
upColor: '#ffffff',
|
||||
/**
|
||||
* @product highstock
|
||||
*/
|
||||
stickyTracking: true
|
||||
};
|
||||
/**
|
||||
* The candlestick series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.candlestick
|
||||
*
|
||||
* @augments Highcharts.seriesTypes.ohlc
|
||||
*/
|
||||
seriesType('candlestick', 'ohlc', merge(defaultOptions.plotOptions.column, candlestickOptions),
|
||||
/**
|
||||
* @lends seriesTypes.candlestick
|
||||
*/
|
||||
{
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Postprocess mapping between options and SVG attributes
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.candlestick#pointAttribs
|
||||
* @param {Highcharts.Point} point
|
||||
* @param {string} [state]
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
*/
|
||||
pointAttribs: function (point, state) {
|
||||
var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options, isUp = point.open < point.close, stroke = options.lineColor || this.color, stateOptions;
|
||||
attribs['stroke-width'] = options.lineWidth;
|
||||
attribs.fill = point.options.color ||
|
||||
(isUp ? (options.upColor || this.color) : this.color);
|
||||
attribs.stroke = point.options.lineColor ||
|
||||
(isUp ? (options.upLineColor || stroke) : stroke);
|
||||
// Select or hover states
|
||||
if (state) {
|
||||
stateOptions = options.states[state];
|
||||
attribs.fill = stateOptions.color || attribs.fill;
|
||||
attribs.stroke = stateOptions.lineColor || attribs.stroke;
|
||||
attribs['stroke-width'] =
|
||||
stateOptions.lineWidth || attribs['stroke-width'];
|
||||
}
|
||||
return attribs;
|
||||
},
|
||||
/**
|
||||
* Draw the data points.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.candlestick#drawPoints
|
||||
* @return {void}
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, points = series.points, chart = series.chart, reversedYAxis = series.yAxis.reversed;
|
||||
points.forEach(function (point) {
|
||||
var graphic = point.graphic, plotOpen, plotClose, topBox, bottomBox, hasTopWhisker, hasBottomWhisker, crispCorr, crispX, path, halfWidth, isNew = !graphic;
|
||||
if (typeof point.plotY !== 'undefined') {
|
||||
if (!graphic) {
|
||||
point.graphic = graphic = chart.renderer.path()
|
||||
.add(series.group);
|
||||
}
|
||||
if (!series.chart.styledMode) {
|
||||
graphic
|
||||
.attr(series.pointAttribs(point, (point.selected && 'select'))) // #3897
|
||||
.shadow(series.options.shadow);
|
||||
}
|
||||
// Crisp vector coordinates
|
||||
crispCorr = (graphic.strokeWidth() % 2) / 2;
|
||||
// #2596:
|
||||
crispX = Math.round(point.plotX) - crispCorr;
|
||||
plotOpen = point.plotOpen;
|
||||
plotClose = point.plotClose;
|
||||
topBox = Math.min(plotOpen, plotClose);
|
||||
bottomBox = Math.max(plotOpen, plotClose);
|
||||
halfWidth = Math.round(point.shapeArgs.width / 2);
|
||||
hasTopWhisker = reversedYAxis ?
|
||||
bottomBox !== point.yBottom :
|
||||
Math.round(topBox) !==
|
||||
Math.round(point.plotHigh);
|
||||
hasBottomWhisker = reversedYAxis ?
|
||||
Math.round(topBox) !==
|
||||
Math.round(point.plotHigh) :
|
||||
bottomBox !== point.yBottom;
|
||||
topBox = Math.round(topBox) + crispCorr;
|
||||
bottomBox = Math.round(bottomBox) + crispCorr;
|
||||
// Create the path. Due to a bug in Chrome 49, the path is
|
||||
// first instanciated with no values, then the values
|
||||
// pushed. For unknown reasons, instanciating the path array
|
||||
// with all the values would lead to a crash when updating
|
||||
// frequently (#5193).
|
||||
path = [];
|
||||
path.push(['M', crispX - halfWidth, bottomBox], ['L', crispX - halfWidth, topBox], ['L', crispX + halfWidth, topBox], ['L', crispX + halfWidth, bottomBox], ['Z'], // Ensure a nice rectangle #2602
|
||||
['M', crispX, topBox], [
|
||||
'L',
|
||||
// #460, #2094
|
||||
crispX,
|
||||
hasTopWhisker ?
|
||||
Math.round(reversedYAxis ?
|
||||
point.yBottom :
|
||||
point.plotHigh) :
|
||||
topBox
|
||||
], ['M', crispX, bottomBox], [
|
||||
'L',
|
||||
// #460, #2094
|
||||
crispX,
|
||||
hasBottomWhisker ?
|
||||
Math.round(reversedYAxis ?
|
||||
point.plotHigh :
|
||||
point.yBottom) :
|
||||
bottomBox
|
||||
]);
|
||||
graphic[isNew ? 'attr' : 'animate']({ d: path })
|
||||
.addClass(point.getClassName(), true);
|
||||
}
|
||||
});
|
||||
/* eslint-enable valid-jsdoc */
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `candlestick` series. If the [type](#series.candlestick.type)
|
||||
* option is not specified, it is inherited from [chart.type](
|
||||
* #chart.type).
|
||||
*
|
||||
* @type {*}
|
||||
* @extends series,plotOptions.candlestick
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highstock
|
||||
* @apioption series.candlestick
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `candlestick` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 5 or 4 values. In this case, the values correspond
|
||||
* to `x,open,high,low,close`. If the first value is a string, it is applied
|
||||
* as the name of the point, and the `x` value is inferred. The `x` value can
|
||||
* also be omitted, in which case the inner arrays should be of length 4.
|
||||
* Then the `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 7, 2, 0, 4],
|
||||
* [1, 1, 4, 2, 8],
|
||||
* [2, 3, 3, 9, 3]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.candlestick.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* open: 9,
|
||||
* high: 2,
|
||||
* low: 4,
|
||||
* close: 6,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* open: 1,
|
||||
* high: 4,
|
||||
* low: 7,
|
||||
* close: 7,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
|
||||
* @extends series.ohlc.data
|
||||
* @excluding y
|
||||
* @product highstock
|
||||
* @apioption series.candlestick.data
|
||||
*/
|
||||
''; // adds doclets above to transpilat
|
||||
@@ -0,0 +1,435 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Math3D from '../Extensions/Math3D.js';
|
||||
var perspective = Math3D.perspective;
|
||||
import StackItem from '../Extensions/Stacking.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, pick = U.pick, wrap = U.wrap;
|
||||
import '../Core/Series/Series.js';
|
||||
var Series = H.Series, seriesTypes = H.seriesTypes, svg = H.svg;
|
||||
/**
|
||||
* Depth of the columns in a 3D column chart.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 25
|
||||
* @since 4.0
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @apioption plotOptions.column.depth
|
||||
*/
|
||||
/**
|
||||
* 3D columns only. The color of the edges. Similar to `borderColor`, except it
|
||||
* defaults to the same color as the column.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @apioption plotOptions.column.edgeColor
|
||||
*/
|
||||
/**
|
||||
* 3D columns only. The width of the colored edges.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @apioption plotOptions.column.edgeWidth
|
||||
*/
|
||||
/**
|
||||
* The spacing between columns on the Z Axis in a 3D chart.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @since 4.0
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @apioption plotOptions.column.groupZPadding
|
||||
*/
|
||||
/* eslint-disable no-invalid-this */
|
||||
/**
|
||||
* @private
|
||||
* @param {Highcharts.Chart} chart
|
||||
* Chart with stacks
|
||||
* @param {string} stacking
|
||||
* Stacking option
|
||||
* @return {Highcharts.Stack3dDictionary}
|
||||
*/
|
||||
function retrieveStacks(chart, stacking) {
|
||||
var series = chart.series, stacks = {};
|
||||
var stackNumber, i = 1;
|
||||
series.forEach(function (s) {
|
||||
stackNumber = pick(s.options.stack, (stacking ? 0 : series.length - 1 - s.index)); // #3841, #4532
|
||||
if (!stacks[stackNumber]) {
|
||||
stacks[stackNumber] = { series: [s], position: i };
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
stacks[stackNumber].series.push(s);
|
||||
}
|
||||
});
|
||||
stacks.totalStacks = i + 1;
|
||||
return stacks;
|
||||
}
|
||||
wrap(seriesTypes.column.prototype, 'translate', function (proceed) {
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
// Do not do this if the chart is not 3D
|
||||
if (this.chart.is3d()) {
|
||||
this.translate3dShapes();
|
||||
}
|
||||
});
|
||||
// Don't use justifyDataLabel when point is outsidePlot
|
||||
wrap(Series.prototype, 'justifyDataLabel', function (proceed) {
|
||||
return !(arguments[2].outside3dPlot) ?
|
||||
proceed.apply(this, [].slice.call(arguments, 1)) :
|
||||
false;
|
||||
});
|
||||
seriesTypes.column.prototype.translate3dPoints = function () { };
|
||||
seriesTypes.column.prototype.translate3dShapes = function () {
|
||||
var series = this, chart = series.chart, seriesOptions = series.options, depth = seriesOptions.depth, stack = seriesOptions.stacking ?
|
||||
(seriesOptions.stack || 0) :
|
||||
series.index, // #4743
|
||||
z = stack * (depth + (seriesOptions.groupZPadding || 1)), borderCrisp = series.borderWidth % 2 ? 0.5 : 0, point2dPos; // Position of point in 2D, used for 3D position calculation.
|
||||
if (chart.inverted && !series.yAxis.reversed) {
|
||||
borderCrisp *= -1;
|
||||
}
|
||||
if (seriesOptions.grouping !== false) {
|
||||
z = 0;
|
||||
}
|
||||
z += (seriesOptions.groupZPadding || 1);
|
||||
series.data.forEach(function (point) {
|
||||
// #7103 Reset outside3dPlot flag
|
||||
point.outside3dPlot = null;
|
||||
if (point.y !== null) {
|
||||
var shapeArgs = point.shapeArgs, tooltipPos = point.tooltipPos,
|
||||
// Array for final shapeArgs calculation.
|
||||
// We are checking two dimensions (x and y).
|
||||
dimensions = [['x', 'width'], ['y', 'height']], borderlessBase; // Crisped rects can have +/- 0.5 pixels offset.
|
||||
// #3131 We need to check if column is inside plotArea.
|
||||
dimensions.forEach(function (d) {
|
||||
borderlessBase = shapeArgs[d[0]] - borderCrisp;
|
||||
if (borderlessBase < 0) {
|
||||
// If borderLessBase is smaller than 0, it is needed to set
|
||||
// its value to 0 or 0.5 depending on borderWidth
|
||||
// borderWidth may be even or odd.
|
||||
shapeArgs[d[1]] +=
|
||||
shapeArgs[d[0]] + borderCrisp;
|
||||
shapeArgs[d[0]] = -borderCrisp;
|
||||
borderlessBase = 0;
|
||||
}
|
||||
if ((borderlessBase + shapeArgs[d[1]] >
|
||||
series[d[0] + 'Axis'].len) &&
|
||||
// Do not change height/width of column if 0 (#6708)
|
||||
shapeArgs[d[1]] !== 0) {
|
||||
shapeArgs[d[1]] =
|
||||
series[d[0] + 'Axis'].len -
|
||||
shapeArgs[d[0]];
|
||||
}
|
||||
if (
|
||||
// Do not remove columns with zero height/width.
|
||||
(shapeArgs[d[1]] !== 0) &&
|
||||
(shapeArgs[d[0]] >=
|
||||
series[d[0] + 'Axis'].len ||
|
||||
shapeArgs[d[0]] + shapeArgs[d[1]] <=
|
||||
borderCrisp)) {
|
||||
// Set args to 0 if column is outside the chart.
|
||||
for (var key in shapeArgs) { // eslint-disable-line guard-for-in
|
||||
shapeArgs[key] = 0;
|
||||
}
|
||||
// #7103 outside3dPlot flag is set on Points which are
|
||||
// currently outside of plot.
|
||||
point.outside3dPlot = true;
|
||||
}
|
||||
});
|
||||
// Change from 2d to 3d
|
||||
if (point.shapeType === 'rect') {
|
||||
point.shapeType = 'cuboid';
|
||||
}
|
||||
shapeArgs.z = z;
|
||||
shapeArgs.depth = depth;
|
||||
shapeArgs.insidePlotArea = true;
|
||||
// Point's position in 2D
|
||||
point2dPos = {
|
||||
x: shapeArgs.x + shapeArgs.width / 2,
|
||||
y: shapeArgs.y,
|
||||
z: z + depth / 2 // The center of column in Z dimension
|
||||
};
|
||||
// Recalculate point positions for inverted graphs
|
||||
if (chart.inverted) {
|
||||
point2dPos.x = shapeArgs.height;
|
||||
point2dPos.y = point.clientX;
|
||||
}
|
||||
// Calculate and store point's position in 3D,
|
||||
// using perspective method.
|
||||
point.plot3d = perspective([point2dPos], chart, true, false)[0];
|
||||
// Translate the tooltip position in 3d space
|
||||
tooltipPos = perspective([{
|
||||
x: tooltipPos[0],
|
||||
y: tooltipPos[1],
|
||||
z: z + depth / 2 // The center of column in Z dimension
|
||||
}], chart, true, false)[0];
|
||||
point.tooltipPos = [tooltipPos.x, tooltipPos.y];
|
||||
}
|
||||
});
|
||||
// store for later use #4067
|
||||
series.z = z;
|
||||
};
|
||||
wrap(seriesTypes.column.prototype, 'animate', function (proceed) {
|
||||
if (!this.chart.is3d()) {
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
}
|
||||
else {
|
||||
var args = arguments, init = args[1], yAxis = this.yAxis, series = this, reversed = this.yAxis.reversed;
|
||||
if (svg) { // VML is too slow anyway
|
||||
if (init) {
|
||||
series.data.forEach(function (point) {
|
||||
if (point.y !== null) {
|
||||
point.height = point.shapeArgs.height;
|
||||
point.shapey = point.shapeArgs.y; // #2968
|
||||
point.shapeArgs.height = 1;
|
||||
if (!reversed) {
|
||||
if (point.stackY) {
|
||||
point.shapeArgs.y =
|
||||
point.plotY +
|
||||
yAxis.translate(point.stackY);
|
||||
}
|
||||
else {
|
||||
point.shapeArgs.y =
|
||||
point.plotY +
|
||||
(point.negative ?
|
||||
-point.height :
|
||||
point.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else { // run the animation
|
||||
series.data.forEach(function (point) {
|
||||
if (point.y !== null) {
|
||||
point.shapeArgs.height = point.height;
|
||||
point.shapeArgs.y = point.shapey; // #2968
|
||||
// null value do not have a graphic
|
||||
if (point.graphic) {
|
||||
point.graphic.animate(point.shapeArgs, series.options.animation);
|
||||
}
|
||||
}
|
||||
});
|
||||
// redraw datalabels to the correct position
|
||||
this.drawDataLabels();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
// In case of 3d columns there is no sense to add this columns to a specific
|
||||
// series group - if series is added to a group all columns will have the same
|
||||
// zIndex in comparison with different series.
|
||||
wrap(seriesTypes.column.prototype, 'plotGroup', function (proceed, prop, name, visibility, zIndex, parent) {
|
||||
if (prop !== 'dataLabelsGroup') {
|
||||
if (this.chart.is3d()) {
|
||||
if (this[prop]) {
|
||||
delete this[prop];
|
||||
}
|
||||
if (parent) {
|
||||
if (!this.chart.columnGroup) {
|
||||
this.chart.columnGroup =
|
||||
this.chart.renderer.g('columnGroup').add(parent);
|
||||
}
|
||||
this[prop] = this.chart.columnGroup;
|
||||
this.chart.columnGroup.attr(this.getPlotBox());
|
||||
this[prop].survive = true;
|
||||
if (prop === 'group' || prop === 'markerGroup') {
|
||||
arguments[3] = 'visible';
|
||||
// For 3D column group and markerGroup should be visible
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
});
|
||||
// When series is not added to group it is needed to change setVisible method to
|
||||
// allow correct Legend funcionality. This wrap is basing on pie chart series.
|
||||
wrap(seriesTypes.column.prototype, 'setVisible', function (proceed, vis) {
|
||||
var series = this, pointVis;
|
||||
if (series.chart.is3d()) {
|
||||
series.data.forEach(function (point) {
|
||||
point.visible = point.options.visible = vis =
|
||||
typeof vis === 'undefined' ?
|
||||
!pick(series.visible, point.visible) : vis;
|
||||
pointVis = vis ? 'visible' : 'hidden';
|
||||
series.options.data[series.data.indexOf(point)] =
|
||||
point.options;
|
||||
if (point.graphic) {
|
||||
point.graphic.attr({
|
||||
visibility: pointVis
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
});
|
||||
seriesTypes.column.prototype
|
||||
.handle3dGrouping = true;
|
||||
addEvent(Series, 'afterInit', function () {
|
||||
if (this.chart.is3d() &&
|
||||
this.handle3dGrouping) {
|
||||
var series = this, seriesOptions = this.options, grouping = seriesOptions.grouping, stacking = seriesOptions.stacking, reversedStacks = pick(this.yAxis.options.reversedStacks, true), z = 0;
|
||||
// @todo grouping === true ?
|
||||
if (!(typeof grouping !== 'undefined' && !grouping)) {
|
||||
var stacks = retrieveStacks(this.chart, stacking), stack = seriesOptions.stack || 0, i; // position within the stack
|
||||
for (i = 0; i < stacks[stack].series.length; i++) {
|
||||
if (stacks[stack].series[i] === this) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
z = (10 * (stacks.totalStacks - stacks[stack].position)) +
|
||||
(reversedStacks ? i : -i); // #4369
|
||||
// In case when axis is reversed, columns are also reversed inside
|
||||
// the group (#3737)
|
||||
if (!this.xAxis.reversed) {
|
||||
z = (stacks.totalStacks * 10) - z;
|
||||
}
|
||||
}
|
||||
seriesOptions.depth = seriesOptions.depth || 25;
|
||||
series.z = series.z || 0;
|
||||
seriesOptions.zIndex = z;
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function pointAttribs(proceed) {
|
||||
var attr = proceed.apply(this, [].slice.call(arguments, 1));
|
||||
if (this.chart.is3d && this.chart.is3d()) {
|
||||
// Set the fill color to the fill color to provide a smooth edge
|
||||
attr.stroke = this.options.edgeColor || attr.fill;
|
||||
attr['stroke-width'] = pick(this.options.edgeWidth, 1); // #4055
|
||||
}
|
||||
return attr;
|
||||
}
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* In 3D mode, all column-series are rendered in one main group. Because of that
|
||||
* we need to apply inactive state on all points.
|
||||
* @private
|
||||
*/
|
||||
function setState(proceed, state, inherit) {
|
||||
var is3d = this.chart.is3d && this.chart.is3d();
|
||||
if (is3d) {
|
||||
this.options.inactiveOtherPoints = true;
|
||||
}
|
||||
proceed.call(this, state, inherit);
|
||||
if (is3d) {
|
||||
this.options.inactiveOtherPoints = false;
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* In 3D mode, simple checking for a new shape to animate is not enough.
|
||||
* Additionally check if graphic is a group of elements
|
||||
* @private
|
||||
*/
|
||||
function hasNewShapeType(proceed) {
|
||||
var args = [];
|
||||
for (var _i = 1; _i < arguments.length; _i++) {
|
||||
args[_i - 1] = arguments[_i];
|
||||
}
|
||||
return this.series.chart.is3d() ?
|
||||
this.graphic && this.graphic.element.nodeName !== 'g' :
|
||||
proceed.apply(this, args);
|
||||
}
|
||||
wrap(seriesTypes.column.prototype, 'pointAttribs', pointAttribs);
|
||||
wrap(seriesTypes.column.prototype, 'setState', setState);
|
||||
wrap(seriesTypes.column.prototype.pointClass.prototype, 'hasNewShapeType', hasNewShapeType);
|
||||
if (seriesTypes.columnrange) {
|
||||
wrap(seriesTypes.columnrange.prototype, 'pointAttribs', pointAttribs);
|
||||
wrap(seriesTypes.columnrange.prototype, 'setState', setState);
|
||||
wrap(seriesTypes.columnrange.prototype.pointClass.prototype, 'hasNewShapeType', hasNewShapeType);
|
||||
seriesTypes.columnrange.prototype.plotGroup =
|
||||
seriesTypes.column.prototype.plotGroup;
|
||||
seriesTypes.columnrange.prototype.setVisible =
|
||||
seriesTypes.column.prototype.setVisible;
|
||||
}
|
||||
wrap(Series.prototype, 'alignDataLabel', function (proceed, point, dataLabel, options, alignTo) {
|
||||
var chart = this.chart;
|
||||
// In 3D we need to pass point.outsidePlot option to the justifyDataLabel
|
||||
// method for disabling justifying dataLabels in columns outside plot
|
||||
options.outside3dPlot = point.outside3dPlot;
|
||||
// Only do this for 3D columns and it's derived series
|
||||
if (chart.is3d() &&
|
||||
this.is('column')) {
|
||||
var series = this, seriesOptions = series.options, inside = pick(options.inside, !!series.options.stacking), options3d = chart.options.chart.options3d, xOffset = point.pointWidth / 2 || 0;
|
||||
var dLPosition = {
|
||||
x: alignTo.x + xOffset,
|
||||
y: alignTo.y,
|
||||
z: series.z + seriesOptions.depth / 2
|
||||
};
|
||||
if (chart.inverted) {
|
||||
// Inside dataLabels are positioned according to above
|
||||
// logic and there is no need to position them using
|
||||
// non-3D algorighm (that use alignTo.width)
|
||||
if (inside) {
|
||||
alignTo.width = 0;
|
||||
dLPosition.x += point.shapeArgs.height / 2;
|
||||
}
|
||||
// When chart is upside down
|
||||
// (alpha angle between 180 and 360 degrees)
|
||||
// it is needed to add column width to calculated value.
|
||||
if (options3d.alpha >= 90 && options3d.alpha <= 270) {
|
||||
dLPosition.y += point.shapeArgs.width;
|
||||
}
|
||||
}
|
||||
// dLPosition is recalculated for 3D graphs
|
||||
dLPosition = perspective([dLPosition], chart, true, false)[0];
|
||||
alignTo.x = dLPosition.x - xOffset;
|
||||
// #7103 If point is outside of plotArea, hide data label.
|
||||
alignTo.y = point.outside3dPlot ? -9e9 : dLPosition.y;
|
||||
}
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
});
|
||||
// Added stackLabels position calculation for 3D charts.
|
||||
wrap(StackItem.prototype, 'getStackBox', function (proceed, chart, stackItem, x, y, xWidth, h, axis) {
|
||||
var stackBox = proceed.apply(this, [].slice.call(arguments, 1));
|
||||
// Only do this for 3D graph
|
||||
if (chart.is3d() && stackItem.base) {
|
||||
// First element of stackItem.base is an index of base series.
|
||||
var baseSeriesInd = +(stackItem.base).split(',')[0];
|
||||
var columnSeries = chart.series[baseSeriesInd];
|
||||
var options3d = chart.options.chart.options3d;
|
||||
// Only do this if base series is a column or inherited type,
|
||||
// use its barW, z and depth parameters
|
||||
// for correct stackLabels position calculation
|
||||
if (columnSeries &&
|
||||
columnSeries instanceof seriesTypes.column) {
|
||||
var dLPosition = {
|
||||
x: stackBox.x + (chart.inverted ? h : xWidth / 2),
|
||||
y: stackBox.y,
|
||||
z: columnSeries.options.depth / 2
|
||||
};
|
||||
if (chart.inverted) {
|
||||
// Do not use default offset calculation logic
|
||||
// for 3D inverted stackLabels.
|
||||
stackBox.width = 0;
|
||||
// When chart is upside down
|
||||
// (alpha angle between 180 and 360 degrees)
|
||||
// it is needed to add column width to calculated value.
|
||||
if (options3d.alpha >= 90 && options3d.alpha <= 270) {
|
||||
dLPosition.y += xWidth;
|
||||
}
|
||||
}
|
||||
dLPosition = perspective([dLPosition], chart, true, false)[0];
|
||||
stackBox.x = dLPosition.x - xWidth / 2;
|
||||
stackBox.y = dLPosition.y;
|
||||
}
|
||||
}
|
||||
return stackBox;
|
||||
});
|
||||
@@ -0,0 +1,256 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Sebastian Bochan
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var clamp = U.clamp, pick = U.pick, seriesType = U.seriesType;
|
||||
var seriesTypes = H.seriesTypes;
|
||||
var colProto = seriesTypes.column.prototype;
|
||||
/**
|
||||
* The ColumnPyramidSeries class
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.columnpyramid
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('columnpyramid', 'column',
|
||||
/**
|
||||
* Column pyramid series display one pyramid per value along an X axis.
|
||||
* To display horizontal pyramids, set [chart.inverted](#chart.inverted) to
|
||||
* `true`.
|
||||
*
|
||||
* @sample {highcharts|highstock} highcharts/demo/column-pyramid/
|
||||
* Column pyramid
|
||||
* @sample {highcharts|highstock} highcharts/plotoptions/columnpyramid-stacked/
|
||||
* Column pyramid stacked
|
||||
* @sample {highcharts|highstock} highcharts/plotoptions/columnpyramid-inverted/
|
||||
* Column pyramid inverted
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @since 7.0.0
|
||||
* @product highcharts highstock
|
||||
* @excluding boostThreshold, borderRadius, crisp, depth, edgeColor,
|
||||
* edgeWidth, groupZPadding, negativeColor, softThreshold,
|
||||
* threshold, zoneAxis, zones, boostBlending
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.columnpyramid
|
||||
*/
|
||||
{
|
||||
// no additions
|
||||
}, {
|
||||
/* eslint-disable-next-line valid-jsdoc */
|
||||
/**
|
||||
* Overrides the column translate method
|
||||
* @private
|
||||
*/
|
||||
translate: function () {
|
||||
var series = this, chart = series.chart, options = series.options, dense = series.dense =
|
||||
series.closestPointRange * series.xAxis.transA < 2, borderWidth = series.borderWidth = pick(options.borderWidth, dense ? 0 : 1 // #3635
|
||||
), yAxis = series.yAxis, threshold = options.threshold, translatedThreshold = series.translatedThreshold =
|
||||
yAxis.getThreshold(threshold), minPointLength = pick(options.minPointLength, 5), metrics = series.getColumnMetrics(), pointWidth = metrics.width,
|
||||
// postprocessed for border width
|
||||
seriesBarW = series.barW =
|
||||
Math.max(pointWidth, 1 + 2 * borderWidth), pointXOffset = series.pointXOffset = metrics.offset;
|
||||
if (chart.inverted) {
|
||||
translatedThreshold -= 0.5; // #3355
|
||||
}
|
||||
// When the pointPadding is 0,
|
||||
// we want the pyramids to be packed tightly,
|
||||
// so we allow individual pyramids to have individual sizes.
|
||||
// When pointPadding is greater,
|
||||
// we strive for equal-width columns (#2694).
|
||||
if (options.pointPadding) {
|
||||
seriesBarW = Math.ceil(seriesBarW);
|
||||
}
|
||||
colProto.translate.apply(series);
|
||||
// Record the new values
|
||||
series.points.forEach(function (point) {
|
||||
var yBottom = pick(point.yBottom, translatedThreshold), safeDistance = 999 + Math.abs(yBottom), plotY = clamp(point.plotY, -safeDistance, yAxis.len + safeDistance),
|
||||
// Don't draw too far outside plot area
|
||||
// (#1303, #2241, #4264)
|
||||
barX = point.plotX + pointXOffset, barW = seriesBarW / 2, barY = Math.min(plotY, yBottom), barH = Math.max(plotY, yBottom) - barY, stackTotal, stackHeight, topPointY, topXwidth, bottomXwidth, invBarPos, x1, x2, x3, x4, y1, y2;
|
||||
point.barX = barX;
|
||||
point.pointWidth = pointWidth;
|
||||
// Fix the tooltip on center of grouped pyramids
|
||||
// (#1216, #424, #3648)
|
||||
point.tooltipPos = chart.inverted ?
|
||||
[
|
||||
yAxis.len + yAxis.pos - chart.plotLeft - plotY,
|
||||
series.xAxis.len - barX - barW,
|
||||
barH
|
||||
] :
|
||||
[
|
||||
barX + barW,
|
||||
plotY + yAxis.pos - chart.plotTop,
|
||||
barH
|
||||
];
|
||||
stackTotal =
|
||||
threshold + (point.total || point.y);
|
||||
// overwrite stacktotal (always 100 / -100)
|
||||
if (options.stacking === 'percent') {
|
||||
stackTotal =
|
||||
threshold + (point.y < 0) ?
|
||||
-100 :
|
||||
100;
|
||||
}
|
||||
// get the highest point (if stack, extract from total)
|
||||
topPointY = yAxis.toPixels((stackTotal), true);
|
||||
// calculate height of stack (in pixels)
|
||||
stackHeight =
|
||||
chart.plotHeight - topPointY -
|
||||
(chart.plotHeight - translatedThreshold);
|
||||
// topXwidth and bottomXwidth = width of lines from the center
|
||||
// calculated from tanges proportion.
|
||||
// Can not be a NaN #12514
|
||||
topXwidth = stackHeight ? (barW * (barY - topPointY)) / stackHeight : 0;
|
||||
// like topXwidth, but with height of point
|
||||
bottomXwidth = stackHeight ? (barW * (barY + barH - topPointY)) / stackHeight : 0;
|
||||
/*
|
||||
/\
|
||||
/ \
|
||||
x1,y1,------ x2,y1
|
||||
/ \
|
||||
----------
|
||||
x4,y2 x3,y2
|
||||
*/
|
||||
x1 = barX - topXwidth + barW;
|
||||
x2 = barX + topXwidth + barW;
|
||||
x3 = barX + bottomXwidth + barW;
|
||||
x4 = barX - bottomXwidth + barW;
|
||||
y1 = barY - minPointLength;
|
||||
y2 = barY + barH;
|
||||
if (point.y < 0) {
|
||||
y1 = barY;
|
||||
y2 = barY + barH + minPointLength;
|
||||
}
|
||||
// inverted chart
|
||||
if (chart.inverted) {
|
||||
invBarPos = chart.plotWidth - barY;
|
||||
stackHeight = (topPointY -
|
||||
(chart.plotWidth - translatedThreshold));
|
||||
// proportion tanges
|
||||
topXwidth = (barW *
|
||||
(topPointY - invBarPos)) / stackHeight;
|
||||
bottomXwidth = (barW *
|
||||
(topPointY - (invBarPos - barH))) / stackHeight;
|
||||
x1 = barX + barW + topXwidth; // top bottom
|
||||
x2 = x1 - 2 * topXwidth; // top top
|
||||
x3 = barX - bottomXwidth + barW; // bottom top
|
||||
x4 = barX + bottomXwidth + barW; // bottom bottom
|
||||
y1 = barY;
|
||||
y2 = barY + barH - minPointLength;
|
||||
if (point.y < 0) {
|
||||
y2 = barY + barH + minPointLength;
|
||||
}
|
||||
}
|
||||
// Register shape type and arguments to be used in drawPoints
|
||||
point.shapeType = 'path';
|
||||
point.shapeArgs = {
|
||||
// args for datalabels positioning
|
||||
x: x1,
|
||||
y: y1,
|
||||
width: x2 - x1,
|
||||
height: barH,
|
||||
// path of pyramid
|
||||
d: [
|
||||
['M', x1, y1],
|
||||
['L', x2, y1],
|
||||
['L', x3, y2],
|
||||
['L', x4, y2],
|
||||
['Z']
|
||||
]
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `columnpyramid` series. If the [type](#series.columnpyramid.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.columnpyramid
|
||||
* @excluding connectEnds, connectNulls, dashStyle, dataParser, dataURL,
|
||||
* gapSize, gapUnit, linecap, lineWidth, marker, step,
|
||||
* boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @apioption series.columnpyramid
|
||||
*/
|
||||
/**
|
||||
* @excluding halo, lineWidth, lineWidthPlus, marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.columnpyramid.states.hover
|
||||
*/
|
||||
/**
|
||||
* @excluding halo, lineWidth, lineWidthPlus, marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.columnpyramid.states.select
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `columnpyramid` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 6],
|
||||
* [1, 2],
|
||||
* [2, 6]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. An array of objects with named values. The objects are point configuration
|
||||
* objects as seen below. If the total number of data points exceeds the
|
||||
* series' [turboThreshold](#series.columnpyramid.turboThreshold), this
|
||||
* option is not available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 9,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 6,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @excluding marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.columnpyramid.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file;
|
||||
@@ -0,0 +1,241 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import O from '../Core/Options.js';
|
||||
var defaultOptions = O.defaultOptions;
|
||||
import U from '../Core/Utilities.js';
|
||||
var clamp = U.clamp, merge = U.merge, pick = U.pick, seriesType = U.seriesType;
|
||||
var noop = H.noop, seriesTypes = H.seriesTypes;
|
||||
var colProto = seriesTypes.column.prototype;
|
||||
/**
|
||||
* The column range is a cartesian series type with higher and lower
|
||||
* Y values along an X axis. To display horizontal bars, set
|
||||
* [chart.inverted](#chart.inverted) to `true`.
|
||||
*
|
||||
* @sample {highcharts|highstock} highcharts/demo/columnrange/
|
||||
* Inverted column range
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @since 2.3.0
|
||||
* @excluding negativeColor, stacking, softThreshold, threshold
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.columnrange
|
||||
*/
|
||||
var columnRangeOptions = {
|
||||
/**
|
||||
* Extended data labels for range series types. Range series data labels
|
||||
* have no `x` and `y` options. Instead, they have `xLow`, `xHigh`,
|
||||
* `yLow` and `yHigh` options to allow the higher and lower data label
|
||||
* sets individually.
|
||||
*
|
||||
* @declare Highcharts.SeriesAreaRangeDataLabelsOptionsObject
|
||||
* @extends plotOptions.arearange.dataLabels
|
||||
* @since 2.3.0
|
||||
* @product highcharts highstock
|
||||
* @apioption plotOptions.columnrange.dataLabels
|
||||
*/
|
||||
pointRange: null,
|
||||
/** @ignore-option */
|
||||
marker: null,
|
||||
states: {
|
||||
hover: {
|
||||
/** @ignore-option */
|
||||
halo: false
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* The ColumnRangeSeries class
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.columnrange
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('columnrange', 'arearange', merge(defaultOptions.plotOptions.column, defaultOptions.plotOptions.arearange, columnRangeOptions), {
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Translate data points from raw values x and y to plotX and plotY
|
||||
* @private
|
||||
*/
|
||||
translate: function () {
|
||||
var series = this, yAxis = series.yAxis, xAxis = series.xAxis, startAngleRad = xAxis.startAngleRad, start, chart = series.chart, isRadial = series.xAxis.isRadial, safeDistance = Math.max(chart.chartWidth, chart.chartHeight) + 999, plotHigh;
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Don't draw too far outside plot area (#6835)
|
||||
* @private
|
||||
*/
|
||||
function safeBounds(pixelPos) {
|
||||
return clamp(pixelPos, -safeDistance, safeDistance);
|
||||
}
|
||||
colProto.translate.apply(series);
|
||||
// Set plotLow and plotHigh
|
||||
series.points.forEach(function (point) {
|
||||
var shapeArgs = point.shapeArgs, minPointLength = series.options.minPointLength, heightDifference, height, y;
|
||||
point.plotHigh = plotHigh = safeBounds(yAxis.translate(point.high, 0, 1, 0, 1));
|
||||
point.plotLow = safeBounds(point.plotY);
|
||||
// adjust shape
|
||||
y = plotHigh;
|
||||
height = pick(point.rectPlotY, point.plotY) - plotHigh;
|
||||
// Adjust for minPointLength
|
||||
if (Math.abs(height) < minPointLength) {
|
||||
heightDifference = (minPointLength - height);
|
||||
height += heightDifference;
|
||||
y -= heightDifference / 2;
|
||||
// Adjust for negative ranges or reversed Y axis (#1457)
|
||||
}
|
||||
else if (height < 0) {
|
||||
height *= -1;
|
||||
y -= height;
|
||||
}
|
||||
if (isRadial) {
|
||||
start = point.barX + startAngleRad;
|
||||
point.shapeType = 'arc';
|
||||
point.shapeArgs = series.polarArc(y + height, y, start, start + point.pointWidth);
|
||||
}
|
||||
else {
|
||||
shapeArgs.height = height;
|
||||
shapeArgs.y = y;
|
||||
point.tooltipPos = chart.inverted ?
|
||||
[
|
||||
yAxis.len + yAxis.pos - chart.plotLeft - y -
|
||||
height / 2,
|
||||
xAxis.len + xAxis.pos - chart.plotTop -
|
||||
shapeArgs.x - shapeArgs.width / 2,
|
||||
height
|
||||
] : [
|
||||
xAxis.left - chart.plotLeft + shapeArgs.x +
|
||||
shapeArgs.width / 2,
|
||||
yAxis.pos - chart.plotTop + y + height / 2,
|
||||
height
|
||||
]; // don't inherit from column tooltip position - #3372
|
||||
}
|
||||
});
|
||||
},
|
||||
directTouch: true,
|
||||
trackerGroups: ['group', 'dataLabelsGroup'],
|
||||
drawGraph: noop,
|
||||
getSymbol: noop,
|
||||
// Overrides from modules that may be loaded after this module
|
||||
crispCol: function () {
|
||||
return colProto.crispCol.apply(this, arguments);
|
||||
},
|
||||
drawPoints: function () {
|
||||
return colProto.drawPoints.apply(this, arguments);
|
||||
},
|
||||
drawTracker: function () {
|
||||
return colProto.drawTracker.apply(this, arguments);
|
||||
},
|
||||
getColumnMetrics: function () {
|
||||
return colProto.getColumnMetrics.apply(this, arguments);
|
||||
},
|
||||
pointAttribs: function () {
|
||||
return colProto.pointAttribs.apply(this, arguments);
|
||||
},
|
||||
animate: function () {
|
||||
return colProto.animate.apply(this, arguments);
|
||||
},
|
||||
polarArc: function () {
|
||||
return colProto.polarArc.apply(this, arguments);
|
||||
},
|
||||
translate3dPoints: function () {
|
||||
return colProto.translate3dPoints.apply(this, arguments);
|
||||
},
|
||||
translate3dShapes: function () {
|
||||
return colProto.translate3dShapes.apply(this, arguments);
|
||||
}
|
||||
}, {
|
||||
setState: colProto.pointClass.prototype.setState
|
||||
});
|
||||
/**
|
||||
* A `columnrange` series. If the [type](#series.columnrange.type)
|
||||
* option is not specified, it is inherited from
|
||||
* [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.columnrange
|
||||
* @excluding dataParser, dataURL, stack, stacking
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @apioption series.columnrange
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `columnrange` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,low,high`. If the first value is a string, it is applied as the name
|
||||
* of the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2\. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 4, 2],
|
||||
* [1, 2, 1],
|
||||
* [2, 9, 10]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.columnrange.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* low: 0,
|
||||
* high: 4,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* low: 5,
|
||||
* high: 3,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.arearange.data
|
||||
* @excluding marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.columnrange.data
|
||||
*/
|
||||
/**
|
||||
* @extends series.columnrange.dataLabels
|
||||
* @product highcharts highstock
|
||||
* @apioption series.columnrange.data.dataLabels
|
||||
*/
|
||||
/**
|
||||
* @excluding halo, lineWidth, lineWidthPlus, marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.columnrange.states.hover
|
||||
*/
|
||||
/**
|
||||
* @excluding halo, lineWidth, lineWidthPlus, marker
|
||||
* @product highcharts highstock
|
||||
* @apioption series.columnrange.states.select
|
||||
*/
|
||||
''; // adds doclets above into transpiled
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
/* *
|
||||
*
|
||||
* Highcharts cylinder - a 3D series
|
||||
*
|
||||
* (c) 2010-2020 Highsoft AS
|
||||
*
|
||||
* Author: Kacper Madej
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Color from '../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import Math3D from '../Extensions/Math3D.js';
|
||||
var perspective = Math3D.perspective;
|
||||
import U from '../Core/Utilities.js';
|
||||
var merge = U.merge, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Series/ColumnSeries.js';
|
||||
import '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
var charts = H.charts, deg2rad = H.deg2rad,
|
||||
// Work on H.Renderer instead of SVGRenderer for VML support.
|
||||
RendererProto = H.Renderer.prototype, cuboidPath = RendererProto.cuboidPath, cylinderMethods;
|
||||
// Check if a path is simplified. The simplified path contains only lineTo
|
||||
// segments, whereas non-simplified contain curves.
|
||||
var isSimplified = function (path) {
|
||||
return !path.some(function (seg) { return seg[0] === 'C'; });
|
||||
};
|
||||
/**
|
||||
* The cylinder series type.
|
||||
*
|
||||
* @requires module:highcharts-3d
|
||||
* @requires module:modules/cylinder
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.cylinder
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('cylinder', 'column',
|
||||
/**
|
||||
* A cylinder graph is a variation of a 3d column graph. The cylinder graph
|
||||
* features cylindrical points.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/cylinder/
|
||||
* Cylinder graph
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @since 7.0.0
|
||||
* @product highcharts
|
||||
* @excluding allAreas, boostThreshold, colorAxis, compare, compareBase,
|
||||
* dragDrop, boostBlending
|
||||
* @requires modules/cylinder
|
||||
* @optionparent plotOptions.cylinder
|
||||
*/
|
||||
{}, {},
|
||||
/** @lends Highcharts.seriesTypes.cylinder#pointClass# */
|
||||
{
|
||||
shapeType: 'cylinder',
|
||||
hasNewShapeType: H
|
||||
.seriesTypes.column.prototype
|
||||
.pointClass.prototype
|
||||
.hasNewShapeType
|
||||
});
|
||||
/**
|
||||
* A `cylinder` series. If the [type](#series.cylinder.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.cylinder
|
||||
* @since 7.0.0
|
||||
* @product highcharts
|
||||
* @excluding allAreas, boostThreshold, colorAxis, compare, compareBase,
|
||||
* boostBlending
|
||||
* @requires modules/cylinder
|
||||
* @apioption series.cylinder
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `cylinder` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 0],
|
||||
* [1, 8],
|
||||
* [2, 9]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.cylinder.turboThreshold), this option is not
|
||||
* available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 2,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.column.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.cylinder.data
|
||||
*/
|
||||
// cylinder extends cuboid
|
||||
cylinderMethods = merge(RendererProto.elements3d.cuboid, {
|
||||
parts: ['top', 'bottom', 'front', 'back'],
|
||||
pathType: 'cylinder',
|
||||
fillSetter: function (fill) {
|
||||
this.singleSetterForParts('fill', null, {
|
||||
front: fill,
|
||||
back: fill,
|
||||
top: color(fill).brighten(0.1).get(),
|
||||
bottom: color(fill).brighten(-0.1).get()
|
||||
});
|
||||
// fill for animation getter (#6776)
|
||||
this.color = this.fill = fill;
|
||||
return this;
|
||||
}
|
||||
});
|
||||
RendererProto.elements3d.cylinder = cylinderMethods;
|
||||
RendererProto.cylinder = function (shapeArgs) {
|
||||
return this.element3d('cylinder', shapeArgs);
|
||||
};
|
||||
// Generates paths and zIndexes.
|
||||
RendererProto.cylinderPath = function (shapeArgs) {
|
||||
var renderer = this, chart = charts[renderer.chartIndex],
|
||||
// decide zIndexes of parts based on cubiod logic, for consistency.
|
||||
cuboidData = cuboidPath.call(renderer, shapeArgs), isTopFirst = !cuboidData.isTop, isFronFirst = !cuboidData.isFront, top = renderer.getCylinderEnd(chart, shapeArgs), bottom = renderer.getCylinderEnd(chart, shapeArgs, true);
|
||||
return {
|
||||
front: renderer.getCylinderFront(top, bottom),
|
||||
back: renderer.getCylinderBack(top, bottom),
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
zIndexes: {
|
||||
top: isTopFirst ? 3 : 0,
|
||||
bottom: isTopFirst ? 0 : 3,
|
||||
front: isFronFirst ? 2 : 1,
|
||||
back: isFronFirst ? 1 : 2,
|
||||
group: cuboidData.zIndexes.group
|
||||
}
|
||||
};
|
||||
};
|
||||
// Returns cylinder Front path
|
||||
RendererProto.getCylinderFront = function (topPath, bottomPath) {
|
||||
var path = topPath.slice(0, 3);
|
||||
if (isSimplified(bottomPath)) {
|
||||
var move = bottomPath[0];
|
||||
if (move[0] === 'M') {
|
||||
path.push(bottomPath[2]);
|
||||
path.push(bottomPath[1]);
|
||||
path.push(['L', move[1], move[2]]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var move = bottomPath[0], curve1 = bottomPath[1], curve2 = bottomPath[2];
|
||||
if (move[0] === 'M' && curve1[0] === 'C' && curve2[0] === 'C') {
|
||||
path.push(['L', curve2[5], curve2[6]]);
|
||||
path.push(['C', curve2[3], curve2[4], curve2[1], curve2[2], curve1[5], curve1[6]]);
|
||||
path.push(['C', curve1[3], curve1[4], curve1[1], curve1[2], move[1], move[2]]);
|
||||
}
|
||||
}
|
||||
path.push(['Z']);
|
||||
return path;
|
||||
};
|
||||
// Returns cylinder Back path
|
||||
RendererProto.getCylinderBack = function (topPath, bottomPath) {
|
||||
var path = [];
|
||||
if (isSimplified(topPath)) {
|
||||
var move = topPath[0], line2 = topPath[2];
|
||||
if (move[0] === 'M' && line2[0] === 'L') {
|
||||
path.push(['M', line2[1], line2[2]]);
|
||||
path.push(topPath[3]);
|
||||
// End at start
|
||||
path.push(['L', move[1], move[2]]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (topPath[2][0] === 'C') {
|
||||
path.push(['M', topPath[2][5], topPath[2][6]]);
|
||||
}
|
||||
path.push(topPath[3], topPath[4]);
|
||||
}
|
||||
if (isSimplified(bottomPath)) {
|
||||
var move = bottomPath[0];
|
||||
if (move[0] === 'M') {
|
||||
path.push(['L', move[1], move[2]]);
|
||||
path.push(bottomPath[3]);
|
||||
path.push(bottomPath[2]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var curve2 = bottomPath[2], curve3 = bottomPath[3], curve4 = bottomPath[4];
|
||||
if (curve2[0] === 'C' && curve3[0] === 'C' && curve4[0] === 'C') {
|
||||
path.push(['L', curve4[5], curve4[6]]);
|
||||
path.push(['C', curve4[3], curve4[4], curve4[1], curve4[2], curve3[5], curve3[6]]);
|
||||
path.push(['C', curve3[3], curve3[4], curve3[1], curve3[2], curve2[5], curve2[6]]);
|
||||
}
|
||||
}
|
||||
path.push(['Z']);
|
||||
return path;
|
||||
};
|
||||
// Retruns cylinder path for top or bottom
|
||||
RendererProto.getCylinderEnd = function (chart, shapeArgs, isBottom) {
|
||||
// A half of the smaller one out of width or depth (optional, because
|
||||
// there's no depth for a funnel that reuses the code)
|
||||
var depth = pick(shapeArgs.depth, shapeArgs.width), radius = Math.min(shapeArgs.width, depth) / 2,
|
||||
// Approximated longest diameter
|
||||
angleOffset = deg2rad * (chart.options.chart.options3d.beta - 90 +
|
||||
(shapeArgs.alphaCorrection || 0)),
|
||||
// Could be top or bottom of the cylinder
|
||||
y = shapeArgs.y + (isBottom ? shapeArgs.height : 0),
|
||||
// Use cubic Bezier curve to draw a cricle in x,z (y is constant).
|
||||
// More math. at spencermortensen.com/articles/bezier-circle/
|
||||
c = 0.5519 * radius, centerX = shapeArgs.width / 2 + shapeArgs.x, centerZ = depth / 2 + shapeArgs.z,
|
||||
// points could be generated in a loop, but readability will plummet
|
||||
points = [{
|
||||
x: 0,
|
||||
y: y,
|
||||
z: radius
|
||||
}, {
|
||||
x: c,
|
||||
y: y,
|
||||
z: radius
|
||||
}, {
|
||||
x: radius,
|
||||
y: y,
|
||||
z: c
|
||||
}, {
|
||||
x: radius,
|
||||
y: y,
|
||||
z: 0
|
||||
}, {
|
||||
x: radius,
|
||||
y: y,
|
||||
z: -c
|
||||
}, {
|
||||
x: c,
|
||||
y: y,
|
||||
z: -radius
|
||||
}, {
|
||||
x: 0,
|
||||
y: y,
|
||||
z: -radius
|
||||
}, {
|
||||
x: -c,
|
||||
y: y,
|
||||
z: -radius
|
||||
}, {
|
||||
x: -radius,
|
||||
y: y,
|
||||
z: -c
|
||||
}, {
|
||||
x: -radius,
|
||||
y: y,
|
||||
z: 0
|
||||
}, {
|
||||
x: -radius,
|
||||
y: y,
|
||||
z: c
|
||||
}, {
|
||||
x: -c,
|
||||
y: y,
|
||||
z: radius
|
||||
}, {
|
||||
x: 0,
|
||||
y: y,
|
||||
z: radius
|
||||
}], cosTheta = Math.cos(angleOffset), sinTheta = Math.sin(angleOffset), perspectivePoints, path, x, z;
|
||||
// rotete to match chart's beta and translate to the shape center
|
||||
points.forEach(function (point, i) {
|
||||
x = point.x;
|
||||
z = point.z;
|
||||
// x′ = (x * cosθ − z * sinθ) + centerX
|
||||
// z′ = (z * cosθ + x * sinθ) + centerZ
|
||||
points[i].x = (x * cosTheta - z * sinTheta) + centerX;
|
||||
points[i].z = (z * cosTheta + x * sinTheta) + centerZ;
|
||||
});
|
||||
perspectivePoints = perspective(points, chart, true);
|
||||
// check for sub-pixel curve issue, compare front and back edges
|
||||
if (Math.abs(perspectivePoints[3].y - perspectivePoints[9].y) < 2.5 &&
|
||||
Math.abs(perspectivePoints[0].y - perspectivePoints[6].y) < 2.5) {
|
||||
// use simplied shape
|
||||
path = this.toLinePath([
|
||||
perspectivePoints[0],
|
||||
perspectivePoints[3],
|
||||
perspectivePoints[6],
|
||||
perspectivePoints[9]
|
||||
], true);
|
||||
}
|
||||
else {
|
||||
// or default curved path to imitate ellipse (2D circle)
|
||||
path = this.getCurvedPath(perspectivePoints);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
// Returns curved path in format of:
|
||||
// [ M, x, y, ...[C, cp1x, cp2y, cp2x, cp2y, epx, epy]*n_times ]
|
||||
// (cp - control point, ep - end point)
|
||||
RendererProto.getCurvedPath = function (points) {
|
||||
var path = [['M', points[0].x, points[0].y]], limit = points.length - 2, i;
|
||||
for (i = 1; i < limit; i += 3) {
|
||||
path.push([
|
||||
'C',
|
||||
points[i].x, points[i].y,
|
||||
points[i + 1].x, points[i + 1].y,
|
||||
points[i + 2].x, points[i + 2].y
|
||||
]);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
@@ -0,0 +1,337 @@
|
||||
/* *
|
||||
*
|
||||
* Dependency wheel module
|
||||
*
|
||||
* (c) 2018-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var animObject = U.animObject, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import NodesMixin from '../Mixins/Nodes.js';
|
||||
var base = H.seriesTypes.sankey.prototype;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.dependencywheel
|
||||
*
|
||||
* @augments Highcharts.seriesTypes.sankey
|
||||
*/
|
||||
seriesType('dependencywheel', 'sankey',
|
||||
/**
|
||||
* A dependency wheel chart is a type of flow diagram, where all nodes are
|
||||
* laid out in a circle, and the flow between the are drawn as link bands.
|
||||
*
|
||||
* @sample highcharts/demo/dependency-wheel/
|
||||
* Dependency wheel
|
||||
*
|
||||
* @extends plotOptions.sankey
|
||||
* @exclude dataSorting
|
||||
* @since 7.1.0
|
||||
* @product highcharts
|
||||
* @requires modules/dependencywheel
|
||||
* @optionparent plotOptions.dependencywheel
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* The center of the wheel relative to the plot area. Can be
|
||||
* percentages or pixel values. The default behaviour is to
|
||||
* center the wheel inside the plot area.
|
||||
*
|
||||
* @type {Array<number|string|null>}
|
||||
* @default [null, null]
|
||||
* @product highcharts
|
||||
*/
|
||||
center: [null, null],
|
||||
curveFactor: 0.6,
|
||||
/**
|
||||
* The start angle of the dependency wheel, in degrees where 0 is up.
|
||||
*/
|
||||
startAngle: 0
|
||||
}, {
|
||||
orderNodes: false,
|
||||
getCenter: H.seriesTypes.pie.prototype.getCenter,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Dependency wheel has only one column, it runs along the perimeter.
|
||||
* @private
|
||||
*/
|
||||
createNodeColumns: function () {
|
||||
var columns = [this.createNodeColumn()];
|
||||
this.nodes.forEach(function (node) {
|
||||
node.column = 0;
|
||||
columns[0].push(node);
|
||||
});
|
||||
return columns;
|
||||
},
|
||||
/**
|
||||
* Translate from vertical pixels to perimeter.
|
||||
* @private
|
||||
*/
|
||||
getNodePadding: function () {
|
||||
return this.options.nodePadding / Math.PI;
|
||||
},
|
||||
createNode: function (id) {
|
||||
var node = base.createNode.call(this, id);
|
||||
node.index = this.nodes.length - 1;
|
||||
/**
|
||||
* Return the sum of incoming and outgoing links.
|
||||
* @private
|
||||
*/
|
||||
node.getSum = function () {
|
||||
return node.linksFrom
|
||||
.concat(node.linksTo)
|
||||
.reduce(function (acc, link) {
|
||||
return acc + link.weight;
|
||||
}, 0);
|
||||
};
|
||||
/**
|
||||
* Get the offset in weight values of a point/link.
|
||||
* @private
|
||||
*/
|
||||
node.offset = function (point) {
|
||||
var offset = 0, i, links = node.linksFrom.concat(node.linksTo), sliced;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function otherNode(link) {
|
||||
if (link.fromNode === node) {
|
||||
return link.toNode;
|
||||
}
|
||||
return link.fromNode;
|
||||
}
|
||||
// Sort and slice the links to avoid links going out of each
|
||||
// node crossing each other.
|
||||
links.sort(function (a, b) {
|
||||
return otherNode(a).index - otherNode(b).index;
|
||||
});
|
||||
for (i = 0; i < links.length; i++) {
|
||||
if (otherNode(links[i]).index > node.index) {
|
||||
links = links.slice(0, i).reverse().concat(links.slice(i).reverse());
|
||||
sliced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!sliced) {
|
||||
links.reverse();
|
||||
}
|
||||
for (i = 0; i < links.length; i++) {
|
||||
if (links[i] === point) {
|
||||
return offset;
|
||||
}
|
||||
offset += links[i].weight;
|
||||
}
|
||||
};
|
||||
return node;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @todo Override the refactored sankey translateLink and translateNode
|
||||
* functions instead of the whole translate function.
|
||||
*/
|
||||
translate: function () {
|
||||
var options = this.options, factor = 2 * Math.PI /
|
||||
(this.chart.plotHeight + this.getNodePadding()), center = this.getCenter(), startAngle = (options.startAngle - 90) * H.deg2rad;
|
||||
base.translate.call(this);
|
||||
this.nodeColumns[0].forEach(function (node) {
|
||||
// Don't render the nodes if sum is 0 #12453
|
||||
if (node.sum) {
|
||||
var shapeArgs = node.shapeArgs, centerX = center[0], centerY = center[1], r = center[2] / 2, innerR = r - options.nodeWidth, start = startAngle + factor * shapeArgs.y, end = startAngle +
|
||||
factor * (shapeArgs.y + shapeArgs.height);
|
||||
// Middle angle
|
||||
node.angle = start + (end - start) / 2;
|
||||
node.shapeType = 'arc';
|
||||
node.shapeArgs = {
|
||||
x: centerX,
|
||||
y: centerY,
|
||||
r: r,
|
||||
innerR: innerR,
|
||||
start: start,
|
||||
end: end
|
||||
};
|
||||
node.dlBox = {
|
||||
x: centerX + Math.cos((start + end) / 2) * (r + innerR) / 2,
|
||||
y: centerY + Math.sin((start + end) / 2) * (r + innerR) / 2,
|
||||
width: 1,
|
||||
height: 1
|
||||
};
|
||||
// Draw the links from this node
|
||||
node.linksFrom.forEach(function (point) {
|
||||
if (point.linkBase) {
|
||||
var distance;
|
||||
var corners = point.linkBase.map(function (top, i) {
|
||||
var angle = factor * top, x = Math.cos(startAngle + angle) * (innerR + 1), y = Math.sin(startAngle + angle) * (innerR + 1), curveFactor = options.curveFactor;
|
||||
// The distance between the from and to node
|
||||
// along the perimeter. This affect how curved
|
||||
// the link is, so that links between neighbours
|
||||
// don't extend too far towards the center.
|
||||
distance = Math.abs(point.linkBase[3 - i] * factor - angle);
|
||||
if (distance > Math.PI) {
|
||||
distance = 2 * Math.PI - distance;
|
||||
}
|
||||
distance = distance * innerR;
|
||||
if (distance < innerR) {
|
||||
curveFactor *= (distance / innerR);
|
||||
}
|
||||
return {
|
||||
x: centerX + x,
|
||||
y: centerY + y,
|
||||
cpX: centerX + (1 - curveFactor) * x,
|
||||
cpY: centerY + (1 - curveFactor) * y
|
||||
};
|
||||
});
|
||||
point.shapeArgs = {
|
||||
d: [[
|
||||
'M',
|
||||
corners[0].x, corners[0].y
|
||||
], [
|
||||
'A',
|
||||
innerR, innerR,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
corners[1].x, corners[1].y
|
||||
], [
|
||||
'C',
|
||||
corners[1].cpX, corners[1].cpY,
|
||||
corners[2].cpX, corners[2].cpY,
|
||||
corners[2].x, corners[2].y
|
||||
], [
|
||||
'A',
|
||||
innerR, innerR,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
corners[3].x, corners[3].y
|
||||
], [
|
||||
'C',
|
||||
corners[3].cpX, corners[3].cpY,
|
||||
corners[0].cpX, corners[0].cpY,
|
||||
corners[0].x, corners[0].y
|
||||
]]
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
animate: function (init) {
|
||||
if (!init) {
|
||||
var duration = animObject(this.options.animation).duration, step = (duration / 2) / this.nodes.length;
|
||||
this.nodes.forEach(function (point, i) {
|
||||
var graphic = point.graphic;
|
||||
if (graphic) {
|
||||
graphic.attr({ opacity: 0 });
|
||||
setTimeout(function () {
|
||||
graphic.animate({ opacity: 1 }, { duration: step });
|
||||
}, step * i);
|
||||
}
|
||||
}, this);
|
||||
this.points.forEach(function (point) {
|
||||
var graphic = point.graphic;
|
||||
if (!point.isNode && graphic) {
|
||||
graphic.attr({ opacity: 0 })
|
||||
.animate({
|
||||
opacity: 1
|
||||
}, this.options.animation);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
},
|
||||
// Point class
|
||||
{
|
||||
setState: NodesMixin.setNodeState,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Return a text path that the data label uses.
|
||||
* @private
|
||||
*/
|
||||
getDataLabelPath: function (label) {
|
||||
var renderer = this.series.chart.renderer, shapeArgs = this.shapeArgs, upperHalf = this.angle < 0 || this.angle > Math.PI, start = shapeArgs.start, end = shapeArgs.end;
|
||||
if (!this.dataLabelPath) {
|
||||
this.dataLabelPath = renderer
|
||||
.arc({ open: true })
|
||||
// Add it inside the data label group so it gets destroyed
|
||||
// with the label
|
||||
.add(label);
|
||||
}
|
||||
this.dataLabelPath.attr({
|
||||
x: shapeArgs.x,
|
||||
y: shapeArgs.y,
|
||||
r: (shapeArgs.r +
|
||||
(this.dataLabel.options.distance || 0)),
|
||||
start: (upperHalf ? start : end),
|
||||
end: (upperHalf ? end : start),
|
||||
clockwise: +upperHalf
|
||||
});
|
||||
return this.dataLabelPath;
|
||||
},
|
||||
isValid: function () {
|
||||
// No null points here
|
||||
return true;
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* A `dependencywheel` series. If the [type](#series.dependencywheel.type)
|
||||
* option is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.dependencywheel
|
||||
* @exclude dataSorting
|
||||
* @product highcharts
|
||||
* @requires modules/dependencywheel
|
||||
* @apioption series.dependencywheel
|
||||
*/
|
||||
/**
|
||||
* A collection of options for the individual nodes. The nodes in a dependency
|
||||
* diagram are auto-generated instances of `Highcharts.Point`, but options can
|
||||
* be applied here and linked by the `id`.
|
||||
*
|
||||
* @extends series.sankey.nodes
|
||||
* @type {Array<*>}
|
||||
* @product highcharts
|
||||
* @excluding offset
|
||||
* @apioption series.dependencywheel.nodes
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `dependencywheel` series
|
||||
* type, points can be given in the following way:
|
||||
*
|
||||
* 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: [{
|
||||
* from: 'Category1',
|
||||
* to: 'Category2',
|
||||
* weight: 2
|
||||
* }, {
|
||||
* from: 'Category1',
|
||||
* to: 'Category3',
|
||||
* weight: 5
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<*>}
|
||||
* @extends series.sankey.data
|
||||
* @product highcharts
|
||||
* @excluding outgoing, dataLabels
|
||||
* @apioption series.dependencywheel.data
|
||||
*/
|
||||
/**
|
||||
* Individual data label for each node. The options are the same as
|
||||
* the ones for [series.dependencywheel.dataLabels](#series.dependencywheel.dataLabels).
|
||||
*
|
||||
* @apioption series.dependencywheel.nodes.dataLabels
|
||||
*/
|
||||
''; // adds doclets above to the transpiled file
|
||||
@@ -0,0 +1,108 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2009-2020 Torstein Honsi
|
||||
*
|
||||
* Dot plot series type for Highcharts
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
* @todo
|
||||
* - Check update, remove etc.
|
||||
* - Custom icons like persons, carts etc. Either as images, font icons or
|
||||
* Highcharts symbols.
|
||||
*/
|
||||
'use strict';
|
||||
import SVGRenderer from '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var extend = U.extend, objectEach = U.objectEach, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Series/Series.js';
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.dotplot
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('dotplot', 'column', {
|
||||
itemPadding: 0.2,
|
||||
marker: {
|
||||
symbol: 'circle',
|
||||
states: {
|
||||
hover: {},
|
||||
select: {}
|
||||
}
|
||||
}
|
||||
}, {
|
||||
markerAttribs: void 0,
|
||||
drawPoints: function () {
|
||||
var series = this, renderer = series.chart.renderer, seriesMarkerOptions = this.options.marker, itemPaddingTranslated = this.yAxis.transA *
|
||||
series.options.itemPadding, borderWidth = this.borderWidth, crisp = borderWidth % 2 ? 0.5 : 1;
|
||||
this.points.forEach(function (point) {
|
||||
var yPos, attr, graphics, itemY, pointAttr, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
|
||||
seriesMarkerOptions.symbol), radius = pick(pointMarkerOptions.radius, seriesMarkerOptions.radius), size, yTop, isSquare = symbol !== 'rect', x, y;
|
||||
point.graphics = graphics = point.graphics || {};
|
||||
pointAttr = point.pointAttr ?
|
||||
(point.pointAttr[point.selected ? 'selected' : ''] ||
|
||||
series.pointAttr['']) :
|
||||
series.pointAttribs(point, point.selected && 'select');
|
||||
delete pointAttr.r;
|
||||
if (series.chart.styledMode) {
|
||||
delete pointAttr.stroke;
|
||||
delete pointAttr['stroke-width'];
|
||||
}
|
||||
if (point.y !== null) {
|
||||
if (!point.graphic) {
|
||||
point.graphic = renderer.g('point').add(series.group);
|
||||
}
|
||||
itemY = point.y;
|
||||
yTop = pick(point.stackY, point.y);
|
||||
size = Math.min(point.pointWidth, series.yAxis.transA - itemPaddingTranslated);
|
||||
for (yPos = yTop; yPos > yTop - point.y; yPos--) {
|
||||
x = point.barX + (isSquare ?
|
||||
point.pointWidth / 2 - size / 2 :
|
||||
0);
|
||||
y = series.yAxis.toPixels(yPos, true) +
|
||||
itemPaddingTranslated / 2;
|
||||
if (series.options.crisp) {
|
||||
x = Math.round(x) - crisp;
|
||||
y = Math.round(y) + crisp;
|
||||
}
|
||||
attr = {
|
||||
x: x,
|
||||
y: y,
|
||||
width: Math.round(isSquare ? size : point.pointWidth),
|
||||
height: Math.round(size),
|
||||
r: radius
|
||||
};
|
||||
if (graphics[itemY]) {
|
||||
graphics[itemY].animate(attr);
|
||||
}
|
||||
else {
|
||||
graphics[itemY] = renderer.symbol(symbol)
|
||||
.attr(extend(attr, pointAttr))
|
||||
.add(point.graphic);
|
||||
}
|
||||
graphics[itemY].isActive = true;
|
||||
itemY--;
|
||||
}
|
||||
}
|
||||
objectEach(graphics, function (graphic, key) {
|
||||
if (!graphic.isActive) {
|
||||
graphic.destroy();
|
||||
delete graphic[key];
|
||||
}
|
||||
else {
|
||||
graphic.isActive = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
SVGRenderer.prototype.symbols.rect = function (x, y, w, h, options) {
|
||||
return SVGRenderer.prototype.symbols.callout(x, y, w, h, options);
|
||||
};
|
||||
@@ -0,0 +1,438 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Sebastian Bochan, Rafal Sebestjanski
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
var SVGRenderer = H.SVGRenderer;
|
||||
import U from '../Core/Utilities.js';
|
||||
var extend = U.extend, pick = U.pick, seriesType = U.seriesType;
|
||||
import './AreaRangeSeries.js';
|
||||
var seriesTypes = H.seriesTypes, seriesProto = H.Series.prototype, areaRangeProto = seriesTypes.arearange.prototype, columnRangeProto = seriesTypes.columnrange.prototype, colProto = seriesTypes.column.prototype, areaRangePointProto = areaRangeProto.pointClass.prototype;
|
||||
/**
|
||||
* The dumbbell series is a cartesian series with higher and lower values for
|
||||
* each point along an X axis, connected with a line between the values.
|
||||
* Requires `highcharts-more.js` and `modules/dumbbell.js`.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/dumbbell/
|
||||
* Dumbbell chart
|
||||
* @sample {highcharts} highcharts/series-dumbbell/styled-mode-dumbbell/
|
||||
* Styled mode
|
||||
*
|
||||
* @extends plotOptions.arearange
|
||||
* @product highcharts highstock
|
||||
* @excluding fillColor, fillOpacity, lineWidth, stack, stacking,
|
||||
* stickyTracking, trackByArea, boostThreshold, boostBlending
|
||||
* @since 8.0.0
|
||||
* @optionparent plotOptions.dumbbell
|
||||
*/
|
||||
seriesType('dumbbell', 'arearange', {
|
||||
/** @ignore-option */
|
||||
trackByArea: false,
|
||||
/** @ignore-option */
|
||||
fillColor: 'none',
|
||||
/** @ignore-option */
|
||||
lineWidth: 0,
|
||||
pointRange: 1,
|
||||
/**
|
||||
* Pixel width of the line that connects the dumbbell point's values.
|
||||
*
|
||||
* @since 8.0.0
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
connectorWidth: 1,
|
||||
/** @ignore-option */
|
||||
stickyTracking: false,
|
||||
groupPadding: 0.2,
|
||||
crisp: false,
|
||||
pointPadding: 0.1,
|
||||
/**
|
||||
* Color of the start markers in a dumbbell graph.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 8.0.0
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
lowColor: '#333333',
|
||||
/**
|
||||
* Color of the line that connects the dumbbell point's values.
|
||||
* By default it is the series' color.
|
||||
*
|
||||
* @type {string}
|
||||
* @product highcharts highstock
|
||||
* @since 8.0.0
|
||||
* @apioption plotOptions.dumbbell.connectorColor
|
||||
*/
|
||||
states: {
|
||||
hover: {
|
||||
/** @ignore-option */
|
||||
lineWidthPlus: 0,
|
||||
/**
|
||||
* The additional connector line width for a hovered point.
|
||||
*
|
||||
* @since 8.0.0
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
connectorWidthPlus: 1,
|
||||
/** @ignore-option */
|
||||
halo: false
|
||||
}
|
||||
}
|
||||
}, {
|
||||
trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
|
||||
drawTracker: H.TrackerMixin.drawTrackerPoint,
|
||||
drawGraph: H.noop,
|
||||
crispCol: colProto.crispCol,
|
||||
/**
|
||||
* Get connector line path and styles that connects dumbbell point's low and
|
||||
* high values.
|
||||
* @private
|
||||
*
|
||||
* @param {Highcharts.Series} this The series of points.
|
||||
* @param {Highcharts.Point} point The point to inspect.
|
||||
*
|
||||
* @return {Highcharts.SVGAttributes} attribs The path and styles.
|
||||
*/
|
||||
getConnectorAttribs: function (point) {
|
||||
var series = this, chart = series.chart, pointOptions = point.options, seriesOptions = series.options, xAxis = series.xAxis, yAxis = series.yAxis, connectorWidth = pick(pointOptions.connectorWidth, seriesOptions.connectorWidth), connectorColor = pick(pointOptions.connectorColor, seriesOptions.connectorColor, pointOptions.color, point.zone ? point.zone.color : void 0, point.color), connectorWidthPlus = pick(seriesOptions.states &&
|
||||
seriesOptions.states.hover &&
|
||||
seriesOptions.states.hover.connectorWidthPlus, 1), dashStyle = pick(pointOptions.dashStyle, seriesOptions.dashStyle), pointTop = pick(point.plotLow, point.plotY), pxThreshold = yAxis.toPixels(seriesOptions.threshold || 0, true), pointHeight = chart.inverted ?
|
||||
yAxis.len - pxThreshold : pxThreshold, pointBottom = pick(point.plotHigh, pointHeight), attribs, origProps;
|
||||
if (point.state) {
|
||||
connectorWidth = connectorWidth + connectorWidthPlus;
|
||||
}
|
||||
if (pointTop < 0) {
|
||||
pointTop = 0;
|
||||
}
|
||||
else if (pointTop >= yAxis.len) {
|
||||
pointTop = yAxis.len;
|
||||
}
|
||||
if (pointBottom < 0) {
|
||||
pointBottom = 0;
|
||||
}
|
||||
else if (pointBottom >= yAxis.len) {
|
||||
pointBottom = yAxis.len;
|
||||
}
|
||||
if (point.plotX < 0 || point.plotX > xAxis.len) {
|
||||
connectorWidth = 0;
|
||||
}
|
||||
// Connector should reflect upper marker's zone color
|
||||
if (point.upperGraphic) {
|
||||
origProps = {
|
||||
y: point.y,
|
||||
zone: point.zone
|
||||
};
|
||||
point.y = point.high;
|
||||
point.zone = point.zone ? point.getZone() : void 0;
|
||||
connectorColor = pick(pointOptions.connectorColor, seriesOptions.connectorColor, pointOptions.color, point.zone ? point.zone.color : void 0, point.color);
|
||||
extend(point, origProps);
|
||||
}
|
||||
attribs = {
|
||||
d: SVGRenderer.prototype.crispLine([[
|
||||
'M',
|
||||
point.plotX,
|
||||
pointTop
|
||||
], [
|
||||
'L',
|
||||
point.plotX,
|
||||
pointBottom
|
||||
]], connectorWidth, 'ceil')
|
||||
};
|
||||
if (!chart.styledMode) {
|
||||
attribs.stroke = connectorColor;
|
||||
attribs['stroke-width'] = connectorWidth;
|
||||
if (dashStyle) {
|
||||
attribs.dashstyle = dashStyle;
|
||||
}
|
||||
}
|
||||
return attribs;
|
||||
},
|
||||
/**
|
||||
* Draw connector line that connects dumbbell point's low and high values.
|
||||
* @private
|
||||
*
|
||||
* @param {Highcharts.Series} this The series of points.
|
||||
* @param {Highcharts.Point} point The point to inspect.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
drawConnector: function (point) {
|
||||
var series = this, animationLimit = pick(series.options.animationLimit, 250), verb = point.connector && series.chart.pointCount < animationLimit ?
|
||||
'animate' : 'attr';
|
||||
if (!point.connector) {
|
||||
point.connector = series.chart.renderer.path()
|
||||
.addClass('highcharts-lollipop-stem')
|
||||
.attr({
|
||||
zIndex: -1
|
||||
})
|
||||
.add(series.markerGroup);
|
||||
}
|
||||
point.connector[verb](this.getConnectorAttribs(point));
|
||||
},
|
||||
/**
|
||||
* Return the width and x offset of the dumbbell adjusted for grouping,
|
||||
* groupPadding, pointPadding, pointWidth etc.
|
||||
*
|
||||
* @private
|
||||
*
|
||||
* @function Highcharts.seriesTypes.column#getColumnMetrics
|
||||
*
|
||||
* @param {Highcharts.Series} this The series of points.
|
||||
*
|
||||
* @return {Highcharts.ColumnMetricsObject} metrics shapeArgs
|
||||
*
|
||||
*/
|
||||
getColumnMetrics: function () {
|
||||
var metrics = colProto.getColumnMetrics.apply(this, arguments);
|
||||
metrics.offset += metrics.width / 2;
|
||||
return metrics;
|
||||
},
|
||||
translatePoint: areaRangeProto.translate,
|
||||
setShapeArgs: columnRangeProto.translate,
|
||||
/**
|
||||
* Translate each point to the plot area coordinate system and find
|
||||
* shape positions
|
||||
*
|
||||
* @private
|
||||
*
|
||||
* @function Highcharts.seriesTypes.dumbbell#translate
|
||||
*
|
||||
* @param {Highcharts.Series} this The series of points.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
translate: function () {
|
||||
// Calculate shapeargs
|
||||
this.setShapeArgs.apply(this);
|
||||
// Calculate point low / high values
|
||||
this.translatePoint.apply(this, arguments);
|
||||
// Correct x position
|
||||
this.points.forEach(function (point) {
|
||||
var shapeArgs = point.shapeArgs, pointWidth = point.pointWidth;
|
||||
point.plotX = shapeArgs.x;
|
||||
shapeArgs.x = point.plotX - pointWidth / 2;
|
||||
point.tooltipPos = null;
|
||||
});
|
||||
this.columnMetrics.offset -= this.columnMetrics.width / 2;
|
||||
},
|
||||
seriesDrawPoints: areaRangeProto.drawPoints,
|
||||
/**
|
||||
* Extend the arearange series' drawPoints method by applying a connector
|
||||
* and coloring markers.
|
||||
* @private
|
||||
*
|
||||
* @function Highcharts.Series#drawPoints
|
||||
*
|
||||
* @param {Highcharts.Series} this The series of points.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, chart = series.chart, pointLength = series.points.length, seriesLowColor = series.lowColor = series.options.lowColor, i = 0, lowerGraphicColor, point, zoneColor;
|
||||
this.seriesDrawPoints.apply(series, arguments);
|
||||
// Draw connectors and color upper markers
|
||||
while (i < pointLength) {
|
||||
point = series.points[i];
|
||||
series.drawConnector(point);
|
||||
if (point.upperGraphic) {
|
||||
point.upperGraphic.element.point = point;
|
||||
point.upperGraphic.addClass('highcharts-lollipop-high');
|
||||
}
|
||||
point.connector.element.point = point;
|
||||
if (point.lowerGraphic) {
|
||||
zoneColor = point.zone && point.zone.color;
|
||||
lowerGraphicColor = pick(point.options.lowColor, seriesLowColor, point.options.color, zoneColor, point.color, series.color);
|
||||
if (!chart.styledMode) {
|
||||
point.lowerGraphic.attr({
|
||||
fill: lowerGraphicColor
|
||||
});
|
||||
}
|
||||
point.lowerGraphic.addClass('highcharts-lollipop-low');
|
||||
}
|
||||
i++;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Get non-presentational attributes for a point. Used internally for
|
||||
* both styled mode and classic. Set correct position in link with connector
|
||||
* line.
|
||||
*
|
||||
* @see Series#pointAttribs
|
||||
*
|
||||
* @function Highcharts.Series#markerAttribs
|
||||
*
|
||||
* @param {Highcharts.Series} this The series of points.
|
||||
*
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
* A hash containing those attributes that are not settable from
|
||||
* CSS.
|
||||
*/
|
||||
markerAttribs: function () {
|
||||
var ret = areaRangeProto.markerAttribs.apply(this, arguments);
|
||||
ret.x = Math.floor(ret.x);
|
||||
ret.y = Math.floor(ret.y);
|
||||
return ret;
|
||||
},
|
||||
/**
|
||||
* Get presentational attributes
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.column#pointAttribs
|
||||
*
|
||||
* @param {Highcharts.Series} this The series of points.
|
||||
* @param {Highcharts.Point} point The point to inspect.
|
||||
* @param {string} state current state of point (normal, hover, select)
|
||||
*
|
||||
* @return {Highcharts.SVGAttributes} pointAttribs SVGAttributes
|
||||
*/
|
||||
pointAttribs: function (point, state) {
|
||||
var pointAttribs;
|
||||
pointAttribs = seriesProto.pointAttribs.apply(this, arguments);
|
||||
if (state === 'hover') {
|
||||
delete pointAttribs.fill;
|
||||
}
|
||||
return pointAttribs;
|
||||
}
|
||||
}, {
|
||||
// seriesTypes doesn't inherit from arearange point proto so put below
|
||||
// methods rigidly.
|
||||
destroyElements: areaRangePointProto.destroyElements,
|
||||
isValid: areaRangePointProto.isValid,
|
||||
pointSetState: areaRangePointProto.setState,
|
||||
/**
|
||||
* Set the point's state extended by have influence on the connector
|
||||
* (between low and high value).
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} this The point to inspect.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
setState: function () {
|
||||
var point = this, series = point.series, chart = series.chart, seriesLowColor = series.options.lowColor, seriesMarker = series.options.marker, pointOptions = point.options, pointLowColor = pointOptions.lowColor, zoneColor = point.zone && point.zone.color, lowerGraphicColor = pick(pointLowColor, seriesLowColor, pointOptions.color, zoneColor, point.color, series.color), verb = 'attr', upperGraphicColor, origProps;
|
||||
this.pointSetState.apply(this, arguments);
|
||||
if (!point.state) {
|
||||
verb = 'animate';
|
||||
if (point.lowerGraphic && !chart.styledMode) {
|
||||
point.lowerGraphic.attr({
|
||||
fill: lowerGraphicColor
|
||||
});
|
||||
if (point.upperGraphic) {
|
||||
origProps = {
|
||||
y: point.y,
|
||||
zone: point.zone
|
||||
};
|
||||
point.y = point.high;
|
||||
point.zone = point.zone ? point.getZone() : void 0;
|
||||
upperGraphicColor = pick(point.marker ? point.marker.fillColor : void 0, seriesMarker ? seriesMarker.fillColor : void 0, pointOptions.color, point.zone ? point.zone.color : void 0, point.color);
|
||||
point.upperGraphic.attr({
|
||||
fill: upperGraphicColor
|
||||
});
|
||||
extend(point, origProps);
|
||||
}
|
||||
}
|
||||
}
|
||||
point.connector[verb](series.getConnectorAttribs(point));
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The `dumbbell` series. If the [type](#series.dumbbell.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.dumbbell
|
||||
* @excluding boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @requires modules/dumbbell
|
||||
* @apioption series.dumbbell
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `dumbbell` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,low,high`. If the first value is a string, it is applied as the name
|
||||
* of the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2\. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 4, 2],
|
||||
* [1, 2, 1],
|
||||
* [2, 9, 10]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.dumbbell.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* low: 0,
|
||||
* high: 4,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00",
|
||||
* lowColor: "#00FFFF",
|
||||
* connectorWidth: 3,
|
||||
* connectorColor: "#FF00FF"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* low: 5,
|
||||
* high: 3,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.arearange.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.dumbbell.data
|
||||
*/
|
||||
/**
|
||||
* Color of the line that connects the dumbbell point's values.
|
||||
* By default it is the series' color.
|
||||
*
|
||||
* @type {string}
|
||||
* @since 8.0.0
|
||||
* @product highcharts highstock
|
||||
* @apioption series.dumbbell.data.connectorColor
|
||||
*/
|
||||
/**
|
||||
* Pixel width of the line that connects the dumbbell point's values.
|
||||
*
|
||||
* @type {number}
|
||||
* @since 8.0.0
|
||||
* @default 1
|
||||
* @product highcharts highstock
|
||||
* @apioption series.dumbbell.data.connectorWidth
|
||||
*/
|
||||
/**
|
||||
* Color of the start markers in a dumbbell graph.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 8.0.0
|
||||
* @default #333333
|
||||
* @product highcharts highstock
|
||||
* @apioption series.dumbbell.data.lowColor
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,168 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import './BoxPlotSeries.js';
|
||||
var noop = H.noop, seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* Error bars are a graphical representation of the variability of data and are
|
||||
* used on graphs to indicate the error, or uncertainty in a reported
|
||||
* measurement.
|
||||
*
|
||||
* @sample highcharts/demo/error-bar/
|
||||
* Error bars on a column series
|
||||
* @sample highcharts/series-errorbar/on-scatter/
|
||||
* Error bars on a scatter series
|
||||
*
|
||||
* @extends plotOptions.boxplot
|
||||
* @excluding boostBlending, boostThreshold
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.errorbar
|
||||
*/
|
||||
seriesType('errorbar', 'boxplot', {
|
||||
/**
|
||||
* The main color of the bars. This can be overridden by
|
||||
* [stemColor](#plotOptions.errorbar.stemColor) and
|
||||
* [whiskerColor](#plotOptions.errorbar.whiskerColor) individually.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/error-bar-styling/
|
||||
* Error bar styling
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #000000
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
color: '#000000',
|
||||
grouping: false,
|
||||
/**
|
||||
* The parent series of the error bar. The default value links it to
|
||||
* the previous series. Otherwise, use the id of the parent series.
|
||||
*
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
linkedTo: ':previous',
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.low}</b> - <b>{point.high}</b><br/>'
|
||||
},
|
||||
/**
|
||||
* The line width of the whiskers, the horizontal lines marking low
|
||||
* and high values. When `null`, the general
|
||||
* [lineWidth](#plotOptions.errorbar.lineWidth) applies.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/error-bar-styling/
|
||||
* Error bar styling
|
||||
*
|
||||
* @type {number}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
whiskerWidth: null
|
||||
// Prototype members
|
||||
}, {
|
||||
type: 'errorbar',
|
||||
// array point configs are mapped to this
|
||||
pointArrayMap: ['low', 'high'],
|
||||
// return a plain array for speedy calculation
|
||||
toYData: function (point) {
|
||||
return [point.low, point.high];
|
||||
},
|
||||
pointValKey: 'high',
|
||||
doQuartiles: false,
|
||||
drawDataLabels: seriesTypes.arearange ?
|
||||
function () {
|
||||
var valKey = this.pointValKey;
|
||||
seriesTypes.arearange.prototype.drawDataLabels.call(this);
|
||||
// Arearange drawDataLabels does not reset point.y to high,
|
||||
// but to low after drawing (#4133)
|
||||
this.data.forEach(function (point) {
|
||||
point.y = point[valKey];
|
||||
});
|
||||
} :
|
||||
noop,
|
||||
// Get the width and X offset, either on top of the linked series column or
|
||||
// standalone
|
||||
getColumnMetrics: function () {
|
||||
return ((this.linkedParent && this.linkedParent.columnMetrics) ||
|
||||
seriesTypes.column.prototype.getColumnMetrics.call(this));
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `errorbar` series. If the [type](#series.errorbar.type) option
|
||||
* is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.errorbar
|
||||
* @excluding dataParser, dataURL, stack, stacking, boostThreshold,
|
||||
* boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-more
|
||||
* @apioption series.errorbar
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `errorbar` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,low,high`. If the first value is a string, it is applied as the name
|
||||
* of the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2\. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 10, 2],
|
||||
* [1, 1, 8],
|
||||
* [2, 4, 5]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.errorbar.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* low: 0,
|
||||
* high: 0,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* low: 5,
|
||||
* high: 5,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.arearange.data
|
||||
* @excluding dataLabels, drilldown, marker, states
|
||||
* @product highcharts
|
||||
* @apioption series.errorbar.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,648 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import SVGElement from '../Core/Renderer/SVG/SVGElement.js';
|
||||
import SVGRenderer from '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, defined = U.defined, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, seriesType = U.seriesType, wrap = U.wrap;
|
||||
/**
|
||||
* @typedef {"circlepin"|"flag"|"squarepin"} Highcharts.FlagsShapeValue
|
||||
*/
|
||||
import '../Core/Series/Series.js';
|
||||
import '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
import onSeriesMixin from '../Mixins/OnSeries.js';
|
||||
var noop = H.noop, Renderer = H.Renderer, Series = H.Series, TrackerMixin = H.TrackerMixin, VMLRenderer = H.VMLRenderer, symbols = SVGRenderer.prototype.symbols;
|
||||
/**
|
||||
* The Flags series.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.flags
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('flags', 'column'
|
||||
/**
|
||||
* Flags are used to mark events in stock charts. They can be added on the
|
||||
* timeline, or attached to a specific series.
|
||||
*
|
||||
* @sample stock/demo/flags-general/
|
||||
* Flags on a line series
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @excluding animation, borderColor, borderRadius, borderWidth,
|
||||
* colorByPoint, dataGrouping, pointPadding, pointWidth,
|
||||
* turboThreshold
|
||||
* @product highstock
|
||||
* @optionparent plotOptions.flags
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* In case the flag is placed on a series, on what point key to place
|
||||
* it. Line and columns have one key, `y`. In range or OHLC-type series,
|
||||
* however, the flag can optionally be placed on the `open`, `high`,
|
||||
* `low` or `close` key.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/flags-onkey/
|
||||
* Range series, flag on high
|
||||
*
|
||||
* @type {string}
|
||||
* @default y
|
||||
* @since 4.2.2
|
||||
* @product highstock
|
||||
* @validvalue ["y", "open", "high", "low", "close"]
|
||||
* @apioption plotOptions.flags.onKey
|
||||
*/
|
||||
/**
|
||||
* The id of the series that the flags should be drawn on. If no id
|
||||
* is given, the flags are drawn on the x axis.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/flags/
|
||||
* Flags on series and on x axis
|
||||
*
|
||||
* @type {string}
|
||||
* @product highstock
|
||||
* @apioption plotOptions.flags.onSeries
|
||||
*/
|
||||
pointRange: 0,
|
||||
/**
|
||||
* Whether the flags are allowed to overlap sideways. If `false`, the
|
||||
* flags are moved sideways using an algorithm that seeks to place every
|
||||
* flag as close as possible to its original position.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/flags-allowoverlapx
|
||||
* Allow sideways overlap
|
||||
*
|
||||
* @since 6.0.4
|
||||
*/
|
||||
allowOverlapX: false,
|
||||
/**
|
||||
* The shape of the marker. Can be one of "flag", "circlepin",
|
||||
* "squarepin", or an image of the format `url(/path-to-image.jpg)`.
|
||||
* Individual shapes can also be set for each point.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/flags/
|
||||
* Different shapes
|
||||
*
|
||||
* @type {Highcharts.FlagsShapeValue}
|
||||
* @product highstock
|
||||
*/
|
||||
shape: 'flag',
|
||||
/**
|
||||
* When multiple flags in the same series fall on the same value, this
|
||||
* number determines the vertical offset between them.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/flags-stackdistance/
|
||||
* A greater stack distance
|
||||
*
|
||||
* @product highstock
|
||||
*/
|
||||
stackDistance: 12,
|
||||
/**
|
||||
* Text alignment for the text inside the flag.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @product highstock
|
||||
* @validvalue ["left", "center", "right"]
|
||||
*/
|
||||
textAlign: 'center',
|
||||
/**
|
||||
* Specific tooltip options for flag series. Flag series tooltips are
|
||||
* different from most other types in that a flag doesn't have a data
|
||||
* value, so the tooltip rather displays the `text` option for each
|
||||
* point.
|
||||
*
|
||||
* @extends plotOptions.series.tooltip
|
||||
* @excluding changeDecimals, valueDecimals, valuePrefix, valueSuffix
|
||||
* @product highstock
|
||||
*/
|
||||
tooltip: {
|
||||
pointFormat: '{point.text}<br/>'
|
||||
},
|
||||
threshold: null,
|
||||
/**
|
||||
* The text to display on each flag. This can be defined on series
|
||||
* level, or individually for each point. Defaults to `"A"`.
|
||||
*
|
||||
* @type {string}
|
||||
* @default A
|
||||
* @product highstock
|
||||
* @apioption plotOptions.flags.title
|
||||
*/
|
||||
/**
|
||||
* The y position of the top left corner of the flag relative to either
|
||||
* the series (if onSeries is defined), or the x axis. Defaults to
|
||||
* `-30`.
|
||||
*
|
||||
* @product highstock
|
||||
*/
|
||||
y: -30,
|
||||
/**
|
||||
* Whether to use HTML to render the flag texts. Using HTML allows for
|
||||
* advanced formatting, images and reliable bi-directional text
|
||||
* rendering. Note that exported images won't respect the HTML, and that
|
||||
* HTML won't respect Z-index settings.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 1.3
|
||||
* @product highstock
|
||||
* @apioption plotOptions.flags.useHTML
|
||||
*/
|
||||
/**
|
||||
* Fixed width of the flag's shape. By default, width is autocalculated
|
||||
* according to the flag's title.
|
||||
*
|
||||
* @sample {highstock} stock/demo/flags-shapes/
|
||||
* Flags with fixed width
|
||||
*
|
||||
* @type {number}
|
||||
* @product highstock
|
||||
* @apioption plotOptions.flags.width
|
||||
*/
|
||||
/**
|
||||
* Fixed height of the flag's shape. By default, height is
|
||||
* autocalculated according to the flag's title.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highstock
|
||||
* @apioption plotOptions.flags.height
|
||||
*/
|
||||
/**
|
||||
* The fill color for the flags.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highstock
|
||||
*/
|
||||
fillColor: '#ffffff',
|
||||
/**
|
||||
* The color of the line/border of the flag.
|
||||
*
|
||||
* In styled mode, the stroke is set in the
|
||||
* `.highcharts-flag-series.highcharts-point` rule.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #000000
|
||||
* @product highstock
|
||||
* @apioption plotOptions.flags.lineColor
|
||||
*/
|
||||
/**
|
||||
* The pixel width of the flag's line/border.
|
||||
*
|
||||
* @product highstock
|
||||
*/
|
||||
lineWidth: 1,
|
||||
states: {
|
||||
/**
|
||||
* @extends plotOptions.column.states.hover
|
||||
* @product highstock
|
||||
*/
|
||||
hover: {
|
||||
/**
|
||||
* The color of the line/border of the flag.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highstock
|
||||
*/
|
||||
lineColor: '#000000',
|
||||
/**
|
||||
* The fill or background color of the flag.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highstock
|
||||
*/
|
||||
fillColor: '#ccd6eb'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The text styles of the flag.
|
||||
*
|
||||
* In styled mode, the styles are set in the
|
||||
* `.highcharts-flag-series .highcharts-point` rule.
|
||||
*
|
||||
* @type {Highcharts.CSSObject}
|
||||
* @default {"fontSize": "11px", "fontWeight": "bold"}
|
||||
* @product highstock
|
||||
*/
|
||||
style: {
|
||||
/** @ignore-option */
|
||||
fontSize: '11px',
|
||||
/** @ignore-option */
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends seriesTypes.flags.prototype
|
||||
*/
|
||||
{
|
||||
sorted: false,
|
||||
noSharedTooltip: true,
|
||||
allowDG: false,
|
||||
takeOrdinalPosition: false,
|
||||
trackerGroups: ['markerGroup'],
|
||||
forceCrop: true,
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
/**
|
||||
* Inherit the initialization from base Series.
|
||||
*
|
||||
* @private
|
||||
* @borrows Highcharts.Series#init as Highcharts.seriesTypes.flags#init
|
||||
*/
|
||||
init: Series.prototype.init,
|
||||
/**
|
||||
* Get presentational attributes
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.flags#pointAttribs
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
*
|
||||
* @param {string} [state]
|
||||
*
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
*/
|
||||
pointAttribs: function (point, state) {
|
||||
var options = this.options, color = (point && point.color) || this.color, lineColor = options.lineColor, lineWidth = (point && point.lineWidth), fill = (point && point.fillColor) || options.fillColor;
|
||||
if (state) {
|
||||
fill = options.states[state].fillColor;
|
||||
lineColor = options.states[state].lineColor;
|
||||
lineWidth = options.states[state].lineWidth;
|
||||
}
|
||||
return {
|
||||
fill: fill || color,
|
||||
stroke: lineColor || color,
|
||||
'stroke-width': lineWidth || options.lineWidth || 0
|
||||
};
|
||||
},
|
||||
translate: onSeriesMixin.translate,
|
||||
getPlotBox: onSeriesMixin.getPlotBox,
|
||||
/**
|
||||
* Draw the markers.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.flags#drawPoints
|
||||
* @return {void}
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, points = series.points, chart = series.chart, renderer = chart.renderer, plotX, plotY, inverted = chart.inverted, options = series.options, optionsY = options.y, shape, i, point, graphic, stackIndex, anchorY, attribs, outsideRight, yAxis = series.yAxis, boxesMap = {}, boxes = [], centered;
|
||||
i = points.length;
|
||||
while (i--) {
|
||||
point = points[i];
|
||||
outsideRight =
|
||||
(inverted ? point.plotY : point.plotX) >
|
||||
series.xAxis.len;
|
||||
plotX = point.plotX;
|
||||
stackIndex = point.stackIndex;
|
||||
shape = point.options.shape || options.shape;
|
||||
plotY = point.plotY;
|
||||
if (typeof plotY !== 'undefined') {
|
||||
plotY = point.plotY + optionsY -
|
||||
(typeof stackIndex !== 'undefined' &&
|
||||
(stackIndex * options.stackDistance));
|
||||
}
|
||||
// skip connectors for higher level stacked points
|
||||
point.anchorX = stackIndex ? void 0 : point.plotX;
|
||||
anchorY = stackIndex ? void 0 : point.plotY;
|
||||
centered = shape !== 'flag';
|
||||
graphic = point.graphic;
|
||||
// Only draw the point if y is defined and the flag is within
|
||||
// the visible area
|
||||
if (typeof plotY !== 'undefined' &&
|
||||
plotX >= 0 &&
|
||||
!outsideRight) {
|
||||
// Create the flag
|
||||
if (!graphic) {
|
||||
graphic = point.graphic = renderer.label('', null, null, shape, null, null, options.useHTML);
|
||||
if (!chart.styledMode) {
|
||||
graphic
|
||||
.attr(series.pointAttribs(point))
|
||||
.css(merge(options.style, point.style));
|
||||
}
|
||||
graphic.attr({
|
||||
align: centered ? 'center' : 'left',
|
||||
width: options.width,
|
||||
height: options.height,
|
||||
'text-align': options.textAlign
|
||||
})
|
||||
.addClass('highcharts-point')
|
||||
.add(series.markerGroup);
|
||||
// Add reference to the point for tracker (#6303)
|
||||
if (point.graphic.div) {
|
||||
point.graphic.div.point = point;
|
||||
}
|
||||
if (!chart.styledMode) {
|
||||
graphic.shadow(options.shadow);
|
||||
}
|
||||
graphic.isNew = true;
|
||||
}
|
||||
if (plotX > 0) { // #3119
|
||||
plotX -= graphic.strokeWidth() % 2; // #4285
|
||||
}
|
||||
// Plant the flag
|
||||
attribs = {
|
||||
y: plotY,
|
||||
anchorY: anchorY
|
||||
};
|
||||
if (options.allowOverlapX) {
|
||||
attribs.x = plotX;
|
||||
attribs.anchorX = point.anchorX;
|
||||
}
|
||||
graphic.attr({
|
||||
text: point.options.title || options.title || 'A'
|
||||
})[graphic.isNew ? 'attr' : 'animate'](attribs);
|
||||
// Rig for the distribute function
|
||||
if (!options.allowOverlapX) {
|
||||
if (!boxesMap[point.plotX]) {
|
||||
boxesMap[point.plotX] = {
|
||||
align: centered ? 0.5 : 0,
|
||||
size: graphic.width,
|
||||
target: plotX,
|
||||
anchorX: plotX
|
||||
};
|
||||
}
|
||||
else {
|
||||
boxesMap[point.plotX].size = Math.max(boxesMap[point.plotX].size, graphic.width);
|
||||
}
|
||||
}
|
||||
// Set the tooltip anchor position
|
||||
point.tooltipPos = [
|
||||
plotX,
|
||||
plotY + yAxis.pos - chart.plotTop
|
||||
]; // #6327
|
||||
}
|
||||
else if (graphic) {
|
||||
point.graphic = graphic.destroy();
|
||||
}
|
||||
}
|
||||
// Handle X-dimension overlapping
|
||||
if (!options.allowOverlapX) {
|
||||
objectEach(boxesMap, function (box) {
|
||||
box.plotX = box.anchorX;
|
||||
boxes.push(box);
|
||||
});
|
||||
H.distribute(boxes, inverted ? yAxis.len : this.xAxis.len, 100);
|
||||
points.forEach(function (point) {
|
||||
var box = point.graphic && boxesMap[point.plotX];
|
||||
if (box) {
|
||||
point.graphic[point.graphic.isNew ? 'attr' : 'animate']({
|
||||
x: box.pos + box.align * box.size,
|
||||
anchorX: point.anchorX
|
||||
});
|
||||
// Hide flag when its box position is not specified
|
||||
// (#8573, #9299)
|
||||
if (!defined(box.pos)) {
|
||||
point.graphic.attr({
|
||||
x: -9999,
|
||||
anchorX: -9999
|
||||
});
|
||||
point.graphic.isNew = true;
|
||||
}
|
||||
else {
|
||||
point.graphic.isNew = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Can be a mix of SVG and HTML and we need events for both (#6303)
|
||||
if (options.useHTML) {
|
||||
wrap(series.markerGroup, 'on', function (proceed) {
|
||||
return SVGElement.prototype.on.apply(
|
||||
// for HTML
|
||||
proceed.apply(this, [].slice.call(arguments, 1)),
|
||||
// and for SVG
|
||||
[].slice.call(arguments, 1));
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Extend the column trackers with listeners to expand and contract
|
||||
* stacks.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.flags#drawTracker
|
||||
* @return {void}
|
||||
*/
|
||||
drawTracker: function () {
|
||||
var series = this, points = series.points;
|
||||
TrackerMixin.drawTrackerPoint.apply(this);
|
||||
/* *
|
||||
* Bring each stacked flag up on mouse over, this allows readability
|
||||
* of vertically stacked elements as well as tight points on the x
|
||||
* axis. #1924.
|
||||
*/
|
||||
points.forEach(function (point) {
|
||||
var graphic = point.graphic;
|
||||
if (graphic) {
|
||||
addEvent(graphic.element, 'mouseover', function () {
|
||||
// Raise this point
|
||||
if (point.stackIndex > 0 &&
|
||||
!point.raised) {
|
||||
point._y = graphic.y;
|
||||
graphic.attr({
|
||||
y: point._y - 8
|
||||
});
|
||||
point.raised = true;
|
||||
}
|
||||
// Revert other raised points
|
||||
points.forEach(function (otherPoint) {
|
||||
if (otherPoint !== point &&
|
||||
otherPoint.raised &&
|
||||
otherPoint.graphic) {
|
||||
otherPoint.graphic.attr({
|
||||
y: otherPoint._y
|
||||
});
|
||||
otherPoint.raised = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Disable animation, but keep clipping (#8546).
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.flags#animate
|
||||
* @param {boolean} [init]
|
||||
* @return {void}
|
||||
*/
|
||||
animate: function (init) {
|
||||
if (init) {
|
||||
this.setClip();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.flags#setClip
|
||||
* @return {void}
|
||||
*/
|
||||
setClip: function () {
|
||||
Series.prototype.setClip.apply(this, arguments);
|
||||
if (this.options.clip !== false && this.sharedClipKey) {
|
||||
this.markerGroup
|
||||
.clip(this.chart[this.sharedClipKey]);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.flags#buildKDTree
|
||||
*/
|
||||
buildKDTree: noop,
|
||||
/**
|
||||
* Don't invert the flag marker group (#4960).
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.flags#invertGroups
|
||||
*/
|
||||
invertGroups: noop
|
||||
/* eslint-enable no-invalid-this, valid-jsdoc */
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.seriesTypes.flag.prototype.pointClass.prototype
|
||||
*/
|
||||
{
|
||||
isValid: function () {
|
||||
// #9233 - Prevent from treating flags as null points (even if
|
||||
// they have no y values defined).
|
||||
return isNumber(this.y) || typeof this.y === 'undefined';
|
||||
}
|
||||
});
|
||||
// create the flag icon with anchor
|
||||
symbols.flag = function (x, y, w, h, options) {
|
||||
var anchorX = (options && options.anchorX) || x, anchorY = (options && options.anchorY) || y;
|
||||
// To do: unwanted any cast because symbols.circle has wrong type, it
|
||||
// actually returns an SVGPathArray
|
||||
var path = symbols.circle(anchorX - 1, anchorY - 1, 2, 2);
|
||||
path.push(['M', anchorX, anchorY], ['L', x, y + h], ['L', x, y], ['L', x + w, y], ['L', x + w, y + h], ['L', x, y + h], ['Z']);
|
||||
return path;
|
||||
};
|
||||
/**
|
||||
* Create the circlepin and squarepin icons with anchor.
|
||||
* @private
|
||||
* @param {string} shape - circle or square
|
||||
* @return {void}
|
||||
*/
|
||||
function createPinSymbol(shape) {
|
||||
symbols[shape + 'pin'] = function (x, y, w, h, options) {
|
||||
var anchorX = options && options.anchorX, anchorY = options && options.anchorY, path;
|
||||
// For single-letter flags, make sure circular flags are not taller
|
||||
// than their width
|
||||
if (shape === 'circle' && h > w) {
|
||||
x -= Math.round((h - w) / 2);
|
||||
w = h;
|
||||
}
|
||||
path = (symbols[shape])(x, y, w, h);
|
||||
if (anchorX && anchorY) {
|
||||
/**
|
||||
* If the label is below the anchor, draw the connecting line from
|
||||
* the top edge of the label, otherwise start drawing from the
|
||||
* bottom edge
|
||||
*/
|
||||
var labelX = anchorX;
|
||||
if (shape === 'circle') {
|
||||
labelX = x + w / 2;
|
||||
}
|
||||
else {
|
||||
var startSeg = path[0];
|
||||
var endSeg = path[1];
|
||||
if (startSeg[0] === 'M' && endSeg[0] === 'L') {
|
||||
labelX = (startSeg[1] + endSeg[1]) / 2;
|
||||
}
|
||||
}
|
||||
var labelY = (y > anchorY) ? y : y + h;
|
||||
path.push([
|
||||
'M',
|
||||
labelX,
|
||||
labelY
|
||||
], [
|
||||
'L',
|
||||
anchorX,
|
||||
anchorY
|
||||
]);
|
||||
path = path.concat(symbols.circle(anchorX - 1, anchorY - 1, 2, 2));
|
||||
}
|
||||
return path;
|
||||
};
|
||||
}
|
||||
createPinSymbol('circle');
|
||||
createPinSymbol('square');
|
||||
/**
|
||||
* The symbol callbacks are generated on the SVGRenderer object in all browsers.
|
||||
* Even VML browsers need this in order to generate shapes in export. Now share
|
||||
* them with the VMLRenderer.
|
||||
*/
|
||||
if (Renderer === VMLRenderer) {
|
||||
['circlepin', 'flag', 'squarepin'].forEach(function (shape) {
|
||||
VMLRenderer.prototype.symbols[shape] = symbols[shape];
|
||||
});
|
||||
}
|
||||
/**
|
||||
* A `flags` series. If the [type](#series.flags.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.flags
|
||||
* @excluding animation, borderColor, borderRadius, borderWidth, colorByPoint,
|
||||
* connectNulls, dashStyle, dataGrouping, dataParser, dataURL,
|
||||
* gapSize, gapUnit, linecap, lineWidth, marker, pointPadding,
|
||||
* pointWidth, step, turboThreshold, useOhlcData
|
||||
* @product highstock
|
||||
* @apioption series.flags
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `flags` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. 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.flags.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* title: "A",
|
||||
* text: "First event"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* title: "B",
|
||||
* text: "Second event"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<*>}
|
||||
* @extends series.line.data
|
||||
* @excluding dataLabels, marker, name, y
|
||||
* @product highstock
|
||||
* @apioption series.flags.data
|
||||
*/
|
||||
/**
|
||||
* The fill color of an individual flag. By default it inherits from
|
||||
* the series color.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highstock
|
||||
* @apioption series.flags.data.fillColor
|
||||
*/
|
||||
/**
|
||||
* The longer text to be shown in the flag's tooltip.
|
||||
*
|
||||
* @type {string}
|
||||
* @product highstock
|
||||
* @apioption series.flags.data.text
|
||||
*/
|
||||
/**
|
||||
* The short text to be shown on the flag.
|
||||
*
|
||||
* @type {string}
|
||||
* @product highstock
|
||||
* @apioption series.flags.data.title
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,703 @@
|
||||
/* *
|
||||
*
|
||||
* Highcharts funnel3d series module
|
||||
*
|
||||
* (c) 2010-2020 Highsoft AS
|
||||
*
|
||||
* Author: Kacper Madej
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Math3D from '../Extensions/Math3D.js';
|
||||
var perspective = Math3D.perspective;
|
||||
import Color from '../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import U from '../Core/Utilities.js';
|
||||
var error = U.error, extend = U.extend, merge = U.merge, pick = U.pick, relativeLength = U.relativeLength, seriesType = U.seriesType;
|
||||
import './ColumnSeries.js';
|
||||
import '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
var charts = H.charts, seriesTypes = H.seriesTypes,
|
||||
// Use H.Renderer instead of SVGRenderer for VML support.
|
||||
RendererProto = H.Renderer.prototype,
|
||||
//
|
||||
cuboidPath = RendererProto.cuboidPath, funnel3dMethods;
|
||||
/**
|
||||
* The funnel3d series type.
|
||||
*
|
||||
* @constructor seriesTypes.funnel3d
|
||||
* @augments seriesTypes.column
|
||||
* @requires highcharts-3d
|
||||
* @requires modules/cylinder
|
||||
* @requires modules/funnel3d
|
||||
*/
|
||||
seriesType('funnel3d', 'column',
|
||||
/**
|
||||
* A funnel3d is a 3d version of funnel series type. Funnel charts are
|
||||
* a type of chart often used to visualize stages in a sales project,
|
||||
* where the top are the initial stages with the most clients.
|
||||
*
|
||||
* It requires that the `highcharts-3d.js`, `cylinder.js` and
|
||||
* `funnel3d.js` module are loaded.
|
||||
*
|
||||
* @sample highcharts/demo/funnel3d/
|
||||
* Funnel3d
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @excluding allAreas, boostThreshold, colorAxis, compare, compareBase,
|
||||
* dataSorting, boostBlending
|
||||
* @product highcharts
|
||||
* @since 7.1.0
|
||||
* @requires highcharts-3d
|
||||
* @requires modules/cylinder
|
||||
* @requires modules/funnel3d
|
||||
* @optionparent plotOptions.funnel3d
|
||||
*/
|
||||
{
|
||||
/** @ignore-option */
|
||||
center: ['50%', '50%'],
|
||||
/**
|
||||
* The max width of the series compared to the width of the plot area,
|
||||
* or the pixel width if it is a number.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @sample {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
|
||||
* @product highcharts
|
||||
*/
|
||||
width: '90%',
|
||||
/**
|
||||
* The width of the neck, the lower part of the funnel. A number defines
|
||||
* pixel width, a percentage string defines a percentage of the plot
|
||||
* area width.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @sample {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
|
||||
* @product highcharts
|
||||
*/
|
||||
neckWidth: '30%',
|
||||
/**
|
||||
* The height of the series. If it is a number it defines
|
||||
* the pixel height, if it is a percentage string it is the percentage
|
||||
* of the plot area height.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @sample {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
|
||||
* @product highcharts
|
||||
*/
|
||||
height: '100%',
|
||||
/**
|
||||
* The height of the neck, the lower part of the funnel. A number
|
||||
* defines pixel width, a percentage string defines a percentage
|
||||
* of the plot area height.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @sample {highcharts} highcharts/demo/funnel3d/ Funnel3d demo
|
||||
* @product highcharts
|
||||
*/
|
||||
neckHeight: '25%',
|
||||
/**
|
||||
* A reversed funnel has the widest area down. A reversed funnel with
|
||||
* no neck width and neck height is a pyramid.
|
||||
*
|
||||
* @product highcharts
|
||||
*/
|
||||
reversed: false,
|
||||
/**
|
||||
* By deafult sides fill is set to a gradient through this option being
|
||||
* set to `true`. Set to `false` to get solid color for the sides.
|
||||
*
|
||||
* @product highcharts
|
||||
*/
|
||||
gradientForSides: true,
|
||||
animation: false,
|
||||
edgeWidth: 0,
|
||||
colorByPoint: true,
|
||||
showInLegend: false,
|
||||
dataLabels: {
|
||||
align: 'right',
|
||||
crop: false,
|
||||
inside: false,
|
||||
overflow: 'allow'
|
||||
}
|
||||
}, {
|
||||
// Override default axis options with series required options for axes
|
||||
bindAxes: function () {
|
||||
H.Series.prototype.bindAxes.apply(this, arguments);
|
||||
extend(this.xAxis.options, {
|
||||
gridLineWidth: 0,
|
||||
lineWidth: 0,
|
||||
title: null,
|
||||
tickPositions: []
|
||||
});
|
||||
extend(this.yAxis.options, {
|
||||
gridLineWidth: 0,
|
||||
title: null,
|
||||
labels: {
|
||||
enabled: false
|
||||
}
|
||||
});
|
||||
},
|
||||
translate3dShapes: H.noop,
|
||||
translate: function () {
|
||||
H.Series.prototype.translate.apply(this, arguments);
|
||||
var sum = 0, series = this, chart = series.chart, options = series.options, reversed = options.reversed, ignoreHiddenPoint = options.ignoreHiddenPoint, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, cumulative = 0, // start at top
|
||||
center = options.center, centerX = relativeLength(center[0], plotWidth), centerY = relativeLength(center[1], plotHeight), width = relativeLength(options.width, plotWidth), tempWidth, getWidthAt, height = relativeLength(options.height, plotHeight), neckWidth = relativeLength(options.neckWidth, plotWidth), neckHeight = relativeLength(options.neckHeight, plotHeight), neckY = (centerY - height / 2) + height - neckHeight, data = series.data, fraction, tooltipPos,
|
||||
//
|
||||
y1, y3, y5,
|
||||
//
|
||||
h, shapeArgs;
|
||||
// Return the width at a specific y coordinate
|
||||
series.getWidthAt = getWidthAt = function (y) {
|
||||
var top = (centerY - height / 2);
|
||||
return (y > neckY || height === neckHeight) ?
|
||||
neckWidth :
|
||||
neckWidth + (width - neckWidth) *
|
||||
(1 - (y - top) / (height - neckHeight));
|
||||
};
|
||||
// Expose
|
||||
series.center = [centerX, centerY, height];
|
||||
series.centerX = centerX;
|
||||
/*
|
||||
* Individual point coordinate naming:
|
||||
*
|
||||
* _________centerX,y1________
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* \ /
|
||||
* ___centerX,y3___
|
||||
*
|
||||
* Additional for the base of the neck:
|
||||
*
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* ___centerX,y5___
|
||||
*/
|
||||
// get the total sum
|
||||
data.forEach(function (point) {
|
||||
if (!ignoreHiddenPoint || point.visible !== false) {
|
||||
sum += point.y;
|
||||
}
|
||||
});
|
||||
data.forEach(function (point) {
|
||||
// set start and end positions
|
||||
y5 = null;
|
||||
fraction = sum ? point.y / sum : 0;
|
||||
y1 = centerY - height / 2 + cumulative * height;
|
||||
y3 = y1 + fraction * height;
|
||||
tempWidth = getWidthAt(y1);
|
||||
h = y3 - y1;
|
||||
shapeArgs = {
|
||||
// for fill setter
|
||||
gradientForSides: pick(point.options.gradientForSides, options.gradientForSides),
|
||||
x: centerX,
|
||||
y: y1,
|
||||
height: h,
|
||||
width: tempWidth,
|
||||
z: 1,
|
||||
top: {
|
||||
width: tempWidth
|
||||
}
|
||||
};
|
||||
tempWidth = getWidthAt(y3);
|
||||
shapeArgs.bottom = {
|
||||
fraction: fraction,
|
||||
width: tempWidth
|
||||
};
|
||||
// the entire point is within the neck
|
||||
if (y1 >= neckY) {
|
||||
shapeArgs.isCylinder = true;
|
||||
}
|
||||
else if (y3 > neckY) {
|
||||
// the base of the neck
|
||||
y5 = y3;
|
||||
tempWidth = getWidthAt(neckY);
|
||||
y3 = neckY;
|
||||
shapeArgs.bottom.width = tempWidth;
|
||||
shapeArgs.middle = {
|
||||
fraction: h ? (neckY - y1) / h : 0,
|
||||
width: tempWidth
|
||||
};
|
||||
}
|
||||
if (reversed) {
|
||||
shapeArgs.y = y1 = centerY + height / 2 -
|
||||
(cumulative + fraction) * height;
|
||||
if (shapeArgs.middle) {
|
||||
shapeArgs.middle.fraction = 1 -
|
||||
(h ? shapeArgs.middle.fraction : 0);
|
||||
}
|
||||
tempWidth = shapeArgs.width;
|
||||
shapeArgs.width = shapeArgs.bottom.width;
|
||||
shapeArgs.bottom.width = tempWidth;
|
||||
}
|
||||
point.shapeArgs = extend(point.shapeArgs, shapeArgs);
|
||||
// for tooltips and data labels context
|
||||
point.percentage = fraction * 100;
|
||||
point.plotX = centerX;
|
||||
if (reversed) {
|
||||
point.plotY = centerY + height / 2 -
|
||||
(cumulative + fraction / 2) * height;
|
||||
}
|
||||
else {
|
||||
point.plotY = (y1 + (y5 || y3)) / 2;
|
||||
}
|
||||
// Placement of tooltips and data labels in 3D
|
||||
tooltipPos = perspective([{
|
||||
x: centerX,
|
||||
y: point.plotY,
|
||||
z: reversed ?
|
||||
-(width - getWidthAt(point.plotY)) / 2 :
|
||||
-(getWidthAt(point.plotY)) / 2
|
||||
}], chart, true)[0];
|
||||
point.tooltipPos = [tooltipPos.x, tooltipPos.y];
|
||||
// base to be used when alignment options are known
|
||||
point.dlBoxRaw = {
|
||||
x: centerX,
|
||||
width: getWidthAt(point.plotY),
|
||||
y: y1,
|
||||
bottom: shapeArgs.height,
|
||||
fullWidth: width
|
||||
};
|
||||
if (!ignoreHiddenPoint || point.visible !== false) {
|
||||
cumulative += fraction;
|
||||
}
|
||||
});
|
||||
},
|
||||
alignDataLabel: function (point, dataLabel, options) {
|
||||
var series = this, dlBoxRaw = point.dlBoxRaw, inverted = series.chart.inverted, below = point.plotY > pick(series.translatedThreshold, series.yAxis.len), inside = pick(options.inside, !!series.options.stacking), dlBox = {
|
||||
x: dlBoxRaw.x,
|
||||
y: dlBoxRaw.y,
|
||||
height: 0
|
||||
};
|
||||
options.align = pick(options.align, !inverted || inside ? 'center' : below ? 'right' : 'left');
|
||||
options.verticalAlign = pick(options.verticalAlign, inverted || inside ? 'middle' : below ? 'top' : 'bottom');
|
||||
if (options.verticalAlign !== 'top') {
|
||||
dlBox.y += dlBoxRaw.bottom /
|
||||
(options.verticalAlign === 'bottom' ? 1 : 2);
|
||||
}
|
||||
dlBox.width = series.getWidthAt(dlBox.y);
|
||||
if (series.options.reversed) {
|
||||
dlBox.width = dlBoxRaw.fullWidth - dlBox.width;
|
||||
}
|
||||
if (inside) {
|
||||
dlBox.x -= dlBox.width / 2;
|
||||
}
|
||||
else {
|
||||
// swap for inside
|
||||
if (options.align === 'left') {
|
||||
options.align = 'right';
|
||||
dlBox.x -= dlBox.width * 1.5;
|
||||
}
|
||||
else if (options.align === 'right') {
|
||||
options.align = 'left';
|
||||
dlBox.x += dlBox.width / 2;
|
||||
}
|
||||
else {
|
||||
dlBox.x -= dlBox.width / 2;
|
||||
}
|
||||
}
|
||||
point.dlBox = dlBox;
|
||||
seriesTypes.column.prototype.alignDataLabel.apply(series, arguments);
|
||||
}
|
||||
}, /** @lends seriesTypes.funnel3d.prototype.pointClass.prototype */ {
|
||||
shapeType: 'funnel3d',
|
||||
hasNewShapeType: H
|
||||
.seriesTypes.column.prototype
|
||||
.pointClass.prototype
|
||||
.hasNewShapeType
|
||||
});
|
||||
/**
|
||||
* A `funnel3d` series. If the [type](#series.funnel3d.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/funnel3d/
|
||||
* Funnel3d demo
|
||||
*
|
||||
* @since 7.1.0
|
||||
* @extends series.funnel,plotOptions.funnel3d
|
||||
* @excluding allAreas,boostThreshold,colorAxis,compare,compareBase
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @requires modules/cylinder
|
||||
* @requires modules/funnel3d
|
||||
* @apioption series.funnel3d
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `funnel3d` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values
|
||||
* will be interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from `pointStart`
|
||||
* and `pointInterval` given in the series options. If the axis has
|
||||
* categories, these will be used. Example:
|
||||
*
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.funnel3d.turboThreshold),
|
||||
* this option is not available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 2,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<number>|*>}
|
||||
* @extends series.column.data
|
||||
* @product highcharts
|
||||
* @apioption series.funnel3d.data
|
||||
*/
|
||||
/**
|
||||
* By deafult sides fill is set to a gradient through this option being
|
||||
* set to `true`. Set to `false` to get solid color for the sides.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @product highcharts
|
||||
* @apioption series.funnel3d.data.gradientForSides
|
||||
*/
|
||||
funnel3dMethods = merge(RendererProto.elements3d.cuboid, {
|
||||
parts: [
|
||||
'top', 'bottom',
|
||||
'frontUpper', 'backUpper',
|
||||
'frontLower', 'backLower',
|
||||
'rightUpper', 'rightLower'
|
||||
],
|
||||
mainParts: ['top', 'bottom'],
|
||||
sideGroups: [
|
||||
'upperGroup', 'lowerGroup'
|
||||
],
|
||||
sideParts: {
|
||||
upperGroup: ['frontUpper', 'backUpper', 'rightUpper'],
|
||||
lowerGroup: ['frontLower', 'backLower', 'rightLower']
|
||||
},
|
||||
pathType: 'funnel3d',
|
||||
// override opacity and color setters to control opacity
|
||||
opacitySetter: function (opacity) {
|
||||
var funnel3d = this, parts = funnel3d.parts, chart = H.charts[funnel3d.renderer.chartIndex], filterId = 'group-opacity-' + opacity + '-' + chart.index;
|
||||
// use default for top and bottom
|
||||
funnel3d.parts = funnel3d.mainParts;
|
||||
funnel3d.singleSetterForParts('opacity', opacity);
|
||||
// restore
|
||||
funnel3d.parts = parts;
|
||||
if (!chart.renderer.filterId) {
|
||||
chart.renderer.definition({
|
||||
tagName: 'filter',
|
||||
id: filterId,
|
||||
children: [{
|
||||
tagName: 'feComponentTransfer',
|
||||
children: [{
|
||||
tagName: 'feFuncA',
|
||||
type: 'table',
|
||||
tableValues: '0 ' + opacity
|
||||
}]
|
||||
}]
|
||||
});
|
||||
funnel3d.sideGroups.forEach(function (groupName) {
|
||||
funnel3d[groupName].attr({
|
||||
filter: 'url(#' + filterId + ')'
|
||||
});
|
||||
});
|
||||
// styled mode
|
||||
if (funnel3d.renderer.styledMode) {
|
||||
chart.renderer.definition({
|
||||
tagName: 'style',
|
||||
textContent: '.highcharts-' + filterId +
|
||||
' {filter:url(#' + filterId + ')}'
|
||||
});
|
||||
funnel3d.sideGroups.forEach(function (group) {
|
||||
group.addClass('highcharts-' + filterId);
|
||||
});
|
||||
}
|
||||
}
|
||||
return funnel3d;
|
||||
},
|
||||
fillSetter: function (fill) {
|
||||
// extract alpha channel to use the opacitySetter
|
||||
var funnel3d = this, fillColor = color(fill), alpha = fillColor.rgba[3], partsWithColor = {
|
||||
// standard color for top and bottom
|
||||
top: color(fill).brighten(0.1).get(),
|
||||
bottom: color(fill).brighten(-0.2).get()
|
||||
};
|
||||
if (alpha < 1) {
|
||||
fillColor.rgba[3] = 1;
|
||||
fillColor = fillColor.get('rgb');
|
||||
// set opacity through the opacitySetter
|
||||
funnel3d.attr({
|
||||
opacity: alpha
|
||||
});
|
||||
}
|
||||
else {
|
||||
// use default for full opacity
|
||||
fillColor = fill;
|
||||
}
|
||||
// add gradient for sides
|
||||
if (!fillColor.linearGradient &&
|
||||
!fillColor.radialGradient &&
|
||||
funnel3d.gradientForSides) {
|
||||
fillColor = {
|
||||
linearGradient: { x1: 0, x2: 1, y1: 1, y2: 1 },
|
||||
stops: [
|
||||
[0, color(fill).brighten(-0.2).get()],
|
||||
[0.5, fill],
|
||||
[1, color(fill).brighten(-0.2).get()]
|
||||
]
|
||||
};
|
||||
}
|
||||
// gradient support
|
||||
if (fillColor.linearGradient) {
|
||||
// color in steps, as each gradient will generate a key
|
||||
funnel3d.sideGroups.forEach(function (sideGroupName) {
|
||||
var box = funnel3d[sideGroupName].gradientBox, gradient = fillColor.linearGradient, alteredGradient = merge(fillColor, {
|
||||
linearGradient: {
|
||||
x1: box.x + gradient.x1 * box.width,
|
||||
y1: box.y + gradient.y1 * box.height,
|
||||
x2: box.x + gradient.x2 * box.width,
|
||||
y2: box.y + gradient.y2 * box.height
|
||||
}
|
||||
});
|
||||
funnel3d.sideParts[sideGroupName].forEach(function (partName) {
|
||||
partsWithColor[partName] = alteredGradient;
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
merge(true, partsWithColor, {
|
||||
frontUpper: fillColor,
|
||||
backUpper: fillColor,
|
||||
rightUpper: fillColor,
|
||||
frontLower: fillColor,
|
||||
backLower: fillColor,
|
||||
rightLower: fillColor
|
||||
});
|
||||
if (fillColor.radialGradient) {
|
||||
funnel3d.sideGroups.forEach(function (sideGroupName) {
|
||||
var gradBox = funnel3d[sideGroupName].gradientBox, centerX = gradBox.x + gradBox.width / 2, centerY = gradBox.y + gradBox.height / 2, diameter = Math.min(gradBox.width, gradBox.height);
|
||||
funnel3d.sideParts[sideGroupName].forEach(function (partName) {
|
||||
funnel3d[partName].setRadialReference([
|
||||
centerX, centerY, diameter
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
funnel3d.singleSetterForParts('fill', null, partsWithColor);
|
||||
// fill for animation getter (#6776)
|
||||
funnel3d.color = funnel3d.fill = fill;
|
||||
// change gradientUnits to userSpaceOnUse for linearGradient
|
||||
if (fillColor.linearGradient) {
|
||||
[funnel3d.frontLower, funnel3d.frontUpper].forEach(function (part) {
|
||||
var elem = part.element, grad = elem && funnel3d.renderer.gradients[elem.gradient];
|
||||
if (grad && grad.attr('gradientUnits') !== 'userSpaceOnUse') {
|
||||
grad.attr({
|
||||
gradientUnits: 'userSpaceOnUse'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
return funnel3d;
|
||||
},
|
||||
adjustForGradient: function () {
|
||||
var funnel3d = this, bbox;
|
||||
funnel3d.sideGroups.forEach(function (sideGroupName) {
|
||||
// use common extremes for groups for matching gradients
|
||||
var topLeftEdge = {
|
||||
x: Number.MAX_VALUE,
|
||||
y: Number.MAX_VALUE
|
||||
}, bottomRightEdge = {
|
||||
x: -Number.MAX_VALUE,
|
||||
y: -Number.MAX_VALUE
|
||||
};
|
||||
// get extremes
|
||||
funnel3d.sideParts[sideGroupName].forEach(function (partName) {
|
||||
var part = funnel3d[partName];
|
||||
bbox = part.getBBox(true);
|
||||
topLeftEdge = {
|
||||
x: Math.min(topLeftEdge.x, bbox.x),
|
||||
y: Math.min(topLeftEdge.y, bbox.y)
|
||||
};
|
||||
bottomRightEdge = {
|
||||
x: Math.max(bottomRightEdge.x, bbox.x + bbox.width),
|
||||
y: Math.max(bottomRightEdge.y, bbox.y + bbox.height)
|
||||
};
|
||||
});
|
||||
// store for color fillSetter
|
||||
funnel3d[sideGroupName].gradientBox = {
|
||||
x: topLeftEdge.x,
|
||||
width: bottomRightEdge.x - topLeftEdge.x,
|
||||
y: topLeftEdge.y,
|
||||
height: bottomRightEdge.y - topLeftEdge.y
|
||||
};
|
||||
});
|
||||
},
|
||||
zIndexSetter: function () {
|
||||
// this.added won't work, because zIndex is set after the prop is set,
|
||||
// but before the graphic is really added
|
||||
if (this.finishedOnAdd) {
|
||||
this.adjustForGradient();
|
||||
}
|
||||
// run default
|
||||
return this.renderer.Element.prototype.zIndexSetter.apply(this, arguments);
|
||||
},
|
||||
onAdd: function () {
|
||||
this.adjustForGradient();
|
||||
this.finishedOnAdd = true;
|
||||
}
|
||||
});
|
||||
RendererProto.elements3d.funnel3d = funnel3dMethods;
|
||||
RendererProto.funnel3d = function (shapeArgs) {
|
||||
var renderer = this, funnel3d = renderer.element3d('funnel3d', shapeArgs), styledMode = renderer.styledMode,
|
||||
// hide stroke for Firefox
|
||||
strokeAttrs = {
|
||||
'stroke-width': 1,
|
||||
stroke: 'none'
|
||||
};
|
||||
// create groups for sides for oppacity setter
|
||||
funnel3d.upperGroup = renderer.g('funnel3d-upper-group').attr({
|
||||
zIndex: funnel3d.frontUpper.zIndex
|
||||
}).add(funnel3d);
|
||||
[
|
||||
funnel3d.frontUpper,
|
||||
funnel3d.backUpper,
|
||||
funnel3d.rightUpper
|
||||
].forEach(function (upperElem) {
|
||||
if (!styledMode) {
|
||||
upperElem.attr(strokeAttrs);
|
||||
}
|
||||
upperElem.add(funnel3d.upperGroup);
|
||||
});
|
||||
funnel3d.lowerGroup = renderer.g('funnel3d-lower-group').attr({
|
||||
zIndex: funnel3d.frontLower.zIndex
|
||||
}).add(funnel3d);
|
||||
[
|
||||
funnel3d.frontLower,
|
||||
funnel3d.backLower,
|
||||
funnel3d.rightLower
|
||||
].forEach(function (lowerElem) {
|
||||
if (!styledMode) {
|
||||
lowerElem.attr(strokeAttrs);
|
||||
}
|
||||
lowerElem.add(funnel3d.lowerGroup);
|
||||
});
|
||||
funnel3d.gradientForSides = shapeArgs.gradientForSides;
|
||||
return funnel3d;
|
||||
};
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Generates paths and zIndexes.
|
||||
* @private
|
||||
*/
|
||||
RendererProto.funnel3dPath = function (shapeArgs) {
|
||||
// Check getCylinderEnd for better error message if
|
||||
// the cylinder module is missing
|
||||
if (!this.getCylinderEnd) {
|
||||
error('A required Highcharts module is missing: cylinder.js', true, charts[this.chartIndex]);
|
||||
}
|
||||
var renderer = this, chart = charts[renderer.chartIndex],
|
||||
// adjust angles for visible edges
|
||||
// based on alpha, selected through visual tests
|
||||
alphaCorrection = shapeArgs.alphaCorrection = 90 -
|
||||
Math.abs((chart.options.chart.options3d.alpha % 180) - 90),
|
||||
// set zIndexes of parts based on cubiod logic, for consistency
|
||||
cuboidData = cuboidPath.call(renderer, merge(shapeArgs, {
|
||||
depth: shapeArgs.width,
|
||||
width: (shapeArgs.width + shapeArgs.bottom.width) / 2
|
||||
})), isTopFirst = cuboidData.isTop, isFrontFirst = !cuboidData.isFront, hasMiddle = !!shapeArgs.middle,
|
||||
//
|
||||
top = renderer.getCylinderEnd(chart, merge(shapeArgs, {
|
||||
x: shapeArgs.x - shapeArgs.width / 2,
|
||||
z: shapeArgs.z - shapeArgs.width / 2,
|
||||
alphaCorrection: alphaCorrection
|
||||
})), bottomWidth = shapeArgs.bottom.width, bottomArgs = merge(shapeArgs, {
|
||||
width: bottomWidth,
|
||||
x: shapeArgs.x - bottomWidth / 2,
|
||||
z: shapeArgs.z - bottomWidth / 2,
|
||||
alphaCorrection: alphaCorrection
|
||||
}), bottom = renderer.getCylinderEnd(chart, bottomArgs, true),
|
||||
//
|
||||
middleWidth = bottomWidth, middleTopArgs = bottomArgs, middleTop = bottom, middleBottom = bottom, ret,
|
||||
// masking for cylinders or a missing part of a side shape
|
||||
useAlphaCorrection;
|
||||
if (hasMiddle) {
|
||||
middleWidth = shapeArgs.middle.width;
|
||||
middleTopArgs = merge(shapeArgs, {
|
||||
y: shapeArgs.y + shapeArgs.middle.fraction * shapeArgs.height,
|
||||
width: middleWidth,
|
||||
x: shapeArgs.x - middleWidth / 2,
|
||||
z: shapeArgs.z - middleWidth / 2
|
||||
});
|
||||
middleTop = renderer.getCylinderEnd(chart, middleTopArgs, false);
|
||||
middleBottom = renderer.getCylinderEnd(chart, middleTopArgs, false);
|
||||
}
|
||||
ret = {
|
||||
top: top,
|
||||
bottom: bottom,
|
||||
frontUpper: renderer.getCylinderFront(top, middleTop),
|
||||
zIndexes: {
|
||||
group: cuboidData.zIndexes.group,
|
||||
top: isTopFirst !== 0 ? 0 : 3,
|
||||
bottom: isTopFirst !== 1 ? 0 : 3,
|
||||
frontUpper: isFrontFirst ? 2 : 1,
|
||||
backUpper: isFrontFirst ? 1 : 2,
|
||||
rightUpper: isFrontFirst ? 2 : 1
|
||||
}
|
||||
};
|
||||
ret.backUpper = renderer.getCylinderBack(top, middleTop);
|
||||
useAlphaCorrection = (Math.min(middleWidth, shapeArgs.width) /
|
||||
Math.max(middleWidth, shapeArgs.width)) !== 1;
|
||||
ret.rightUpper = renderer.getCylinderFront(renderer.getCylinderEnd(chart, merge(shapeArgs, {
|
||||
x: shapeArgs.x - shapeArgs.width / 2,
|
||||
z: shapeArgs.z - shapeArgs.width / 2,
|
||||
alphaCorrection: useAlphaCorrection ? -alphaCorrection : 0
|
||||
}), false), renderer.getCylinderEnd(chart, merge(middleTopArgs, {
|
||||
alphaCorrection: useAlphaCorrection ? -alphaCorrection : 0
|
||||
}), !hasMiddle));
|
||||
if (hasMiddle) {
|
||||
useAlphaCorrection = (Math.min(middleWidth, bottomWidth) /
|
||||
Math.max(middleWidth, bottomWidth)) !== 1;
|
||||
merge(true, ret, {
|
||||
frontLower: renderer.getCylinderFront(middleBottom, bottom),
|
||||
backLower: renderer.getCylinderBack(middleBottom, bottom),
|
||||
rightLower: renderer.getCylinderFront(renderer.getCylinderEnd(chart, merge(bottomArgs, {
|
||||
alphaCorrection: useAlphaCorrection ?
|
||||
-alphaCorrection : 0
|
||||
}), true), renderer.getCylinderEnd(chart, merge(middleTopArgs, {
|
||||
alphaCorrection: useAlphaCorrection ?
|
||||
-alphaCorrection : 0
|
||||
}), false)),
|
||||
zIndexes: {
|
||||
frontLower: isFrontFirst ? 2 : 1,
|
||||
backLower: isFrontFirst ? 1 : 2,
|
||||
rightLower: isFrontFirst ? 1 : 2
|
||||
}
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
@@ -0,0 +1,547 @@
|
||||
/* *
|
||||
*
|
||||
* Highcharts funnel module
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
/* eslint indent: 0 */
|
||||
'use strict';
|
||||
import Chart from '../Core/Chart/Chart.js';
|
||||
import H from '../Core/Globals.js';
|
||||
var noop = H.noop, seriesType = H.seriesType, seriesTypes = H.seriesTypes;
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, fireEvent = U.fireEvent, isArray = U.isArray, pick = U.pick;
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Series.js';
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.funnel
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('funnel', 'pie',
|
||||
/**
|
||||
* Funnel charts are a type of chart often used to visualize stages in a
|
||||
* sales project, where the top are the initial stages with the most
|
||||
* clients. It requires that the modules/funnel.js file is loaded.
|
||||
*
|
||||
* @sample highcharts/demo/funnel/
|
||||
* Funnel demo
|
||||
*
|
||||
* @extends plotOptions.pie
|
||||
* @excluding innerSize,size,dataSorting
|
||||
* @product highcharts
|
||||
* @requires modules/funnel
|
||||
* @optionparent plotOptions.funnel
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Initial animation is by default disabled for the funnel chart.
|
||||
*/
|
||||
animation: false,
|
||||
/**
|
||||
* The center of the series. By default, it is centered in the middle
|
||||
* of the plot area, so it fills the plot area height.
|
||||
*
|
||||
* @type {Array<number|string>}
|
||||
* @default ["50%", "50%"]
|
||||
* @since 3.0
|
||||
*/
|
||||
center: ['50%', '50%'],
|
||||
/**
|
||||
* The width of the funnel compared to the width of the plot area,
|
||||
* or the pixel width if it is a number.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 3.0
|
||||
*/
|
||||
width: '90%',
|
||||
/**
|
||||
* The width of the neck, the lower part of the funnel. A number defines
|
||||
* pixel width, a percentage string defines a percentage of the plot
|
||||
* area width.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/funnel/
|
||||
* Funnel demo
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 3.0
|
||||
*/
|
||||
neckWidth: '30%',
|
||||
/**
|
||||
* The height of the funnel or pyramid. If it is a number it defines
|
||||
* the pixel height, if it is a percentage string it is the percentage
|
||||
* of the plot area height.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/funnel/
|
||||
* Funnel demo
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 3.0
|
||||
*/
|
||||
height: '100%',
|
||||
/**
|
||||
* The height of the neck, the lower part of the funnel. A number
|
||||
* defines pixel width, a percentage string defines a percentage of the
|
||||
* plot area height.
|
||||
*
|
||||
* @type {number|string}
|
||||
*/
|
||||
neckHeight: '25%',
|
||||
/**
|
||||
* A reversed funnel has the widest area down. A reversed funnel with
|
||||
* no neck width and neck height is a pyramid.
|
||||
*
|
||||
* @since 3.0.10
|
||||
*/
|
||||
reversed: false,
|
||||
/**
|
||||
* To avoid adapting the data label size in Pie.drawDataLabels.
|
||||
* @ignore-option
|
||||
*/
|
||||
size: true,
|
||||
dataLabels: {
|
||||
connectorWidth: 1,
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
/**
|
||||
* Options for the series states.
|
||||
*/
|
||||
states: {
|
||||
/**
|
||||
* @excluding halo, marker, lineWidth, lineWidthPlus
|
||||
* @apioption plotOptions.funnel.states.hover
|
||||
*/
|
||||
/**
|
||||
* Options for a selected funnel item.
|
||||
*
|
||||
* @excluding halo, marker, lineWidth, lineWidthPlus
|
||||
*/
|
||||
select: {
|
||||
/**
|
||||
* A specific color for the selected point.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
color: '#cccccc',
|
||||
/**
|
||||
* A specific border color for the selected point.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
*/
|
||||
borderColor: '#000000'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Properties
|
||||
{
|
||||
animate: noop,
|
||||
// Overrides the pie translate method
|
||||
translate: function () {
|
||||
var sum = 0, series = this, chart = series.chart, options = series.options, reversed = options.reversed, ignoreHiddenPoint = options.ignoreHiddenPoint, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, cumulative = 0, // start at top
|
||||
center = options.center, centerX = getLength(center[0], plotWidth), centerY = getLength(center[1], plotHeight), width = getLength(options.width, plotWidth), tempWidth, height = getLength(options.height, plotHeight), neckWidth = getLength(options.neckWidth, plotWidth), neckHeight = getLength(options.neckHeight, plotHeight), neckY = (centerY - height / 2) + height - neckHeight, data = series.data, path, fraction, half = (options.dataLabels.position === 'left' ?
|
||||
1 :
|
||||
0), x1, y1, x2, x3, y3, x4, y5;
|
||||
/**
|
||||
* Get positions - either an integer or a percentage string must be
|
||||
* given.
|
||||
* @private
|
||||
* @param {number|string|undefined} length
|
||||
* Length
|
||||
* @param {number} relativeTo
|
||||
* Relative factor
|
||||
* @return {number}
|
||||
* Relative position
|
||||
*/
|
||||
function getLength(length, relativeTo) {
|
||||
return (/%$/).test(length) ?
|
||||
relativeTo * parseInt(length, 10) / 100 :
|
||||
parseInt(length, 10);
|
||||
}
|
||||
series.getWidthAt = function (y) {
|
||||
var top = (centerY - height / 2);
|
||||
return (y > neckY || height === neckHeight) ?
|
||||
neckWidth :
|
||||
neckWidth + (width - neckWidth) *
|
||||
(1 - (y - top) / (height - neckHeight));
|
||||
};
|
||||
series.getX = function (y, half, point) {
|
||||
return centerX + (half ? -1 : 1) *
|
||||
((series.getWidthAt(reversed ? 2 * centerY - y : y) / 2) +
|
||||
point.labelDistance);
|
||||
};
|
||||
// Expose
|
||||
series.center = [centerX, centerY, height];
|
||||
series.centerX = centerX;
|
||||
/*
|
||||
Individual point coordinate naming:
|
||||
|
||||
x1,y1 _________________ x2,y1
|
||||
\ /
|
||||
\ /
|
||||
\ /
|
||||
\ /
|
||||
\ /
|
||||
x3,y3 _________ x4,y3
|
||||
|
||||
Additional for the base of the neck:
|
||||
|
||||
| |
|
||||
| |
|
||||
| |
|
||||
x3,y5 _________ x4,y5
|
||||
|
||||
*/
|
||||
// get the total sum
|
||||
data.forEach(function (point) {
|
||||
if (!ignoreHiddenPoint || point.visible !== false) {
|
||||
sum += point.y;
|
||||
}
|
||||
});
|
||||
data.forEach(function (point) {
|
||||
// set start and end positions
|
||||
y5 = null;
|
||||
fraction = sum ? point.y / sum : 0;
|
||||
y1 = centerY - height / 2 + cumulative * height;
|
||||
y3 = y1 + fraction * height;
|
||||
tempWidth = series.getWidthAt(y1);
|
||||
x1 = centerX - tempWidth / 2;
|
||||
x2 = x1 + tempWidth;
|
||||
tempWidth = series.getWidthAt(y3);
|
||||
x3 = centerX - tempWidth / 2;
|
||||
x4 = x3 + tempWidth;
|
||||
// the entire point is within the neck
|
||||
if (y1 > neckY) {
|
||||
x1 = x3 = centerX - neckWidth / 2;
|
||||
x2 = x4 = centerX + neckWidth / 2;
|
||||
// the base of the neck
|
||||
}
|
||||
else if (y3 > neckY) {
|
||||
y5 = y3;
|
||||
tempWidth = series.getWidthAt(neckY);
|
||||
x3 = centerX - tempWidth / 2;
|
||||
x4 = x3 + tempWidth;
|
||||
y3 = neckY;
|
||||
}
|
||||
if (reversed) {
|
||||
y1 = 2 * centerY - y1;
|
||||
y3 = 2 * centerY - y3;
|
||||
if (y5 !== null) {
|
||||
y5 = 2 * centerY - y5;
|
||||
}
|
||||
}
|
||||
// save the path
|
||||
path = [
|
||||
['M', x1, y1],
|
||||
['L', x2, y1],
|
||||
['L', x4, y3]
|
||||
];
|
||||
if (y5 !== null) {
|
||||
path.push(['L', x4, y5], ['L', x3, y5]);
|
||||
}
|
||||
path.push(['L', x3, y3], ['Z']);
|
||||
// prepare for using shared dr
|
||||
point.shapeType = 'path';
|
||||
point.shapeArgs = { d: path };
|
||||
// for tooltips and data labels
|
||||
point.percentage = fraction * 100;
|
||||
point.plotX = centerX;
|
||||
point.plotY = (y1 + (y5 || y3)) / 2;
|
||||
// Placement of tooltips and data labels
|
||||
point.tooltipPos = [
|
||||
centerX,
|
||||
point.plotY
|
||||
];
|
||||
point.dlBox = {
|
||||
x: x3,
|
||||
y: y1,
|
||||
topWidth: x2 - x1,
|
||||
bottomWidth: x4 - x3,
|
||||
height: Math.abs(pick(y5, y3) - y1),
|
||||
width: NaN
|
||||
};
|
||||
// Slice is a noop on funnel points
|
||||
point.slice = noop;
|
||||
// Mimicking pie data label placement logic
|
||||
point.half = half;
|
||||
if (!ignoreHiddenPoint || point.visible !== false) {
|
||||
cumulative += fraction;
|
||||
}
|
||||
});
|
||||
fireEvent(series, 'afterTranslate');
|
||||
},
|
||||
// Funnel items don't have angles (#2289)
|
||||
sortByAngle: function (points) {
|
||||
points.sort(function (a, b) {
|
||||
return a.plotY - b.plotY;
|
||||
});
|
||||
},
|
||||
// Extend the pie data label method
|
||||
drawDataLabels: function () {
|
||||
var series = this, data = series.data, labelDistance = series.options.dataLabels.distance, leftSide, sign, point, i = data.length, x, y;
|
||||
// In the original pie label anticollision logic, the slots are
|
||||
// distributed from one labelDistance above to one labelDistance
|
||||
// below the pie. In funnels we don't want this.
|
||||
series.center[2] -= 2 * labelDistance;
|
||||
// Set the label position array for each point.
|
||||
while (i--) {
|
||||
point = data[i];
|
||||
leftSide = point.half;
|
||||
sign = leftSide ? 1 : -1;
|
||||
y = point.plotY;
|
||||
point.labelDistance = pick(point.options.dataLabels &&
|
||||
point.options.dataLabels.distance, labelDistance);
|
||||
series.maxLabelDistance = Math.max(point.labelDistance, series.maxLabelDistance || 0);
|
||||
x = series.getX(y, leftSide, point);
|
||||
// set the anchor point for data labels
|
||||
point.labelPosition = {
|
||||
// initial position of the data label - it's utilized for
|
||||
// finding the final position for the label
|
||||
natural: {
|
||||
x: 0,
|
||||
y: y
|
||||
},
|
||||
'final': {
|
||||
// used for generating connector path -
|
||||
// initialized later in drawDataLabels function
|
||||
// x: undefined,
|
||||
// y: undefined
|
||||
},
|
||||
// left - funnel on the left side of the data label
|
||||
// right - funnel on the right side of the data label
|
||||
alignment: leftSide ? 'right' : 'left',
|
||||
connectorPosition: {
|
||||
breakAt: {
|
||||
x: x + (point.labelDistance - 5) * sign,
|
||||
y: y
|
||||
},
|
||||
touchingSliceAt: {
|
||||
x: x + point.labelDistance * sign,
|
||||
y: y
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
seriesTypes[series.options.dataLabels.inside ? 'column' : 'pie'].prototype.drawDataLabels.call(this);
|
||||
},
|
||||
alignDataLabel: function (point, dataLabel, options, alignTo, isNew) {
|
||||
var series = point.series, reversed = series.options.reversed, dlBox = point.dlBox || point.shapeArgs, align = options.align, verticalAlign = options.verticalAlign, inside = ((series.options || {}).dataLabels || {}).inside, centerY = series.center[1], pointPlotY = (reversed ?
|
||||
2 * centerY - point.plotY :
|
||||
point.plotY), widthAtLabel = series.getWidthAt(pointPlotY - dlBox.height / 2 +
|
||||
dataLabel.height), offset = verticalAlign === 'middle' ?
|
||||
(dlBox.topWidth - dlBox.bottomWidth) / 4 :
|
||||
(widthAtLabel - dlBox.bottomWidth) / 2, y = dlBox.y, x = dlBox.x;
|
||||
if (verticalAlign === 'middle') {
|
||||
y = dlBox.y - dlBox.height / 2 + dataLabel.height / 2;
|
||||
}
|
||||
else if (verticalAlign === 'top') {
|
||||
y = dlBox.y - dlBox.height + dataLabel.height +
|
||||
options.padding;
|
||||
}
|
||||
if (verticalAlign === 'top' && !reversed ||
|
||||
verticalAlign === 'bottom' && reversed ||
|
||||
verticalAlign === 'middle') {
|
||||
if (align === 'right') {
|
||||
x = dlBox.x - options.padding + offset;
|
||||
}
|
||||
else if (align === 'left') {
|
||||
x = dlBox.x + options.padding - offset;
|
||||
}
|
||||
}
|
||||
alignTo = {
|
||||
x: x,
|
||||
y: reversed ? y - dlBox.height : y,
|
||||
width: dlBox.bottomWidth,
|
||||
height: dlBox.height
|
||||
};
|
||||
options.verticalAlign = 'bottom';
|
||||
// Call the parent method
|
||||
if (!inside || point.visible) {
|
||||
Highcharts.Series.prototype.alignDataLabel.call(this, point, dataLabel, options, alignTo, isNew);
|
||||
}
|
||||
if (inside) {
|
||||
if (!point.visible && point.dataLabel) {
|
||||
// Avoid animation from top
|
||||
point.dataLabel.placed = false;
|
||||
}
|
||||
// If label is inside and we have contrast, set it:
|
||||
if (point.contrastColor) {
|
||||
dataLabel.css({
|
||||
color: point.contrastColor
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/* eslint-disable no-invalid-this */
|
||||
addEvent(Chart, 'afterHideAllOverlappingLabels', function () {
|
||||
this.series.forEach(function (series) {
|
||||
var dataLabelsOptions = series.options && series.options.dataLabels;
|
||||
if (isArray(dataLabelsOptions)) {
|
||||
dataLabelsOptions = dataLabelsOptions[0];
|
||||
}
|
||||
if (series.is('pie') &&
|
||||
series.placeDataLabels &&
|
||||
dataLabelsOptions &&
|
||||
!dataLabelsOptions.inside) {
|
||||
series.placeDataLabels();
|
||||
}
|
||||
});
|
||||
});
|
||||
/**
|
||||
* A `funnel` series. If the [type](#series.funnel.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.funnel
|
||||
* @excluding dataParser, dataURL, stack, xAxis, yAxis, dataSorting,
|
||||
* boostBlending, boostThreshold
|
||||
* @product highcharts
|
||||
* @requires modules/funnel
|
||||
* @apioption series.funnel
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `funnel` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values
|
||||
* will be interpreted as `y` options. Example:
|
||||
*
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.funnel.turboThreshold),
|
||||
* this option is not available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 3,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 1,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|null|*>}
|
||||
* @extends series.pie.data
|
||||
* @excluding sliced
|
||||
* @product highcharts
|
||||
* @apioption series.funnel.data
|
||||
*/
|
||||
/**
|
||||
* Pyramid series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.pyramid
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('pyramid', 'funnel',
|
||||
/**
|
||||
* A pyramid series is a special type of funnel, without neck and reversed
|
||||
* by default.
|
||||
*
|
||||
* @sample highcharts/demo/pyramid/
|
||||
* Pyramid chart
|
||||
*
|
||||
* @extends plotOptions.funnel
|
||||
* @product highcharts
|
||||
* @requires modules/funnel
|
||||
* @optionparent plotOptions.pyramid
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* The pyramid neck width is zero by default, as opposed to the funnel,
|
||||
* which shares the same layout logic.
|
||||
*
|
||||
* @since 3.0.10
|
||||
*/
|
||||
neckWidth: '0%',
|
||||
/**
|
||||
* The pyramid neck width is zero by default, as opposed to the funnel,
|
||||
* which shares the same layout logic.
|
||||
*
|
||||
* @since 3.0.10
|
||||
*/
|
||||
neckHeight: '0%',
|
||||
/**
|
||||
* The pyramid is reversed by default, as opposed to the funnel, which
|
||||
* shares the layout engine, and is not reversed.
|
||||
*
|
||||
* @since 3.0.10
|
||||
*/
|
||||
reversed: true
|
||||
});
|
||||
/**
|
||||
* A `pyramid` series. If the [type](#series.pyramid.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.pyramid
|
||||
* @excluding dataParser, dataURL, stack, xAxis, yAxis, dataSorting,
|
||||
* boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/funnel
|
||||
* @apioption series.pyramid
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `pyramid` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.pyramid.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 9,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 6,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @sample {highcharts} highcharts/chart/reflow-true/
|
||||
* Numerical values
|
||||
* @sample {highcharts} highcharts/series/data-array-of-objects/
|
||||
* Config objects
|
||||
*
|
||||
* @type {Array<number|null|*>}
|
||||
* @extends series.pie.data
|
||||
* @excluding sliced
|
||||
* @product highcharts
|
||||
* @apioption series.pyramid.data
|
||||
*/
|
||||
''; // adds doclets above into transpiled file
|
||||
@@ -0,0 +1,352 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2016-2020 Highsoft AS
|
||||
*
|
||||
* Author: Lars A. V. Cabrera
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import O from '../Core/Options.js';
|
||||
var dateFormat = O.dateFormat;
|
||||
import '../Core/Axis/TreeGridAxis.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var isNumber = U.isNumber, merge = U.merge, pick = U.pick, seriesType = U.seriesType, splat = U.splat;
|
||||
import '../Extensions/CurrentDateIndication.js';
|
||||
import '../Extensions/StaticScale.js';
|
||||
import '../Gantt/Pathfinder.js';
|
||||
import './XRangeSeries.js';
|
||||
var seriesTypes = H.seriesTypes, Series = H.Series, parent = seriesTypes.xrange;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.gantt
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('gantt', 'xrange'
|
||||
/**
|
||||
* A `gantt` series. If the [type](#series.gantt.type) option is not specified,
|
||||
* it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends plotOptions.xrange
|
||||
* @product gantt
|
||||
* @requires highcharts-gantt
|
||||
* @optionparent plotOptions.gantt
|
||||
*/
|
||||
, {
|
||||
// options - default options merged with parent
|
||||
grouping: false,
|
||||
dataLabels: {
|
||||
enabled: true
|
||||
},
|
||||
tooltip: {
|
||||
headerFormat: '<span style="font-size: 10px">{series.name}</span><br/>',
|
||||
pointFormat: null,
|
||||
pointFormatter: function () {
|
||||
var point = this, series = point.series, tooltip = series.chart.tooltip, xAxis = series.xAxis, formats = series.tooltipOptions.dateTimeLabelFormats, startOfWeek = xAxis.options.startOfWeek, ttOptions = series.tooltipOptions, format = ttOptions.xDateFormat, start, end, milestone = point.options.milestone, retVal = '<b>' + (point.name || point.yCategory) + '</b>';
|
||||
if (ttOptions.pointFormat) {
|
||||
return point.tooltipFormatter(ttOptions.pointFormat);
|
||||
}
|
||||
if (!format) {
|
||||
format = splat(tooltip.getDateFormat(xAxis.closestPointRange, point.start, startOfWeek, formats))[0];
|
||||
}
|
||||
start = dateFormat(format, point.start);
|
||||
end = dateFormat(format, point.end);
|
||||
retVal += '<br/>';
|
||||
if (!milestone) {
|
||||
retVal += 'Start: ' + start + '<br/>';
|
||||
retVal += 'End: ' + end + '<br/>';
|
||||
}
|
||||
else {
|
||||
retVal += start + '<br/>';
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
},
|
||||
connectors: {
|
||||
type: 'simpleConnect',
|
||||
/**
|
||||
* @declare Highcharts.ConnectorsAnimationOptionsObject
|
||||
*/
|
||||
animation: {
|
||||
reversed: true // Dependencies go from child to parent
|
||||
},
|
||||
startMarker: {
|
||||
enabled: true,
|
||||
symbol: 'arrow-filled',
|
||||
radius: 4,
|
||||
fill: '#fa0',
|
||||
align: 'left'
|
||||
},
|
||||
endMarker: {
|
||||
enabled: false,
|
||||
align: 'right'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
pointArrayMap: ['start', 'end', 'y'],
|
||||
// Keyboard navigation, don't use nearest vertical mode
|
||||
keyboardMoveVertical: false,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Handle milestones, as they have no x2.
|
||||
* @private
|
||||
*/
|
||||
translatePoint: function (point) {
|
||||
var series = this, shapeArgs, size;
|
||||
parent.prototype.translatePoint.call(series, point);
|
||||
if (point.options.milestone) {
|
||||
shapeArgs = point.shapeArgs;
|
||||
size = shapeArgs.height;
|
||||
point.shapeArgs = {
|
||||
x: shapeArgs.x - (size / 2),
|
||||
y: shapeArgs.y,
|
||||
width: size,
|
||||
height: size
|
||||
};
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Draws a single point in the series.
|
||||
*
|
||||
* This override draws the point as a diamond if point.options.milestone
|
||||
* is true, and uses the original drawPoint() if it is false or not set.
|
||||
*
|
||||
* @requires highcharts-gantt
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.gantt#drawPoint
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
* An instance of Point in the series
|
||||
*
|
||||
* @param {"animate"|"attr"} verb
|
||||
* 'animate' (animates changes) or 'attr' (sets options)
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
drawPoint: function (point, verb) {
|
||||
var series = this, seriesOpts = series.options, renderer = series.chart.renderer, shapeArgs = point.shapeArgs, plotY = point.plotY, graphic = point.graphic, state = point.selected && 'select', cutOff = seriesOpts.stacking && !seriesOpts.borderRadius, diamondShape;
|
||||
if (point.options.milestone) {
|
||||
if (isNumber(plotY) && point.y !== null && point.visible !== false) {
|
||||
diamondShape = renderer.symbols.diamond(shapeArgs.x, shapeArgs.y, shapeArgs.width, shapeArgs.height);
|
||||
if (graphic) {
|
||||
graphic[verb]({
|
||||
d: diamondShape
|
||||
});
|
||||
}
|
||||
else {
|
||||
point.graphic = graphic = renderer.path(diamondShape)
|
||||
.addClass(point.getClassName(), true)
|
||||
.add(point.group || series.group);
|
||||
}
|
||||
// Presentational
|
||||
if (!series.chart.styledMode) {
|
||||
point.graphic
|
||||
.attr(series.pointAttribs(point, state))
|
||||
.shadow(seriesOpts.shadow, null, cutOff);
|
||||
}
|
||||
}
|
||||
else if (graphic) {
|
||||
point.graphic = graphic.destroy(); // #1269
|
||||
}
|
||||
}
|
||||
else {
|
||||
parent.prototype.drawPoint.call(series, point, verb);
|
||||
}
|
||||
},
|
||||
setData: Series.prototype.setData,
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
setGanttPointAliases: function (options) {
|
||||
/**
|
||||
* Add a value to options if the value exists.
|
||||
* @private
|
||||
*/
|
||||
function addIfExists(prop, val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
options[prop] = val;
|
||||
}
|
||||
}
|
||||
addIfExists('x', pick(options.start, options.x));
|
||||
addIfExists('x2', pick(options.end, options.x2));
|
||||
addIfExists('partialFill', pick(options.completed, options.partialFill));
|
||||
addIfExists('connect', pick(options.dependency, options.connect));
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
}, merge(parent.prototype.pointClass.prototype, {
|
||||
// pointProps - point member overrides. We inherit from parent as well.
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Applies the options containing the x and y data and possible some
|
||||
* extra properties. This is called on point init or from point.update.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Point#applyOptions
|
||||
*
|
||||
* @param {object} options
|
||||
* The point options
|
||||
*
|
||||
* @param {number} x
|
||||
* The x value
|
||||
*
|
||||
* @return {Highcharts.Point}
|
||||
* The Point instance
|
||||
*/
|
||||
applyOptions: function (options, x) {
|
||||
var point = this, retVal = merge(options);
|
||||
H.seriesTypes.gantt.prototype.setGanttPointAliases(retVal);
|
||||
retVal = parent.prototype.pointClass.prototype.applyOptions
|
||||
.call(point, retVal, x);
|
||||
return retVal;
|
||||
},
|
||||
isValid: function () {
|
||||
return ((typeof this.start === 'number' ||
|
||||
typeof this.x === 'number') &&
|
||||
(typeof this.end === 'number' ||
|
||||
typeof this.x2 === 'number' ||
|
||||
this.milestone));
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
}));
|
||||
/**
|
||||
* A `gantt` series.
|
||||
*
|
||||
* @extends series,plotOptions.gantt
|
||||
* @excluding boostThreshold, connectors, dashStyle, findNearestPointBy,
|
||||
* getExtremesFromAll, marker, negativeColor, pointInterval,
|
||||
* pointIntervalUnit, pointPlacement, pointStart
|
||||
* @product gantt
|
||||
* @requires highcharts-gantt
|
||||
* @apioption series.gantt
|
||||
*/
|
||||
/**
|
||||
* Data for a Gantt series.
|
||||
*
|
||||
* @declare Highcharts.GanttPointOptionsObject
|
||||
* @type {Array<*>}
|
||||
* @extends series.xrange.data
|
||||
* @excluding className, color, colorIndex, connect, dataLabels, events,
|
||||
* partialFill, selected, x, x2
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data
|
||||
*/
|
||||
/**
|
||||
* Whether the grid node belonging to this point should start as collapsed. Used
|
||||
* in axes of type treegrid.
|
||||
*
|
||||
* @sample {gantt} gantt/treegrid-axis/collapsed/
|
||||
* Start as collapsed
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.collapsed
|
||||
*/
|
||||
/**
|
||||
* The start time of a task.
|
||||
*
|
||||
* @type {number}
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.start
|
||||
*/
|
||||
/**
|
||||
* The end time of a task.
|
||||
*
|
||||
* @type {number}
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.end
|
||||
*/
|
||||
/**
|
||||
* The Y value of a task.
|
||||
*
|
||||
* @type {number}
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.y
|
||||
*/
|
||||
/**
|
||||
* The name of a task. If a `treegrid` y-axis is used (default in Gantt charts),
|
||||
* this will be picked up automatically, and used to calculate the y-value.
|
||||
*
|
||||
* @type {string}
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.name
|
||||
*/
|
||||
/**
|
||||
* Progress indicator, how much of the task completed. If it is a number, the
|
||||
* `fill` will be applied automatically.
|
||||
*
|
||||
* @sample {gantt} gantt/demo/progress-indicator
|
||||
* Progress indicator
|
||||
*
|
||||
* @type {number|*}
|
||||
* @extends series.xrange.data.partialFill
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.completed
|
||||
*/
|
||||
/**
|
||||
* The amount of the progress indicator, ranging from 0 (not started) to 1
|
||||
* (finished).
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
* @apioption series.gantt.data.completed.amount
|
||||
*/
|
||||
/**
|
||||
* The fill of the progress indicator. Defaults to a darkened variety of the
|
||||
* main color.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @apioption series.gantt.data.completed.fill
|
||||
*/
|
||||
/**
|
||||
* The ID of the point (task) that this point depends on in Gantt charts.
|
||||
* Aliases [connect](series.xrange.data.connect). Can also be an object,
|
||||
* specifying further connecting [options](series.gantt.connectors) between the
|
||||
* points. Multiple connections can be specified by providing an array.
|
||||
*
|
||||
* @sample gantt/demo/project-management
|
||||
* Dependencies
|
||||
* @sample gantt/pathfinder/demo
|
||||
* Different connection types
|
||||
*
|
||||
* @type {string|Array<string|*>|*}
|
||||
* @extends series.xrange.data.connect
|
||||
* @since 6.2.0
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.dependency
|
||||
*/
|
||||
/**
|
||||
* Whether this point is a milestone. If so, only the `start` option is handled,
|
||||
* while `end` is ignored.
|
||||
*
|
||||
* @sample gantt/gantt/milestones
|
||||
* Milestones
|
||||
*
|
||||
* @type {boolean}
|
||||
* @since 6.2.0
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.milestone
|
||||
*/
|
||||
/**
|
||||
* The ID of the parent point (task) of this point in Gantt charts.
|
||||
*
|
||||
* @sample gantt/demo/subtasks
|
||||
* Gantt chart with subtasks
|
||||
*
|
||||
* @type {string}
|
||||
* @since 6.2.0
|
||||
* @product gantt
|
||||
* @apioption series.gantt.data.parent
|
||||
*/
|
||||
/**
|
||||
* @excluding afterAnimate
|
||||
* @apioption series.gantt.events
|
||||
*/
|
||||
''; // adds doclets above to the transpiled file
|
||||
@@ -0,0 +1,510 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var clamp = U.clamp, isNumber = U.isNumber, merge = U.merge, pick = U.pick, pInt = U.pInt, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Point.js';
|
||||
import '../Core/Series/Series.js';
|
||||
import '../Core/Interaction.js';
|
||||
var noop = H.noop, Series = H.Series, TrackerMixin = H.TrackerMixin;
|
||||
/**
|
||||
* Gauges are circular plots displaying one or more values with a dial pointing
|
||||
* to values along the perimeter.
|
||||
*
|
||||
* @sample highcharts/demo/gauge-speedometer/
|
||||
* Gauge chart
|
||||
*
|
||||
* @extends plotOptions.line
|
||||
* @excluding animationLimit, boostThreshold, colorAxis, colorKey,
|
||||
* connectEnds, connectNulls, cropThreshold, dashStyle, dragDrop,
|
||||
* findNearestPointBy, getExtremesFromAll, marker, negativeColor,
|
||||
* pointPlacement, shadow, softThreshold, stacking, states, step,
|
||||
* threshold, turboThreshold, xAxis, zoneAxis, zones, dataSorting,
|
||||
* boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.gauge
|
||||
*/
|
||||
seriesType('gauge', 'line', {
|
||||
/**
|
||||
* When this option is `true`, the dial will wrap around the axes. For
|
||||
* instance, in a full-range gauge going from 0 to 360, a value of 400
|
||||
* will point to 40\. When `wrap` is `false`, the dial stops at 360.
|
||||
*
|
||||
* @see [overshoot](#plotOptions.gauge.overshoot)
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.wrap
|
||||
*/
|
||||
/**
|
||||
* Data labels for the gauge. For gauges, the data labels are enabled
|
||||
* by default and shown in a bordered box below the point.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
dataLabels: {
|
||||
borderColor: '#cccccc',
|
||||
borderRadius: 3,
|
||||
borderWidth: 1,
|
||||
crop: false,
|
||||
defer: false,
|
||||
enabled: true,
|
||||
verticalAlign: 'top',
|
||||
y: 15,
|
||||
zIndex: 2
|
||||
},
|
||||
/**
|
||||
* Options for the dial or arrow pointer of the gauge.
|
||||
*
|
||||
* In styled mode, the dial is styled with the
|
||||
* `.highcharts-gauge-series .highcharts-dial` rule.
|
||||
*
|
||||
* @sample {highcharts} highcharts/css/gauge/
|
||||
* Styled mode
|
||||
*
|
||||
* @type {*}
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
dial: {},
|
||||
/**
|
||||
* The length of the dial's base part, relative to the total radius
|
||||
* or length of the dial.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {string}
|
||||
* @default 70%
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.baseLength
|
||||
*/
|
||||
/**
|
||||
* The pixel width of the base of the gauge dial. The base is the part
|
||||
* closest to the pivot, defined by baseLength.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {number}
|
||||
* @default 3
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.baseWidth
|
||||
*/
|
||||
/**
|
||||
* The radius or length of the dial, in percentages relative to the
|
||||
* radius of the gauge itself.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {string}
|
||||
* @default 80%
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.radius
|
||||
*/
|
||||
/**
|
||||
* The length of the dial's rear end, the part that extends out on the
|
||||
* other side of the pivot. Relative to the dial's length.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {string}
|
||||
* @default 10%
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.rearLength
|
||||
*/
|
||||
/**
|
||||
* The width of the top of the dial, closest to the perimeter. The pivot
|
||||
* narrows in from the base to the top.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.topWidth
|
||||
*/
|
||||
/**
|
||||
* The background or fill color of the gauge's dial.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #000000
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.backgroundColor
|
||||
*/
|
||||
/**
|
||||
* The border color or stroke of the gauge's dial. By default, the
|
||||
* borderWidth is 0, so this must be set in addition to a custom border
|
||||
* color.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #cccccc
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.borderColor
|
||||
*/
|
||||
/**
|
||||
* The width of the gauge dial border in pixels.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-dial/
|
||||
* Dial options demonstrated
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.dial.borderWidth
|
||||
*/
|
||||
/**
|
||||
* Allow the dial to overshoot the end of the perimeter axis by this
|
||||
* many degrees. Say if the gauge axis goes from 0 to 60, a value of
|
||||
* 100, or 1000, will show 5 degrees beyond the end of the axis when this
|
||||
* option is set to 5.
|
||||
*
|
||||
* @see [wrap](#plotOptions.gauge.wrap)
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-overshoot/
|
||||
* Allow 5 degrees overshoot
|
||||
*
|
||||
* @type {number}
|
||||
* @since 3.0.10
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.overshoot
|
||||
*/
|
||||
/**
|
||||
* Options for the pivot or the center point of the gauge.
|
||||
*
|
||||
* In styled mode, the pivot is styled with the
|
||||
* `.highcharts-gauge-series .highcharts-pivot` rule.
|
||||
*
|
||||
* @sample {highcharts} highcharts/css/gauge/
|
||||
* Styled mode
|
||||
*
|
||||
* @type {*}
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
pivot: {},
|
||||
/**
|
||||
* The pixel radius of the pivot.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-pivot/
|
||||
* Pivot options demonstrated
|
||||
*
|
||||
* @type {number}
|
||||
* @default 5
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.pivot.radius
|
||||
*/
|
||||
/**
|
||||
* The border or stroke width of the pivot.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-pivot/
|
||||
* Pivot options demonstrated
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.pivot.borderWidth
|
||||
*/
|
||||
/**
|
||||
* The border or stroke color of the pivot. In able to change this,
|
||||
* the borderWidth must also be set to something other than the default
|
||||
* 0.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-pivot/
|
||||
* Pivot options demonstrated
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #cccccc
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.pivot.borderColor
|
||||
*/
|
||||
/**
|
||||
* The background color or fill of the pivot.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/gauge-pivot/
|
||||
* Pivot options demonstrated
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @default #000000
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.gauge.pivot.backgroundColor
|
||||
*/
|
||||
tooltip: {
|
||||
headerFormat: ''
|
||||
},
|
||||
/**
|
||||
* Whether to display this particular series or series type in the
|
||||
* legend. Defaults to false for gauge series.
|
||||
*
|
||||
* @since 2.3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
showInLegend: false
|
||||
// Prototype members
|
||||
}, {
|
||||
// chart.angular will be set to true when a gauge series is present,
|
||||
// and this will be used on the axes
|
||||
angular: true,
|
||||
directTouch: true,
|
||||
drawGraph: noop,
|
||||
fixedBox: true,
|
||||
forceDL: true,
|
||||
noSharedTooltip: true,
|
||||
trackerGroups: ['group', 'dataLabelsGroup'],
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Calculate paths etc
|
||||
* @private
|
||||
*/
|
||||
translate: function () {
|
||||
var series = this, yAxis = series.yAxis, options = series.options, center = yAxis.center;
|
||||
series.generatePoints();
|
||||
series.points.forEach(function (point) {
|
||||
var dialOptions = merge(options.dial, point.dial), radius = ((pInt(pick(dialOptions.radius, '80%')) * center[2]) /
|
||||
200), baseLength = ((pInt(pick(dialOptions.baseLength, '70%')) * radius) /
|
||||
100), rearLength = ((pInt(pick(dialOptions.rearLength, '10%')) * radius) /
|
||||
100), baseWidth = dialOptions.baseWidth || 3, topWidth = dialOptions.topWidth || 1, overshoot = options.overshoot, rotation = yAxis.startAngleRad + yAxis.translate(point.y, null, null, null, true);
|
||||
// Handle the wrap and overshoot options
|
||||
if (isNumber(overshoot) || options.wrap === false) {
|
||||
overshoot = isNumber(overshoot) ?
|
||||
(overshoot / 180 * Math.PI) : 0;
|
||||
rotation = clamp(rotation, yAxis.startAngleRad - overshoot, yAxis.endAngleRad + overshoot);
|
||||
}
|
||||
rotation = rotation * 180 / Math.PI;
|
||||
point.shapeType = 'path';
|
||||
var d = dialOptions.path || [
|
||||
['M', -rearLength, -baseWidth / 2],
|
||||
['L', baseLength, -baseWidth / 2],
|
||||
['L', radius, -topWidth / 2],
|
||||
['L', radius, topWidth / 2],
|
||||
['L', baseLength, baseWidth / 2],
|
||||
['L', -rearLength, baseWidth / 2],
|
||||
['Z']
|
||||
];
|
||||
point.shapeArgs = {
|
||||
d: d,
|
||||
translateX: center[0],
|
||||
translateY: center[1],
|
||||
rotation: rotation
|
||||
};
|
||||
// Positions for data label
|
||||
point.plotX = center[0];
|
||||
point.plotY = center[1];
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Draw the points where each point is one needle
|
||||
* @private
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, chart = series.chart, center = series.yAxis.center, pivot = series.pivot, options = series.options, pivotOptions = options.pivot, renderer = chart.renderer;
|
||||
series.points.forEach(function (point) {
|
||||
var graphic = point.graphic, shapeArgs = point.shapeArgs, d = shapeArgs.d, dialOptions = merge(options.dial, point.dial); // #1233
|
||||
if (graphic) {
|
||||
graphic.animate(shapeArgs);
|
||||
shapeArgs.d = d; // animate alters it
|
||||
}
|
||||
else {
|
||||
point.graphic =
|
||||
renderer[point.shapeType](shapeArgs)
|
||||
.attr({
|
||||
// required by VML when animation is false
|
||||
rotation: shapeArgs.rotation,
|
||||
zIndex: 1
|
||||
})
|
||||
.addClass('highcharts-dial')
|
||||
.add(series.group);
|
||||
}
|
||||
// Presentational attributes
|
||||
if (!chart.styledMode) {
|
||||
point.graphic[graphic ? 'animate' : 'attr']({
|
||||
stroke: dialOptions.borderColor || 'none',
|
||||
'stroke-width': dialOptions.borderWidth || 0,
|
||||
fill: dialOptions.backgroundColor ||
|
||||
'#000000'
|
||||
});
|
||||
}
|
||||
});
|
||||
// Add or move the pivot
|
||||
if (pivot) {
|
||||
pivot.animate({
|
||||
translateX: center[0],
|
||||
translateY: center[1]
|
||||
});
|
||||
}
|
||||
else {
|
||||
series.pivot =
|
||||
renderer.circle(0, 0, pick(pivotOptions.radius, 5))
|
||||
.attr({
|
||||
zIndex: 2
|
||||
})
|
||||
.addClass('highcharts-pivot')
|
||||
.translate(center[0], center[1])
|
||||
.add(series.group);
|
||||
// Presentational attributes
|
||||
if (!chart.styledMode) {
|
||||
series.pivot.attr({
|
||||
'stroke-width': pivotOptions.borderWidth || 0,
|
||||
stroke: pivotOptions.borderColor ||
|
||||
'#cccccc',
|
||||
fill: pivotOptions.backgroundColor ||
|
||||
'#000000'
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Animate the arrow up from startAngle
|
||||
* @private
|
||||
*/
|
||||
animate: function (init) {
|
||||
var series = this;
|
||||
if (!init) {
|
||||
series.points.forEach(function (point) {
|
||||
var graphic = point.graphic;
|
||||
if (graphic) {
|
||||
// start value
|
||||
graphic.attr({
|
||||
rotation: series.yAxis.startAngleRad * 180 / Math.PI
|
||||
});
|
||||
// animate
|
||||
graphic.animate({
|
||||
rotation: point.shapeArgs.rotation
|
||||
}, series.options.animation);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
render: function () {
|
||||
this.group = this.plotGroup('group', 'series', this.visible ? 'visible' : 'hidden', this.options.zIndex, this.chart.seriesGroup);
|
||||
Series.prototype.render.call(this);
|
||||
this.group.clip(this.chart.clipRect);
|
||||
},
|
||||
/**
|
||||
* Extend the basic setData method by running processData and generatePoints
|
||||
* immediately, in order to access the points from the legend.
|
||||
* @private
|
||||
*/
|
||||
setData: function (data, redraw) {
|
||||
Series.prototype.setData.call(this, data, false);
|
||||
this.processData();
|
||||
this.generatePoints();
|
||||
if (pick(redraw, true)) {
|
||||
this.chart.redraw();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Define hasData function for non-cartesian series.
|
||||
* Returns true if the series has points at all.
|
||||
* @private
|
||||
*/
|
||||
hasData: function () {
|
||||
return !!this.points.length; // != 0
|
||||
},
|
||||
// If the tracking module is loaded, add the point tracker
|
||||
drawTracker: TrackerMixin && TrackerMixin.drawTrackerPoint
|
||||
/* eslint-enable valid-jsdoc */
|
||||
}, {
|
||||
// Point members
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Don't do any hover colors or anything
|
||||
* @private
|
||||
*/
|
||||
setState: function (state) {
|
||||
this.state = state;
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* A `gauge` series. If the [type](#series.gauge.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.gauge
|
||||
* @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
|
||||
* cropThreshold, dashStyle, dataParser, dataURL, findNearestPointBy,
|
||||
* getExtremesFromAll, marker, negativeColor, pointPlacement, shadow,
|
||||
* softThreshold, stack, stacking, states, step, threshold,
|
||||
* turboThreshold, zoneAxis, zones, dataSorting, boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-more
|
||||
* @apioption series.gauge
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `gauge` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.gauge.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 6,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 8,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* The typical gauge only contains a single data value.
|
||||
*
|
||||
* @sample {highcharts} highcharts/chart/reflow-true/
|
||||
* Numerical values
|
||||
* @sample {highcharts} highcharts/series/data-array-of-objects/
|
||||
* Config objects
|
||||
*
|
||||
* @type {Array<number|null|*>}
|
||||
* @extends series.line.data
|
||||
* @excluding drilldown, marker, x
|
||||
* @product highcharts
|
||||
* @apioption series.gauge.data
|
||||
*/
|
||||
''; // adds the doclets above in the transpiled file
|
||||
@@ -0,0 +1,958 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
|
||||
import SVGRenderer from '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var clamp = U.clamp, extend = U.extend, fireEvent = U.fireEvent, isNumber = U.isNumber, merge = U.merge, pick = U.pick, seriesType = U.seriesType;
|
||||
/* *
|
||||
* @interface Highcharts.PointOptionsObject in parts/Point.ts
|
||||
*/ /**
|
||||
* Heatmap series only. Point padding for a single point.
|
||||
* @name Highcharts.PointOptionsObject#pointPadding
|
||||
* @type {number|undefined}
|
||||
*/ /**
|
||||
* Heatmap series only. The value of the point, resulting in a color controled
|
||||
* by options as set in the colorAxis configuration.
|
||||
* @name Highcharts.PointOptionsObject#value
|
||||
* @type {number|null|undefined}
|
||||
*/
|
||||
''; // detach doclets above
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Series.js';
|
||||
import '../Mixins/ColorMapSeries.js';
|
||||
var colorMapPointMixin = H.colorMapPointMixin, colorMapSeriesMixin = H.colorMapSeriesMixin, noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes, symbols = SVGRenderer.prototype.symbols;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.heatmap
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('heatmap', 'scatter',
|
||||
/**
|
||||
* A heatmap is a graphical representation of data where the individual
|
||||
* values contained in a matrix are represented as colors.
|
||||
*
|
||||
* @productdesc {highcharts}
|
||||
* Requires `modules/heatmap`.
|
||||
*
|
||||
* @sample highcharts/demo/heatmap/
|
||||
* Simple heatmap
|
||||
* @sample highcharts/demo/heatmap-canvas/
|
||||
* Heavy heatmap
|
||||
*
|
||||
* @extends plotOptions.scatter
|
||||
* @excluding animationLimit, connectEnds, connectNulls, cropThreshold,
|
||||
* dashStyle, findNearestPointBy, getExtremesFromAll, jitter,
|
||||
* linecap, lineWidth, pointInterval, pointIntervalUnit,
|
||||
* pointRange, pointStart, shadow, softThreshold, stacking,
|
||||
* step, threshold, cluster
|
||||
* @product highcharts highmaps
|
||||
* @optionparent plotOptions.heatmap
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* Animation is disabled by default on the heatmap series.
|
||||
*/
|
||||
animation: false,
|
||||
/**
|
||||
* The border width for each heat map item.
|
||||
*/
|
||||
borderWidth: 0,
|
||||
/**
|
||||
* Padding between the points in the heatmap.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
* @since 6.0
|
||||
* @apioption plotOptions.heatmap.pointPadding
|
||||
*/
|
||||
/**
|
||||
* @default value
|
||||
* @apioption plotOptions.heatmap.colorKey
|
||||
*/
|
||||
/**
|
||||
* The main color of the series. In heat maps this color is rarely used,
|
||||
* as we mostly use the color to denote the value of each point. Unless
|
||||
* options are set in the [colorAxis](#colorAxis), the default value
|
||||
* is pulled from the [options.colors](#colors) array.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 4.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.heatmap.color
|
||||
*/
|
||||
/**
|
||||
* The column size - how many X axis units each column in the heatmap
|
||||
* should span.
|
||||
*
|
||||
* @sample {highcharts} maps/demo/heatmap/
|
||||
* One day
|
||||
* @sample {highmaps} maps/demo/heatmap/
|
||||
* One day
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @since 4.0
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.colsize
|
||||
*/
|
||||
/**
|
||||
* The row size - how many Y axis units each heatmap row should span.
|
||||
*
|
||||
* @sample {highcharts} maps/demo/heatmap/
|
||||
* 1 by default
|
||||
* @sample {highmaps} maps/demo/heatmap/
|
||||
* 1 by default
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @since 4.0
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.rowsize
|
||||
*/
|
||||
/**
|
||||
* The color applied to null points. In styled mode, a general CSS class
|
||||
* is applied instead.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
nullColor: '#f7f7f7',
|
||||
dataLabels: {
|
||||
formatter: function () {
|
||||
return this.point.value;
|
||||
},
|
||||
inside: true,
|
||||
verticalAlign: 'middle',
|
||||
crop: false,
|
||||
overflow: false,
|
||||
padding: 0 // #3837
|
||||
},
|
||||
/**
|
||||
* @excluding radius, enabledThreshold
|
||||
* @since 8.1
|
||||
*/
|
||||
marker: {
|
||||
/**
|
||||
* A predefined shape or symbol for the marker. When undefined, the
|
||||
* symbol is pulled from options.symbols. Other possible values are
|
||||
* `'circle'`, `'square'`,`'diamond'`, `'triangle'`,
|
||||
* `'triangle-down'`, `'rect'`, and `'ellipse'`.
|
||||
*
|
||||
* Additionally, the URL to a graphic can be given on this form:
|
||||
* `'url(graphic.png)'`. Note that for the image to be applied to
|
||||
* exported charts, its URL needs to be accessible by the export
|
||||
* server.
|
||||
*
|
||||
* Custom callbacks for symbol path generation can also be added to
|
||||
* `Highcharts.SVGRenderer.prototype.symbols`. The callback is then
|
||||
* used by its method name, as shown in the demo.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/series-marker-symbol/
|
||||
* Predefined, graphic and custom markers
|
||||
* @sample {highstock} highcharts/plotoptions/series-marker-symbol/
|
||||
* Predefined, graphic and custom markers
|
||||
*/
|
||||
symbol: 'rect',
|
||||
/** @ignore-option */
|
||||
radius: 0,
|
||||
lineColor: void 0,
|
||||
states: {
|
||||
/**
|
||||
* @excluding radius, radiusPlus
|
||||
*/
|
||||
hover: {
|
||||
/**
|
||||
* Set the marker's fixed width on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.hover.width
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed height on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.hover.height
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the width of the
|
||||
* selected point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* 20px greater width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.hover.widthPlus
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the height of the
|
||||
* selected point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* 20px greater width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.hover.heightPlus
|
||||
*/
|
||||
/**
|
||||
* The additional line width for a hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-linewidthplus
|
||||
* 5 pixels wider lineWidth on hover
|
||||
* @sample {highmaps} maps/plotoptions/heatmap-marker-states-hover-linewidthplus
|
||||
* 5 pixels wider lineWidth on hover
|
||||
*/
|
||||
lineWidthPlus: 0
|
||||
},
|
||||
/**
|
||||
* @excluding radius
|
||||
*/
|
||||
select: {
|
||||
/**
|
||||
* Set the marker's fixed width on select state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.select.width
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed height on select state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.select.height
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the width of the
|
||||
* selected point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* 20px greater width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.select.widthPlus
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the height of the
|
||||
* selected point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* 20px greater width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.heatmap.marker.states.select.heightPlus
|
||||
*/
|
||||
}
|
||||
}
|
||||
},
|
||||
clip: true,
|
||||
/** @ignore-option */
|
||||
pointRange: null,
|
||||
tooltip: {
|
||||
pointFormat: '{point.x}, {point.y}: {point.value}<br/>'
|
||||
},
|
||||
states: {
|
||||
hover: {
|
||||
/** @ignore-option */
|
||||
halo: false,
|
||||
/**
|
||||
* How much to brighten the point on interaction. Requires the
|
||||
* main color to be defined in hex or rgb(a) format.
|
||||
*
|
||||
* In styled mode, the hover brightening is by default replaced
|
||||
* with a fill-opacity set in the `.highcharts-point:hover`
|
||||
* rule.
|
||||
*/
|
||||
brightness: 0.2
|
||||
}
|
||||
}
|
||||
}, merge(colorMapSeriesMixin, {
|
||||
pointArrayMap: ['y', 'value'],
|
||||
hasPointSpecificOptions: true,
|
||||
getExtremesFromAll: true,
|
||||
directTouch: true,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Override the init method to add point ranges on both axes.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.heatmap#init
|
||||
* @return {void}
|
||||
*/
|
||||
init: function () {
|
||||
var options;
|
||||
Series.prototype.init.apply(this, arguments);
|
||||
options = this.options;
|
||||
// #3758, prevent resetting in setData
|
||||
options.pointRange = pick(options.pointRange, options.colsize || 1);
|
||||
// general point range
|
||||
this.yAxis.axisPointRange = options.rowsize || 1;
|
||||
// Bind new symbol names
|
||||
extend(symbols, {
|
||||
ellipse: symbols.circle,
|
||||
rect: symbols.square
|
||||
});
|
||||
},
|
||||
getSymbol: Series.prototype.getSymbol,
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.heatmap#setClip
|
||||
* @return {void}
|
||||
*/
|
||||
setClip: function (animation) {
|
||||
var series = this, chart = series.chart;
|
||||
Series.prototype.setClip.apply(series, arguments);
|
||||
if (series.options.clip !== false || animation) {
|
||||
series.markerGroup
|
||||
.clip((animation || series.clipBox) && series.sharedClipKey ?
|
||||
chart[series.sharedClipKey] :
|
||||
chart.clipRect);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.heatmap#translate
|
||||
* @return {void}
|
||||
*/
|
||||
translate: function () {
|
||||
var series = this, options = series.options, symbol = options.marker && options.marker.symbol || '', shape = symbols[symbol] ? symbol : 'rect', options = series.options, hasRegularShape = ['circle', 'square'].indexOf(shape) !== -1;
|
||||
series.generatePoints();
|
||||
series.points.forEach(function (point) {
|
||||
var pointAttr, sizeDiff, hasImage, cellAttr = point.getCellAttributes(), shapeArgs = {
|
||||
x: Math.min(cellAttr.x1, cellAttr.x2),
|
||||
y: Math.min(cellAttr.y1, cellAttr.y2),
|
||||
width: Math.max(Math.abs(cellAttr.x2 - cellAttr.x1), 0),
|
||||
height: Math.max(Math.abs(cellAttr.y2 - cellAttr.y1), 0)
|
||||
};
|
||||
hasImage = point.hasImage =
|
||||
(point.marker && point.marker.symbol || symbol || '')
|
||||
.indexOf('url') === 0;
|
||||
// If marker shape is regular (symetric), find shorter
|
||||
// cell's side.
|
||||
if (hasRegularShape) {
|
||||
sizeDiff = Math.abs(shapeArgs.width - shapeArgs.height);
|
||||
shapeArgs.x = Math.min(cellAttr.x1, cellAttr.x2) +
|
||||
(shapeArgs.width < shapeArgs.height ? 0 : sizeDiff / 2);
|
||||
shapeArgs.y = Math.min(cellAttr.y1, cellAttr.y2) +
|
||||
(shapeArgs.width < shapeArgs.height ? sizeDiff / 2 : 0);
|
||||
shapeArgs.width = shapeArgs.height =
|
||||
Math.min(shapeArgs.width, shapeArgs.height);
|
||||
}
|
||||
pointAttr = {
|
||||
plotX: (cellAttr.x1 + cellAttr.x2) / 2,
|
||||
plotY: (cellAttr.y1 + cellAttr.y2) / 2,
|
||||
clientX: (cellAttr.x1 + cellAttr.x2) / 2,
|
||||
shapeType: 'path',
|
||||
shapeArgs: merge(true, shapeArgs, {
|
||||
d: symbols[shape](shapeArgs.x, shapeArgs.y, shapeArgs.width, shapeArgs.height)
|
||||
})
|
||||
};
|
||||
if (hasImage) {
|
||||
point.marker = {
|
||||
width: shapeArgs.width,
|
||||
height: shapeArgs.height
|
||||
};
|
||||
}
|
||||
extend(point, pointAttr);
|
||||
});
|
||||
fireEvent(series, 'afterTranslate');
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.heatmap#pointAttribs
|
||||
* @param {Highcharts.HeatmapPoint} point
|
||||
* @param {string} state
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
*/
|
||||
pointAttribs: function (point, state) {
|
||||
var series = this, attr = Series.prototype.pointAttribs
|
||||
.call(series, point, state), seriesOptions = series.options || {}, plotOptions = series.chart.options.plotOptions || {}, seriesPlotOptions = plotOptions.series || {}, heatmapPlotOptions = plotOptions.heatmap || {}, stateOptions, brightness,
|
||||
// Get old properties in order to keep backward compatibility
|
||||
borderColor = seriesOptions.borderColor ||
|
||||
heatmapPlotOptions.borderColor ||
|
||||
seriesPlotOptions.borderColor, borderWidth = seriesOptions.borderWidth ||
|
||||
heatmapPlotOptions.borderWidth ||
|
||||
seriesPlotOptions.borderWidth ||
|
||||
attr['stroke-width'];
|
||||
// Apply lineColor, or set it to default series color.
|
||||
attr.stroke = ((point && point.marker && point.marker.lineColor) ||
|
||||
(seriesOptions.marker && seriesOptions.marker.lineColor) ||
|
||||
borderColor ||
|
||||
this.color);
|
||||
// Apply old borderWidth property if exists.
|
||||
attr['stroke-width'] = borderWidth;
|
||||
if (state) {
|
||||
stateOptions =
|
||||
merge(seriesOptions.states[state], seriesOptions.marker &&
|
||||
seriesOptions.marker.states[state], point.options.states &&
|
||||
point.options.states[state] || {});
|
||||
brightness = stateOptions.brightness;
|
||||
attr.fill =
|
||||
stateOptions.color ||
|
||||
H.color(attr.fill).brighten(brightness || 0).get();
|
||||
attr.stroke = stateOptions.lineColor;
|
||||
}
|
||||
return attr;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.heatmap#markerAttribs
|
||||
* @param {Highcharts.HeatmapPoint} point
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
*/
|
||||
markerAttribs: function (point, state) {
|
||||
var pointMarkerOptions = point.marker || {}, seriesMarkerOptions = this.options.marker || {}, seriesStateOptions, pointStateOptions, shapeArgs = point.shapeArgs || {}, hasImage = point.hasImage, attribs = {};
|
||||
if (hasImage) {
|
||||
return {
|
||||
x: point.plotX,
|
||||
y: point.plotY
|
||||
};
|
||||
}
|
||||
// Setting width and height attributes on image does not affect
|
||||
// on its dimensions.
|
||||
if (state) {
|
||||
seriesStateOptions = seriesMarkerOptions.states[state] || {};
|
||||
pointStateOptions = pointMarkerOptions.states &&
|
||||
pointMarkerOptions.states[state] || {};
|
||||
[['width', 'x'], ['height', 'y']].forEach(function (dimension) {
|
||||
// Set new width and height basing on state options.
|
||||
attribs[dimension[0]] = (pointStateOptions[dimension[0]] ||
|
||||
seriesStateOptions[dimension[0]] ||
|
||||
shapeArgs[dimension[0]]) + (pointStateOptions[dimension[0] + 'Plus'] ||
|
||||
seriesStateOptions[dimension[0] + 'Plus'] || 0);
|
||||
// Align marker by a new size.
|
||||
attribs[dimension[1]] = shapeArgs[dimension[1]] +
|
||||
(shapeArgs[dimension[0]] - attribs[dimension[0]]) / 2;
|
||||
});
|
||||
}
|
||||
return state ? attribs : shapeArgs;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.heatmap#drawPoints
|
||||
* @return {void}
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var _this = this;
|
||||
// In styled mode, use CSS, otherwise the fill used in the style
|
||||
// sheet will take precedence over the fill attribute.
|
||||
var seriesMarkerOptions = this.options.marker || {};
|
||||
if (seriesMarkerOptions.enabled || this._hasPointMarkers) {
|
||||
Series.prototype.drawPoints.call(this);
|
||||
this.points.forEach(function (point) {
|
||||
point.graphic &&
|
||||
point.graphic[_this.chart.styledMode ? 'css' : 'animate'](_this.colorAttribs(point));
|
||||
});
|
||||
}
|
||||
},
|
||||
// Define hasData function for non-cartesian series.
|
||||
// Returns true if the series has points at all.
|
||||
hasData: function () {
|
||||
return !!this.processedXData.length; // != 0
|
||||
},
|
||||
// Override to also allow null points, used when building the k-d-tree
|
||||
// for tooltips in boost mode.
|
||||
getValidPoints: function (points, insideOnly) {
|
||||
return Series.prototype.getValidPoints.call(this, points, insideOnly, true);
|
||||
},
|
||||
/**
|
||||
* @ignore
|
||||
* @deprecated
|
||||
* @function Highcharts.seriesTypes.heatmap#getBox
|
||||
*/
|
||||
getBox: noop,
|
||||
/**
|
||||
* @private
|
||||
* @borrows Highcharts.LegendSymbolMixin.drawRectangle as Highcharts.seriesTypes.heatmap#drawLegendSymbol
|
||||
*/
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle,
|
||||
/**
|
||||
* @private
|
||||
* @borrows Highcharts.seriesTypes.column#alignDataLabel as Highcharts.seriesTypes.heatmap#alignDataLabel
|
||||
*/
|
||||
alignDataLabel: seriesTypes.column.prototype.alignDataLabel,
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.heatmap#getExtremes
|
||||
* @return {void}
|
||||
*/
|
||||
getExtremes: function () {
|
||||
// Get the extremes from the value data
|
||||
var _a = Series.prototype.getExtremes
|
||||
.call(this, this.valueData), dataMin = _a.dataMin, dataMax = _a.dataMax;
|
||||
if (isNumber(dataMin)) {
|
||||
this.valueMin = dataMin;
|
||||
}
|
||||
if (isNumber(dataMax)) {
|
||||
this.valueMax = dataMax;
|
||||
}
|
||||
// Get the extremes from the y data
|
||||
return Series.prototype.getExtremes.call(this);
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
}), merge(colorMapPointMixin, {
|
||||
/**
|
||||
* Heatmap series only. Padding between the points in the heatmap.
|
||||
* @name Highcharts.Point#pointPadding
|
||||
* @type {number|undefined}
|
||||
*/
|
||||
/**
|
||||
* Heatmap series only. The value of the point, resulting in a color
|
||||
* controled by options as set in the colorAxis configuration.
|
||||
* @name Highcharts.Point#value
|
||||
* @type {number|null|undefined}
|
||||
*/
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Point#applyOptions
|
||||
* @param {Highcharts.HeatmapPointOptions} options
|
||||
* @param {number} x
|
||||
* @return {Highcharts.SVGPathArray}
|
||||
*/
|
||||
applyOptions: function (options, x) {
|
||||
var point = H.Point.prototype
|
||||
.applyOptions.call(this, options, x);
|
||||
point.formatPrefix =
|
||||
point.isNull || point.value === null ?
|
||||
'null' : 'point';
|
||||
return point;
|
||||
},
|
||||
/**
|
||||
* Color points have a value option that determines whether or not it is
|
||||
* a null point
|
||||
* @private
|
||||
* @function Highcharts.HeatmapPoint.isValid
|
||||
* @return {boolean}
|
||||
*/
|
||||
isValid: function () {
|
||||
// undefined is allowed
|
||||
return (this.value !== Infinity &&
|
||||
this.value !== -Infinity);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Point#haloPath
|
||||
* @param {number} size
|
||||
* @return {Highcharts.SVGPathArray}
|
||||
*/
|
||||
haloPath: function (size) {
|
||||
if (!size) {
|
||||
return [];
|
||||
}
|
||||
var rect = this.shapeArgs;
|
||||
return [
|
||||
'M',
|
||||
rect.x - size,
|
||||
rect.y - size,
|
||||
'L',
|
||||
rect.x - size,
|
||||
rect.y + rect.height + size,
|
||||
rect.x + rect.width + size,
|
||||
rect.y + rect.height + size,
|
||||
rect.x + rect.width + size,
|
||||
rect.y - size,
|
||||
'Z'
|
||||
];
|
||||
},
|
||||
getCellAttributes: function () {
|
||||
var point = this, series = point.series, seriesOptions = series.options, xPad = (seriesOptions.colsize || 1) / 2, yPad = (seriesOptions.rowsize || 1) / 2, xAxis = series.xAxis, yAxis = series.yAxis, markerOptions = point.options.marker || series.options.marker, pointPlacement = series.pointPlacementToXValue(), // #7860
|
||||
pointPadding = pick(point.pointPadding, seriesOptions.pointPadding, 0), cellAttr = {
|
||||
x1: clamp(Math.round(xAxis.len -
|
||||
(xAxis.translate(point.x - xPad, false, true, false, true, -pointPlacement) || 0)), -xAxis.len, 2 * xAxis.len),
|
||||
x2: clamp(Math.round(xAxis.len -
|
||||
(xAxis.translate(point.x + xPad, false, true, false, true, -pointPlacement) || 0)), -xAxis.len, 2 * xAxis.len),
|
||||
y1: clamp(Math.round((yAxis.translate(point.y - yPad, false, true, false, true) || 0)), -yAxis.len, 2 * yAxis.len),
|
||||
y2: clamp(Math.round((yAxis.translate(point.y + yPad, false, true, false, true) || 0)), -yAxis.len, 2 * yAxis.len)
|
||||
};
|
||||
// Handle marker's fixed width, and height values including border
|
||||
// and pointPadding while calculating cell attributes.
|
||||
[['width', 'x'], ['height', 'y']].forEach(function (dimension) {
|
||||
var prop = dimension[0], direction = dimension[1];
|
||||
var start = direction + '1', end = direction + '2';
|
||||
var side = Math.abs(cellAttr[start] - cellAttr[end]), borderWidth = markerOptions &&
|
||||
markerOptions.lineWidth || 0, plotPos = Math.abs(cellAttr[start] + cellAttr[end]) / 2;
|
||||
if (markerOptions[prop] &&
|
||||
markerOptions[prop] < side) {
|
||||
cellAttr[start] = plotPos - (markerOptions[prop] / 2) -
|
||||
(borderWidth / 2);
|
||||
cellAttr[end] = plotPos + (markerOptions[prop] / 2) +
|
||||
(borderWidth / 2);
|
||||
}
|
||||
// Handle pointPadding
|
||||
if (pointPadding) {
|
||||
if (direction === 'y') {
|
||||
start = end;
|
||||
end = direction + '1';
|
||||
}
|
||||
cellAttr[start] += pointPadding;
|
||||
cellAttr[end] -= pointPadding;
|
||||
}
|
||||
});
|
||||
return cellAttr;
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
}));
|
||||
/**
|
||||
* A `heatmap` series. If the [type](#series.heatmap.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @productdesc {highcharts}
|
||||
* Requires `modules/heatmap`.
|
||||
*
|
||||
* @extends series,plotOptions.heatmap
|
||||
* @excluding cropThreshold, dataParser, dataURL, pointRange, stack,
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `heatmap` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values
|
||||
* correspond to `x,y,value`. If the first value is a string, it is
|
||||
* applied as the name of the point, and the `x` value is inferred.
|
||||
* The `x` value can also be omitted, in which case the inner arrays
|
||||
* should be of length 2\. Then the `x` value is automatically calculated,
|
||||
* either starting at 0 and incremented by 1, or from `pointStart`
|
||||
* and `pointInterval` given in the series options.
|
||||
*
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 9, 7],
|
||||
* [1, 10, 4],
|
||||
* [2, 6, 3]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.heatmap.turboThreshold),
|
||||
* this option is not available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 3,
|
||||
* value: 10,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 7,
|
||||
* value: 10,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data
|
||||
*/
|
||||
/**
|
||||
* The color of the point. In heat maps the point color is rarely set
|
||||
* explicitly, as we use the color to denote the `value`. Options for
|
||||
* this are set in the [colorAxis](#colorAxis) configuration.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.color
|
||||
*/
|
||||
/**
|
||||
* The value of the point, resulting in a color controled by options
|
||||
* as set in the [colorAxis](#colorAxis) configuration.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.value
|
||||
*/
|
||||
/**
|
||||
* The x value of the point. For datetime axes,
|
||||
* the X value is the timestamp in milliseconds since 1970.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.x
|
||||
*/
|
||||
/**
|
||||
* The y value of the point.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.y
|
||||
*/
|
||||
/**
|
||||
* Point padding for a single point.
|
||||
*
|
||||
* @sample maps/plotoptions/tilemap-pointpadding
|
||||
* Point padding on tiles
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.pointPadding
|
||||
*/
|
||||
/**
|
||||
* @excluding radius, enabledThreshold
|
||||
* @product highcharts highmaps
|
||||
* @since 8.1
|
||||
* @apioption series.heatmap.data.marker
|
||||
*/
|
||||
/**
|
||||
* @excluding radius, enabledThreshold
|
||||
* @product highcharts highmaps
|
||||
* @since 8.1
|
||||
* @apioption series.heatmap.marker
|
||||
*/
|
||||
/**
|
||||
* @excluding radius, radiusPlus
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.hover
|
||||
*/
|
||||
/**
|
||||
* @excluding radius
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.select
|
||||
*/
|
||||
/**
|
||||
* @excluding radius, radiusPlus
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.marker.states.hover
|
||||
*/
|
||||
/**
|
||||
* @excluding radius
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.marker.states.select
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed width on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-linewidthplus
|
||||
* 5 pixels wider lineWidth on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default 0
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.hover.lineWidthPlus
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed width on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.hover.width
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed height on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.hover.height
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the width of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.hover.widthPlus
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the height of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.hover.heightPlus
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the width of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.select.widthPlus
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the height of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.marker.states.select.heightPlus
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed width on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-linewidthplus
|
||||
* 5 pixels wider lineWidth on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default 0
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.marker.states.hover.lineWidthPlus
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed width on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.marker.states.hover.width
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed height on hover state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.marker.states.hover.height
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the width of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highstock
|
||||
* @apioption series.heatmap.data.marker.states.hover.widthPlus
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the height of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highstock
|
||||
* @apioption series.heatmap.data.marker.states.hover.heightPlus
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed width on select state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.marker.states.select.width
|
||||
*/
|
||||
/**
|
||||
* Set the marker's fixed height on select state.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-width
|
||||
* 70px fixed marker's width and height on hover
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.heatmap.data.marker.states.select.height
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the width of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highstock
|
||||
* @apioption series.heatmap.data.marker.states.select.widthPlus
|
||||
*/
|
||||
/**
|
||||
* The number of pixels to increase the height of the
|
||||
* hovered point.
|
||||
*
|
||||
* @sample {highcharts} maps/plotoptions/heatmap-marker-states-hover-widthplus
|
||||
* One day
|
||||
*
|
||||
* @type {number|undefined}
|
||||
* @default undefined
|
||||
* @product highcharts highstock
|
||||
* @apioption series.heatmap.data.marker.states.select.heightPlus
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,193 @@
|
||||
/* *
|
||||
*
|
||||
* Copyright (c) 2010-2017 Highsoft AS
|
||||
* Author: Sebastian Domas
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../Core/Utilities.js';
|
||||
var arrayMax = U.arrayMax, arrayMin = U.arrayMin, correctFloat = U.correctFloat, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, seriesType = U.seriesType;
|
||||
import derivedSeriesMixin from '../Mixins/DerivedSeries.js';
|
||||
/* ************************************************************************** *
|
||||
* HISTOGRAM
|
||||
* ************************************************************************** */
|
||||
/**
|
||||
* A dictionary with formulas for calculating number of bins based on the
|
||||
* base series
|
||||
**/
|
||||
var binsNumberFormulas = {
|
||||
'square-root': function (baseSeries) {
|
||||
return Math.ceil(Math.sqrt(baseSeries.options.data.length));
|
||||
},
|
||||
'sturges': function (baseSeries) {
|
||||
return Math.ceil(Math.log(baseSeries.options.data.length) * Math.LOG2E);
|
||||
},
|
||||
'rice': function (baseSeries) {
|
||||
return Math.ceil(2 * Math.pow(baseSeries.options.data.length, 1 / 3));
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Returns a function for mapping number to the closed (right opened) bins
|
||||
* @private
|
||||
* @param {Array<number>} bins - Width of the bins
|
||||
* @return {Function}
|
||||
**/
|
||||
function fitToBinLeftClosed(bins) {
|
||||
return function (y) {
|
||||
var i = 1;
|
||||
while (bins[i] <= y) {
|
||||
i++;
|
||||
}
|
||||
return bins[--i];
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Histogram class
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.histogram
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('histogram', 'column',
|
||||
/**
|
||||
* A histogram is a column series which represents the distribution of the
|
||||
* data set in the base series. Histogram splits data into bins and shows
|
||||
* their frequencies.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/histogram/
|
||||
* Histogram
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @excluding boostThreshold, dragDrop, pointInterval, pointIntervalUnit,
|
||||
* stacking, boostBlending
|
||||
* @product highcharts
|
||||
* @since 6.0.0
|
||||
* @requires modules/histogram
|
||||
* @optionparent plotOptions.histogram
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* A preferable number of bins. It is a suggestion, so a histogram may
|
||||
* have a different number of bins. By default it is set to the square
|
||||
* root of the base series' data length. Available options are:
|
||||
* `square-root`, `sturges`, `rice`. You can also define a function
|
||||
* which takes a `baseSeries` as a parameter and should return a
|
||||
* positive integer.
|
||||
*
|
||||
* @type {"square-root"|"sturges"|"rice"|number|function}
|
||||
*/
|
||||
binsNumber: 'square-root',
|
||||
/**
|
||||
* Width of each bin. By default the bin's width is calculated as
|
||||
* `(max - min) / number of bins`. This option takes precedence over
|
||||
* [binsNumber](#plotOptions.histogram.binsNumber).
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
binWidth: void 0,
|
||||
pointPadding: 0,
|
||||
groupPadding: 0,
|
||||
grouping: false,
|
||||
pointPlacement: 'between',
|
||||
tooltip: {
|
||||
headerFormat: '',
|
||||
pointFormat: ('<span style="font-size: 10px">{point.x} - {point.x2}' +
|
||||
'</span><br/>' +
|
||||
'<span style="color:{point.color}">\u25CF</span>' +
|
||||
' {series.name} <b>{point.y}</b><br/>')
|
||||
}
|
||||
}, merge(derivedSeriesMixin, {
|
||||
setDerivedData: function () {
|
||||
var yData = this.baseSeries.yData;
|
||||
if (!yData.length) {
|
||||
return;
|
||||
}
|
||||
var data = this.derivedData(yData, this.binsNumber(), this.options.binWidth);
|
||||
this.setData(data, false);
|
||||
},
|
||||
derivedData: function (baseData, binsNumber, binWidth) {
|
||||
var series = this, max = arrayMax(baseData),
|
||||
// Float correction needed, because first frequency value is not
|
||||
// corrected when generating frequencies (within for loop).
|
||||
min = correctFloat(arrayMin(baseData)), frequencies = [], bins = {}, data = [], x, fitToBin;
|
||||
binWidth = series.binWidth = (correctFloat(isNumber(binWidth) ?
|
||||
(binWidth || 1) :
|
||||
(max - min) / binsNumber));
|
||||
// #12077 negative pointRange causes wrong calculations,
|
||||
// browser hanging.
|
||||
series.options.pointRange = Math.max(binWidth, 0);
|
||||
// If binWidth is 0 then max and min are equaled,
|
||||
// increment the x with some positive value to quit the loop
|
||||
for (x = min;
|
||||
// This condition is needed because of the margin of error while
|
||||
// operating on decimal numbers. Without that, additional bin
|
||||
// was sometimes noticeable on the graph, because of too small
|
||||
// precision of float correction.
|
||||
x < max &&
|
||||
(series.userOptions.binWidth ||
|
||||
correctFloat(max - x) >= binWidth ||
|
||||
// #13069 - Every add and subtract operation should
|
||||
// be corrected, due to general problems with
|
||||
// operations on float numbers in JS.
|
||||
correctFloat(correctFloat(min + (frequencies.length * binWidth)) -
|
||||
x) <= 0); x = correctFloat(x + binWidth)) {
|
||||
frequencies.push(x);
|
||||
bins[x] = 0;
|
||||
}
|
||||
if (bins[min] !== 0) {
|
||||
frequencies.push(correctFloat(min));
|
||||
bins[correctFloat(min)] = 0;
|
||||
}
|
||||
fitToBin = fitToBinLeftClosed(frequencies.map(function (elem) {
|
||||
return parseFloat(elem);
|
||||
}));
|
||||
baseData.forEach(function (y) {
|
||||
var x = correctFloat(fitToBin(y));
|
||||
bins[x]++;
|
||||
});
|
||||
objectEach(bins, function (frequency, x) {
|
||||
data.push({
|
||||
x: Number(x),
|
||||
y: frequency,
|
||||
x2: correctFloat(Number(x) + binWidth)
|
||||
});
|
||||
});
|
||||
data.sort(function (a, b) {
|
||||
return a.x - b.x;
|
||||
});
|
||||
return data;
|
||||
},
|
||||
binsNumber: function () {
|
||||
var binsNumberOption = this.options.binsNumber;
|
||||
var binsNumber = binsNumberFormulas[binsNumberOption] ||
|
||||
// #7457
|
||||
(typeof binsNumberOption === 'function' && binsNumberOption);
|
||||
return Math.ceil((binsNumber && binsNumber(this.baseSeries)) ||
|
||||
(isNumber(binsNumberOption) ?
|
||||
binsNumberOption :
|
||||
binsNumberFormulas['square-root'](this.baseSeries)));
|
||||
}
|
||||
}));
|
||||
/**
|
||||
* A `histogram` series. If the [type](#series.histogram.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.histogram
|
||||
* @excluding data, dataParser, dataURL, boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @since 6.0.0
|
||||
* @requires modules/histogram
|
||||
* @apioption series.histogram
|
||||
*/
|
||||
/**
|
||||
* An integer identifying the index to use for the base series, or a string
|
||||
* representing the id of the series.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @apioption series.histogram.baseSeries
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,456 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2020 Torstein Honsi
|
||||
*
|
||||
* Item series type for Highcharts
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import O from '../Core/Options.js';
|
||||
var defaultOptions = O.defaultOptions;
|
||||
import U from '../Core/Utilities.js';
|
||||
var defined = U.defined, extend = U.extend, fireEvent = U.fireEvent, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Series/Series.js';
|
||||
var piePoint = H.seriesTypes.pie.prototype.pointClass.prototype;
|
||||
/**
|
||||
* The item series type.
|
||||
*
|
||||
* @requires module:modules/item-series
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.item
|
||||
*
|
||||
* @augments Highcharts.seriesTypes.pie
|
||||
*/
|
||||
seriesType('item',
|
||||
// Inherits pie as the most tested non-cartesian series with individual
|
||||
// point legend, tooltips etc. Only downside is we need to re-enable
|
||||
// marker options.
|
||||
'pie',
|
||||
/**
|
||||
* An item chart is an infographic chart where a number of items are laid
|
||||
* out in either a rectangular or circular pattern. It can be used to
|
||||
* visualize counts within a group, or for the circular pattern, typically
|
||||
* a parliament.
|
||||
*
|
||||
* The circular layout has much in common with a pie chart. Many of the item
|
||||
* series options, like `center`, `size` and data label positioning, are
|
||||
* inherited from the pie series and don't apply for rectangular layouts.
|
||||
*
|
||||
* @sample highcharts/demo/parliament-chart
|
||||
* Parliament chart (circular item chart)
|
||||
* @sample highcharts/series-item/rectangular
|
||||
* Rectangular item chart
|
||||
* @sample highcharts/series-item/symbols
|
||||
* Infographic with symbols
|
||||
*
|
||||
* @extends plotOptions.pie
|
||||
* @since 7.1.0
|
||||
* @product highcharts
|
||||
* @excluding borderColor, borderWidth, depth, linecap, shadow,
|
||||
* slicedOffset
|
||||
* @requires modules/item-series
|
||||
* @optionparent plotOptions.item
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* In circular view, the end angle of the item layout, in degrees where
|
||||
* 0 is up.
|
||||
*
|
||||
* @sample highcharts/demo/parliament-chart
|
||||
* Parliament chart
|
||||
* @type {undefined|number}
|
||||
*/
|
||||
endAngle: void 0,
|
||||
/**
|
||||
* In circular view, the size of the inner diameter of the circle. Can
|
||||
* be a percentage or pixel value. Percentages are relative to the outer
|
||||
* perimeter. Pixel values are given as integers.
|
||||
*
|
||||
* If the `rows` option is set, it overrides the `innerSize` setting.
|
||||
*
|
||||
* @sample highcharts/demo/parliament-chart
|
||||
* Parliament chart
|
||||
* @type {string|number}
|
||||
*/
|
||||
innerSize: '40%',
|
||||
/**
|
||||
* The padding between the items, given in relative size where the size
|
||||
* of the item is 1.
|
||||
* @type {number}
|
||||
*/
|
||||
itemPadding: 0.1,
|
||||
/**
|
||||
* The layout of the items in rectangular view. Can be either
|
||||
* `horizontal` or `vertical`.
|
||||
* @sample highcharts/series-item/symbols
|
||||
* Horizontal layout
|
||||
* @type {string}
|
||||
*/
|
||||
layout: 'vertical',
|
||||
/**
|
||||
* @extends plotOptions.series.marker
|
||||
*/
|
||||
marker: merge(defaultOptions.plotOptions.line.marker, {
|
||||
radius: null
|
||||
}),
|
||||
/**
|
||||
* The number of rows to display in the rectangular or circular view. If
|
||||
* the `innerSize` is set, it will be overridden by the `rows` setting.
|
||||
*
|
||||
* @sample highcharts/series-item/rows-columns
|
||||
* Fixed row count
|
||||
* @type {number}
|
||||
*/
|
||||
rows: void 0,
|
||||
crisp: false,
|
||||
showInLegend: true,
|
||||
/**
|
||||
* In circular view, the start angle of the item layout, in degrees
|
||||
* where 0 is up.
|
||||
*
|
||||
* @sample highcharts/demo/parliament-chart
|
||||
* Parliament chart
|
||||
* @type {undefined|number}
|
||||
*/
|
||||
startAngle: void 0
|
||||
},
|
||||
// Prototype members
|
||||
{
|
||||
markerAttribs: void 0,
|
||||
translate: function (positions) {
|
||||
// Initialize chart without setting data, #13379.
|
||||
if (this.total === 0) {
|
||||
this.center = this.getCenter();
|
||||
}
|
||||
if (!this.slots) {
|
||||
this.slots = [];
|
||||
}
|
||||
if (isNumber(this.options.startAngle) &&
|
||||
isNumber(this.options.endAngle)) {
|
||||
H.seriesTypes.pie.prototype.translate.apply(this, arguments);
|
||||
this.slots = this.getSlots();
|
||||
}
|
||||
else {
|
||||
this.generatePoints();
|
||||
fireEvent(this, 'afterTranslate');
|
||||
}
|
||||
},
|
||||
// Get the semi-circular slots
|
||||
getSlots: function () {
|
||||
var center = this.center, diameter = center[2], innerSize = center[3], row, slots = this.slots, x, y, rowRadius, rowLength, colCount, increment, angle, col, itemSize = 0, rowCount, fullAngle = (this.endAngleRad - this.startAngleRad), itemCount = Number.MAX_VALUE, finalItemCount, rows, testRows, rowsOption = this.options.rows,
|
||||
// How many rows (arcs) should be used
|
||||
rowFraction = (diameter - innerSize) / diameter, isCircle = fullAngle % (2 * Math.PI) === 0;
|
||||
// Increase the itemSize until we find the best fit
|
||||
while (itemCount > this.total + (rows && isCircle ? rows.length : 0)) {
|
||||
finalItemCount = itemCount;
|
||||
// Reset
|
||||
slots.length = 0;
|
||||
itemCount = 0;
|
||||
// Now rows is the last successful run
|
||||
rows = testRows;
|
||||
testRows = [];
|
||||
itemSize++;
|
||||
// Total number of rows (arcs) from the center to the
|
||||
// perimeter
|
||||
rowCount = diameter / itemSize / 2;
|
||||
if (rowsOption) {
|
||||
innerSize = ((rowCount - rowsOption) / rowCount) * diameter;
|
||||
if (innerSize >= 0) {
|
||||
rowCount = rowsOption;
|
||||
// If innerSize is negative, we are trying to set too
|
||||
// many rows in the rows option, so fall back to
|
||||
// treating it as innerSize 0
|
||||
}
|
||||
else {
|
||||
innerSize = 0;
|
||||
rowFraction = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rowCount = Math.floor(rowCount * rowFraction);
|
||||
}
|
||||
for (row = rowCount; row > 0; row--) {
|
||||
rowRadius = (innerSize + (row / rowCount) *
|
||||
(diameter - innerSize - itemSize)) / 2;
|
||||
rowLength = fullAngle * rowRadius;
|
||||
colCount = Math.ceil(rowLength / itemSize);
|
||||
testRows.push({
|
||||
rowRadius: rowRadius,
|
||||
rowLength: rowLength,
|
||||
colCount: colCount
|
||||
});
|
||||
itemCount += colCount + 1;
|
||||
}
|
||||
}
|
||||
if (!rows) {
|
||||
return;
|
||||
}
|
||||
// We now have more slots than we have total items. Loop over
|
||||
// the rows and remove the last slot until the count is correct.
|
||||
// For each iteration we sort the last slot by the angle, and
|
||||
// remove those with the highest angles.
|
||||
var overshoot = finalItemCount - this.total -
|
||||
(isCircle ? rows.length : 0);
|
||||
/**
|
||||
* @private
|
||||
* @param {Highcharts.ItemRowContainerObject} item
|
||||
* Wrapped object with angle and row
|
||||
* @return {void}
|
||||
*/
|
||||
function cutOffRow(item) {
|
||||
if (overshoot > 0) {
|
||||
item.row.colCount--;
|
||||
overshoot--;
|
||||
}
|
||||
}
|
||||
while (overshoot > 0) {
|
||||
rows
|
||||
// Return a simplified representation of the angle of
|
||||
// the last slot within each row.
|
||||
.map(function (row) {
|
||||
return {
|
||||
angle: row.colCount / row.rowLength,
|
||||
row: row
|
||||
};
|
||||
})
|
||||
// Sort by the angles...
|
||||
.sort(function (a, b) {
|
||||
return b.angle - a.angle;
|
||||
})
|
||||
// ...so that we can ignore the items with the lowest
|
||||
// angles...
|
||||
.slice(0, Math.min(overshoot, Math.ceil(rows.length / 2)))
|
||||
// ...and remove the ones with the highest angles
|
||||
.forEach(cutOffRow);
|
||||
}
|
||||
rows.forEach(function (row) {
|
||||
var rowRadius = row.rowRadius, colCount = row.colCount;
|
||||
increment = colCount ? fullAngle / colCount : 0;
|
||||
for (col = 0; col <= colCount; col += 1) {
|
||||
angle = this.startAngleRad + col * increment;
|
||||
x = center[0] + Math.cos(angle) * rowRadius;
|
||||
y = center[1] + Math.sin(angle) * rowRadius;
|
||||
slots.push({ x: x, y: y, angle: angle });
|
||||
}
|
||||
}, this);
|
||||
// Sort by angle
|
||||
slots.sort(function (a, b) {
|
||||
return a.angle - b.angle;
|
||||
});
|
||||
this.itemSize = itemSize;
|
||||
return slots;
|
||||
},
|
||||
getRows: function () {
|
||||
var rows = this.options.rows, cols, ratio;
|
||||
// Get the row count that gives the most square cells
|
||||
if (!rows) {
|
||||
ratio = this.chart.plotWidth / this.chart.plotHeight;
|
||||
rows = Math.sqrt(this.total);
|
||||
if (ratio > 1) {
|
||||
rows = Math.ceil(rows);
|
||||
while (rows > 0) {
|
||||
cols = this.total / rows;
|
||||
if (cols / rows > ratio) {
|
||||
break;
|
||||
}
|
||||
rows--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
rows = Math.floor(rows);
|
||||
while (rows < this.total) {
|
||||
cols = this.total / rows;
|
||||
if (cols / rows < ratio) {
|
||||
break;
|
||||
}
|
||||
rows++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
},
|
||||
drawPoints: function () {
|
||||
var series = this, options = this.options, renderer = series.chart.renderer, seriesMarkerOptions = options.marker, borderWidth = this.borderWidth, crisp = borderWidth % 2 ? 0.5 : 1, i = 0, rows = this.getRows(), cols = Math.ceil(this.total / rows), cellWidth = this.chart.plotWidth / cols, cellHeight = this.chart.plotHeight / rows, itemSize = this.itemSize || Math.min(cellWidth, cellHeight);
|
||||
/*
|
||||
this.slots.forEach(slot => {
|
||||
this.chart.renderer.circle(slot.x, slot.y, 6)
|
||||
.attr({
|
||||
fill: 'silver'
|
||||
})
|
||||
.add(this.group);
|
||||
});
|
||||
//*/
|
||||
this.points.forEach(function (point) {
|
||||
var attr, graphics, pointAttr, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol ||
|
||||
seriesMarkerOptions.symbol), r = pick(pointMarkerOptions.radius, seriesMarkerOptions.radius), size = defined(r) ? 2 * r : itemSize, padding = size * options.itemPadding, x, y, width, height;
|
||||
point.graphics = graphics = point.graphics || {};
|
||||
if (!series.chart.styledMode) {
|
||||
pointAttr = series.pointAttribs(point, point.selected && 'select');
|
||||
}
|
||||
if (!point.isNull && point.visible) {
|
||||
if (!point.graphic) {
|
||||
point.graphic = renderer.g('point')
|
||||
.add(series.group);
|
||||
}
|
||||
for (var val = 0; val < point.y; val++) {
|
||||
// Semi-circle
|
||||
if (series.center && series.slots) {
|
||||
// Fill up the slots from left to right
|
||||
var slot = series.slots.shift();
|
||||
x = slot.x - itemSize / 2;
|
||||
y = slot.y - itemSize / 2;
|
||||
}
|
||||
else if (options.layout === 'horizontal') {
|
||||
x = cellWidth * (i % cols);
|
||||
y = cellHeight * Math.floor(i / cols);
|
||||
}
|
||||
else {
|
||||
x = cellWidth * Math.floor(i / rows);
|
||||
y = cellHeight * (i % rows);
|
||||
}
|
||||
x += padding;
|
||||
y += padding;
|
||||
width = Math.round(size - 2 * padding);
|
||||
height = width;
|
||||
if (series.options.crisp) {
|
||||
x = Math.round(x) - crisp;
|
||||
y = Math.round(y) + crisp;
|
||||
}
|
||||
attr = {
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
if (typeof r !== 'undefined') {
|
||||
attr.r = r;
|
||||
}
|
||||
if (graphics[val]) {
|
||||
graphics[val].animate(attr);
|
||||
}
|
||||
else {
|
||||
graphics[val] = renderer
|
||||
.symbol(symbol, null, null, null, null, {
|
||||
backgroundSize: 'within'
|
||||
})
|
||||
.attr(extend(attr, pointAttr))
|
||||
.add(point.graphic);
|
||||
}
|
||||
graphics[val].isActive = true;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
objectEach(graphics, function (graphic, key) {
|
||||
if (!graphic.isActive) {
|
||||
graphic.destroy();
|
||||
delete graphics[key];
|
||||
}
|
||||
else {
|
||||
graphic.isActive = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
drawDataLabels: function () {
|
||||
if (this.center && this.slots) {
|
||||
H.seriesTypes.pie.prototype.drawDataLabels.call(this);
|
||||
// else, it's just a dot chart with no natural place to put the
|
||||
// data labels
|
||||
}
|
||||
else {
|
||||
this.points.forEach(function (point) {
|
||||
point.destroyElements({ dataLabel: 1 });
|
||||
});
|
||||
}
|
||||
},
|
||||
// Fade in the whole chart
|
||||
animate: function (init) {
|
||||
if (init) {
|
||||
this.group.attr({
|
||||
opacity: 0
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.group.animate({
|
||||
opacity: 1
|
||||
}, this.options.animation);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Point class
|
||||
{
|
||||
connectorShapes: piePoint.connectorShapes,
|
||||
getConnectorPath: piePoint.getConnectorPath,
|
||||
setVisible: piePoint.setVisible,
|
||||
getTranslate: piePoint.getTranslate
|
||||
});
|
||||
/**
|
||||
* An `item` series. If the [type](#series.item.type) option is not specified,
|
||||
* it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.item
|
||||
* @excluding dataParser, dataURL, stack, xAxis, yAxis, dataSorting,
|
||||
* boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/item-series
|
||||
* @apioption series.item
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `item` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.item.turboThreshold),
|
||||
* this option is not available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 1,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 7,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<string,(number|null)>|null|*>}
|
||||
* @extends series.pie.data
|
||||
* @excludes sliced
|
||||
* @product highcharts
|
||||
* @apioption series.item.data
|
||||
*/
|
||||
/**
|
||||
* The sequential index of the data point in the legend.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.pie.data.legendIndex
|
||||
*/
|
||||
''; // adds the doclets above to the transpiled file
|
||||
@@ -0,0 +1,160 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Sebastian Bochan, Rafal Sebestjanski
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
var areaProto = H.seriesTypes.area.prototype, colProto = H.seriesTypes.column.prototype;
|
||||
/**
|
||||
* The lollipop series is a carteseian series with a line anchored from
|
||||
* the x axis and a dot at the end to mark the value.
|
||||
* Requires `highcharts-more.js`, `modules/dumbbell.js` and
|
||||
* `modules/lollipop.js`.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/lollipop/
|
||||
* Lollipop chart
|
||||
* @sample {highcharts} highcharts/series-dumbbell/styled-mode-dumbbell/
|
||||
* Styled mode
|
||||
*
|
||||
* @extends plotOptions.dumbbell
|
||||
* @product highcharts highstock
|
||||
* @excluding fillColor, fillOpacity, lineWidth, stack, stacking, lowColor,
|
||||
* stickyTracking, trackByArea
|
||||
* @since 8.0.0
|
||||
* @optionparent plotOptions.lollipop
|
||||
*/
|
||||
seriesType('lollipop', 'dumbbell', {
|
||||
/** @ignore-option */
|
||||
lowColor: void 0,
|
||||
/** @ignore-option */
|
||||
threshold: 0,
|
||||
/** @ignore-option */
|
||||
connectorWidth: 1,
|
||||
/** @ignore-option */
|
||||
groupPadding: 0.2,
|
||||
/** @ignore-option */
|
||||
pointPadding: 0.1,
|
||||
/** @ignore-option */
|
||||
states: {
|
||||
hover: {
|
||||
/** @ignore-option */
|
||||
lineWidthPlus: 0,
|
||||
/** @ignore-option */
|
||||
connectorWidthPlus: 1,
|
||||
/** @ignore-option */
|
||||
halo: false
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{series.color}">●</span> {series.name}: <b>{point.y}</b><br/>'
|
||||
}
|
||||
}, {
|
||||
pointArrayMap: ['y'],
|
||||
pointValKey: 'y',
|
||||
toYData: function (point) {
|
||||
return [H.pick(point.y, point.low)];
|
||||
},
|
||||
translatePoint: areaProto.translate,
|
||||
drawPoint: areaProto.drawPoints,
|
||||
drawDataLabels: colProto.drawDataLabels,
|
||||
setShapeArgs: colProto.translate
|
||||
}, {
|
||||
pointSetState: areaProto.pointClass.prototype.setState,
|
||||
setState: H.seriesTypes.dumbbell.prototype.pointClass.prototype.setState,
|
||||
init: function (series, options, x) {
|
||||
if (H.isObject(options) && 'low' in options) {
|
||||
options.y = options.low;
|
||||
delete options.low;
|
||||
}
|
||||
return H.Point.prototype.init.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The `lollipop` series. If the [type](#series.lollipop.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.lollipop,
|
||||
* @excluding boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @requires modules/dumbbell
|
||||
* @requires modules/lollipop
|
||||
* @apioption series.lollipop
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `lollipop` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 6],
|
||||
* [1, 2],
|
||||
* [2, 6]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.lollipop.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 9,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00",
|
||||
* connectorWidth: 3,
|
||||
* connectorColor: "#FF00FF"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 6,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.dumbbell.data
|
||||
* @excluding high, low, lowColor
|
||||
* @product highcharts highstock
|
||||
* @apioption series.lollipop.data
|
||||
*/
|
||||
/**
|
||||
* The y value of the point.
|
||||
*
|
||||
* @type {number|null}
|
||||
* @product highcharts highstock
|
||||
* @apioption series.line.data.y
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,244 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Point from '../Core/Series/Point.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var merge = U.merge, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import '../Series/Bubble/BubbleSeries.js';
|
||||
var seriesTypes = H.seriesTypes;
|
||||
// The mapbubble series type
|
||||
if (seriesTypes.bubble) {
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.mapbubble
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('mapbubble', 'bubble'
|
||||
/**
|
||||
* A map bubble series is a bubble series laid out on top of a map
|
||||
* series, where each bubble is tied to a specific map area.
|
||||
*
|
||||
* @sample maps/demo/map-bubble/
|
||||
* Map bubble chart
|
||||
*
|
||||
* @extends plotOptions.bubble
|
||||
* @product highmaps
|
||||
* @optionparent plotOptions.mapbubble
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* The main color of the series. This color affects both the fill
|
||||
* and the stroke of the bubble. For enhanced control, use `marker`
|
||||
* options.
|
||||
*
|
||||
* @sample {highmaps} maps/plotoptions/mapbubble-color/
|
||||
* Pink bubbles
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @apioption plotOptions.mapbubble.color
|
||||
*/
|
||||
/**
|
||||
* Whether to display negative sized bubbles. The threshold is
|
||||
* given by the [zThreshold](#plotOptions.mapbubble.zThreshold)
|
||||
* option, and negative bubbles can be visualized by setting
|
||||
* [negativeColor](#plotOptions.bubble.negativeColor).
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default true
|
||||
* @apioption plotOptions.mapbubble.displayNegative
|
||||
*/
|
||||
/**
|
||||
* @sample {highmaps} maps/demo/map-bubble/
|
||||
* Bubble size
|
||||
*
|
||||
* @apioption plotOptions.mapbubble.maxSize
|
||||
*/
|
||||
/**
|
||||
* @sample {highmaps} maps/demo/map-bubble/
|
||||
* Bubble size
|
||||
*
|
||||
* @apioption plotOptions.mapbubble.minSize
|
||||
*/
|
||||
/**
|
||||
* When a point's Z value is below the
|
||||
* [zThreshold](#plotOptions.mapbubble.zThreshold) setting, this
|
||||
* color is used.
|
||||
*
|
||||
* @sample {highmaps} maps/plotoptions/mapbubble-negativecolor/
|
||||
* Negative color below a threshold
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @apioption plotOptions.mapbubble.negativeColor
|
||||
*/
|
||||
/**
|
||||
* Whether the bubble's value should be represented by the area or
|
||||
* the width of the bubble. The default, `area`, corresponds best to
|
||||
* the human perception of the size of each bubble.
|
||||
*
|
||||
* @type {Highcharts.BubbleSizeByValue}
|
||||
* @default area
|
||||
* @apioption plotOptions.mapbubble.sizeBy
|
||||
*/
|
||||
/**
|
||||
* When this is true, the absolute value of z determines the size
|
||||
* of the bubble. This means that with the default `zThreshold` of
|
||||
* 0, a bubble of value -1 will have the same size as a bubble of
|
||||
* value 1, while a bubble of value 0 will have a smaller size
|
||||
* according to `minSize`.
|
||||
*
|
||||
* @sample {highmaps} highcharts/plotoptions/bubble-sizebyabsolutevalue/
|
||||
* Size by absolute value, various thresholds
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 1.1.9
|
||||
* @apioption plotOptions.mapbubble.sizeByAbsoluteValue
|
||||
*/
|
||||
/**
|
||||
* The minimum for the Z value range. Defaults to the highest Z
|
||||
* value in the data.
|
||||
*
|
||||
* @see [zMax](#plotOptions.mapbubble.zMin)
|
||||
*
|
||||
* @sample {highmaps} highcharts/plotoptions/bubble-zmin-zmax/
|
||||
* Z has a possible range of 0-100
|
||||
*
|
||||
* @type {number}
|
||||
* @since 1.0.3
|
||||
* @apioption plotOptions.mapbubble.zMax
|
||||
*/
|
||||
/**
|
||||
* The minimum for the Z value range. Defaults to the lowest Z value
|
||||
* in the data.
|
||||
*
|
||||
* @see [zMax](#plotOptions.mapbubble.zMax)
|
||||
*
|
||||
* @sample {highmaps} highcharts/plotoptions/bubble-zmin-zmax/
|
||||
* Z has a possible range of 0-100
|
||||
*
|
||||
* @type {number}
|
||||
* @since 1.0.3
|
||||
* @apioption plotOptions.mapbubble.zMin
|
||||
*/
|
||||
/**
|
||||
* When [displayNegative](#plotOptions.mapbubble.displayNegative)
|
||||
* is `false`, bubbles with lower Z values are skipped. When
|
||||
* `displayNegative` is `true` and a
|
||||
* [negativeColor](#plotOptions.mapbubble.negativeColor) is given,
|
||||
* points with lower Z is colored.
|
||||
*
|
||||
* @sample {highmaps} maps/plotoptions/mapbubble-negativecolor/
|
||||
* Negative color below a threshold
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
* @apioption plotOptions.mapbubble.zThreshold
|
||||
*/
|
||||
animationLimit: 500,
|
||||
tooltip: {
|
||||
pointFormat: '{point.name}: {point.z}'
|
||||
}
|
||||
// Prototype members
|
||||
}, {
|
||||
xyFromShape: true,
|
||||
type: 'mapbubble',
|
||||
// If one single value is passed, it is interpreted as z
|
||||
pointArrayMap: ['z'],
|
||||
// Return the map area identified by the dataJoinBy option
|
||||
getMapData: seriesTypes.map.prototype.getMapData,
|
||||
getBox: seriesTypes.map.prototype.getBox,
|
||||
setData: seriesTypes.map.prototype.setData,
|
||||
setOptions: seriesTypes.map.prototype.setOptions
|
||||
// Point class
|
||||
}, {
|
||||
applyOptions: function (options, x) {
|
||||
var point;
|
||||
if (options &&
|
||||
typeof options.lat !== 'undefined' &&
|
||||
typeof options.lon !== 'undefined') {
|
||||
point = Point.prototype.applyOptions.call(this, merge(options, this.series.chart.fromLatLonToPoint(options)), x);
|
||||
}
|
||||
else {
|
||||
point = seriesTypes.map.prototype.pointClass.prototype
|
||||
.applyOptions.call(this, options, x);
|
||||
}
|
||||
return point;
|
||||
},
|
||||
isValid: function () {
|
||||
return typeof this.z === 'number';
|
||||
},
|
||||
ttBelow: false
|
||||
});
|
||||
}
|
||||
/**
|
||||
* A `mapbubble` series. If the [type](#series.mapbubble.type) option
|
||||
* is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.mapbubble
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highmaps
|
||||
* @apioption series.mapbubble
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `mapbubble` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values
|
||||
* will be interpreted as `z` options. Example:
|
||||
*
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.mapbubble.turboThreshold),
|
||||
* this option is not available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* z: 9,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* z: 10,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<number|null|*>}
|
||||
* @extends series.mappoint.data
|
||||
* @excluding labelrank, middleX, middleY, path, value, x, y, lat, lon
|
||||
* @product highmaps
|
||||
* @apioption series.mapbubble.data
|
||||
*/
|
||||
/**
|
||||
* While the `x` and `y` values of the bubble are determined by the
|
||||
* underlying map, the `z` indicates the actual value that gives the
|
||||
* size of the bubble.
|
||||
*
|
||||
* @sample {highmaps} maps/demo/map-bubble/
|
||||
* Bubble
|
||||
*
|
||||
* @type {number|null}
|
||||
* @product highmaps
|
||||
* @apioption series.mapbubble.data.z
|
||||
*/
|
||||
/**
|
||||
* @excluding enabled, enabledThreshold, height, radius, width
|
||||
* @apioption series.mapbubble.marker
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,127 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
var seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.mapline
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('mapline', 'map',
|
||||
/**
|
||||
* A mapline series is a special case of the map series where the value
|
||||
* colors are applied to the strokes rather than the fills. It can also be
|
||||
* used for freeform drawing, like dividers, in the map.
|
||||
*
|
||||
* @sample maps/demo/mapline-mappoint/
|
||||
* Mapline and map-point chart
|
||||
*
|
||||
* @extends plotOptions.map
|
||||
* @product highmaps
|
||||
* @optionparent plotOptions.mapline
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* The width of the map line.
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* Fill color for the map line shapes
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
*/
|
||||
fillColor: 'none'
|
||||
}, {
|
||||
type: 'mapline',
|
||||
colorProp: 'stroke',
|
||||
pointAttrToOptions: {
|
||||
'stroke': 'color',
|
||||
'stroke-width': 'lineWidth'
|
||||
},
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Get presentational attributes
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.mapline#pointAttribs
|
||||
* @param {Highcharts.Point} point
|
||||
* @param {string} state
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
*/
|
||||
pointAttribs: function (point, state) {
|
||||
var attr = seriesTypes.map.prototype.pointAttribs.call(this, point, state);
|
||||
// The difference from a map series is that the stroke takes the
|
||||
// point color
|
||||
attr.fill = this.options.fillColor;
|
||||
return attr;
|
||||
},
|
||||
drawLegendSymbol: seriesTypes.line.prototype.drawLegendSymbol
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* A `mapline` series. If the [type](#series.mapline.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.mapline
|
||||
* @excluding dataParser, dataURL, marker
|
||||
* @product highmaps
|
||||
* @apioption series.mapline
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `mapline` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values
|
||||
* will be interpreted as `value` options. Example:
|
||||
*
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond
|
||||
* to `[hc-key, value]`. Example:
|
||||
*
|
||||
* ```js
|
||||
* data: [
|
||||
* ['us-ny', 0],
|
||||
* ['us-mi', 5],
|
||||
* ['us-tx', 3],
|
||||
* ['us-ak', 5]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.map.turboThreshold),
|
||||
* this option is not available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* value: 6,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* value: 6,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<number|Array<string,(number|null)>|null|*>}
|
||||
* @product highmaps
|
||||
* @apioption series.mapline.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,174 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import '../Core/Utilities.js';
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Point.js';
|
||||
import '../Series/ScatterSeries.js';
|
||||
var merge = H.merge, Point = H.Point, Series = H.Series, seriesType = H.seriesType;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.mappoint
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('mappoint', 'scatter',
|
||||
/**
|
||||
* A mappoint series is a special form of scatter series where the points
|
||||
* can be laid out in map coordinates on top of a map.
|
||||
*
|
||||
* @sample maps/demo/mapline-mappoint/
|
||||
* Map-line and map-point series.
|
||||
*
|
||||
* @extends plotOptions.scatter
|
||||
* @product highmaps
|
||||
* @optionparent plotOptions.mappoint
|
||||
*/
|
||||
{
|
||||
dataLabels: {
|
||||
crop: false,
|
||||
defer: false,
|
||||
enabled: true,
|
||||
formatter: function () {
|
||||
return this.point.name;
|
||||
},
|
||||
overflow: false,
|
||||
style: {
|
||||
/** @internal */
|
||||
color: '#000000'
|
||||
}
|
||||
}
|
||||
// Prototype members
|
||||
}, {
|
||||
type: 'mappoint',
|
||||
forceDL: true,
|
||||
drawDataLabels: function () {
|
||||
Series.prototype.drawDataLabels.call(this);
|
||||
if (this.dataLabelsGroup) {
|
||||
this.dataLabelsGroup.clip(this.chart.clipRect);
|
||||
}
|
||||
}
|
||||
// Point class
|
||||
}, {
|
||||
applyOptions: function (options, x) {
|
||||
var mergedOptions = (typeof options.lat !== 'undefined' &&
|
||||
typeof options.lon !== 'undefined' ?
|
||||
merge(options, this.series.chart.fromLatLonToPoint(options)) :
|
||||
options);
|
||||
return Point.prototype
|
||||
.applyOptions.call(this, mergedOptions, x);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `mappoint` series. If the [type](#series.mappoint.type) option
|
||||
* is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
*
|
||||
* @extends series,plotOptions.mappoint
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highmaps
|
||||
* @apioption series.mappoint
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `mappoint` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 1],
|
||||
* [1, 8],
|
||||
* [2, 7]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.mappoint.turboThreshold),
|
||||
* this option is not available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 7,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<number|Array<number,(number|null)>|null|*>}
|
||||
* @extends series.map.data
|
||||
* @excluding labelrank, middleX, middleY, path, value
|
||||
* @product highmaps
|
||||
* @apioption series.mappoint.data
|
||||
*/
|
||||
/**
|
||||
* The latitude of the point. Must be combined with the `lon` option
|
||||
* to work. Overrides `x` and `y` values.
|
||||
*
|
||||
* @sample {highmaps} maps/demo/mappoint-latlon/
|
||||
* Point position by lat/lon
|
||||
*
|
||||
* @type {number}
|
||||
* @since 1.1.0
|
||||
* @product highmaps
|
||||
* @apioption series.mappoint.data.lat
|
||||
*/
|
||||
/**
|
||||
* The longitude of the point. Must be combined with the `lon` option
|
||||
* to work. Overrides `x` and `y` values.
|
||||
*
|
||||
* @sample {highmaps} maps/demo/mappoint-latlon/
|
||||
* Point position by lat/lon
|
||||
*
|
||||
* @type {number}
|
||||
* @since 1.1.0
|
||||
* @product highmaps
|
||||
* @apioption series.mappoint.data.lon
|
||||
*/
|
||||
/**
|
||||
* The x coordinate of the point in terms of the map path coordinates.
|
||||
*
|
||||
* @sample {highmaps} maps/demo/mapline-mappoint/
|
||||
* Map point demo
|
||||
*
|
||||
* @type {number}
|
||||
* @product highmaps
|
||||
* @apioption series.mappoint.data.x
|
||||
*/
|
||||
/**
|
||||
* The x coordinate of the point in terms of the map path coordinates.
|
||||
*
|
||||
* @sample {highmaps} maps/demo/mapline-mappoint/
|
||||
* Map point demo
|
||||
*
|
||||
* @type {number|null}
|
||||
* @product highmaps
|
||||
* @apioption series.mappoint.data.y
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,133 @@
|
||||
/* *
|
||||
*
|
||||
* Networkgraph series
|
||||
*
|
||||
* (c) 2010-2020 Paweł Fus
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
import Chart from '../../Core/Chart/Chart.js';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var addEvent = U.addEvent;
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
H.dragNodesMixin = {
|
||||
/**
|
||||
* Mouse down action, initializing drag&drop mode.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} point The point that event occured.
|
||||
* @param {Highcharts.PointerEventObject} event Browser event, before normalization.
|
||||
* @return {void}
|
||||
*/
|
||||
onMouseDown: function (point, event) {
|
||||
var normalizedEvent = this.chart.pointer.normalize(event);
|
||||
point.fixedPosition = {
|
||||
chartX: normalizedEvent.chartX,
|
||||
chartY: normalizedEvent.chartY,
|
||||
plotX: point.plotX,
|
||||
plotY: point.plotY
|
||||
};
|
||||
point.inDragMode = true;
|
||||
},
|
||||
/**
|
||||
* Mouse move action during drag&drop.
|
||||
*
|
||||
* @private
|
||||
*
|
||||
* @param {global.Event} event Browser event, before normalization.
|
||||
* @param {Highcharts.Point} point The point that event occured.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
onMouseMove: function (point, event) {
|
||||
if (point.fixedPosition && point.inDragMode) {
|
||||
var series = this, chart = series.chart, normalizedEvent = chart.pointer.normalize(event), diffX = point.fixedPosition.chartX - normalizedEvent.chartX, diffY = point.fixedPosition.chartY - normalizedEvent.chartY, newPlotX, newPlotY, graphLayoutsLookup = chart.graphLayoutsLookup;
|
||||
// At least 5px to apply change (avoids simple click):
|
||||
if (Math.abs(diffX) > 5 || Math.abs(diffY) > 5) {
|
||||
newPlotX = point.fixedPosition.plotX - diffX;
|
||||
newPlotY = point.fixedPosition.plotY - diffY;
|
||||
if (chart.isInsidePlot(newPlotX, newPlotY)) {
|
||||
point.plotX = newPlotX;
|
||||
point.plotY = newPlotY;
|
||||
point.hasDragged = true;
|
||||
this.redrawHalo(point);
|
||||
graphLayoutsLookup.forEach(function (layout) {
|
||||
layout.restartSimulation();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Mouse up action, finalizing drag&drop.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} point The point that event occured.
|
||||
* @return {void}
|
||||
*/
|
||||
onMouseUp: function (point, event) {
|
||||
if (point.fixedPosition && point.hasDragged) {
|
||||
if (this.layout.enableSimulation) {
|
||||
this.layout.start();
|
||||
}
|
||||
else {
|
||||
this.chart.redraw();
|
||||
}
|
||||
point.inDragMode = point.hasDragged = false;
|
||||
if (!this.options.fixedDraggable) {
|
||||
delete point.fixedPosition;
|
||||
}
|
||||
}
|
||||
},
|
||||
// Draggable mode:
|
||||
/**
|
||||
* Redraw halo on mousemove during the drag&drop action.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} point The point that should show halo.
|
||||
* @return {void}
|
||||
*/
|
||||
redrawHalo: function (point) {
|
||||
if (point && this.halo) {
|
||||
this.halo.attr({
|
||||
d: point.haloPath(this.options.states.hover.halo.size)
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
/*
|
||||
* Draggable mode:
|
||||
*/
|
||||
addEvent(Chart, 'load', function () {
|
||||
var chart = this, mousedownUnbinder, mousemoveUnbinder, mouseupUnbinder;
|
||||
if (chart.container) {
|
||||
mousedownUnbinder = addEvent(chart.container, 'mousedown', function (event) {
|
||||
var point = chart.hoverPoint;
|
||||
if (point &&
|
||||
point.series &&
|
||||
point.series.hasDraggableNodes &&
|
||||
point.series.options.draggable) {
|
||||
point.series.onMouseDown(point, event);
|
||||
mousemoveUnbinder = addEvent(chart.container, 'mousemove', function (e) {
|
||||
return point &&
|
||||
point.series &&
|
||||
point.series.onMouseMove(point, e);
|
||||
});
|
||||
mouseupUnbinder = addEvent(chart.container.ownerDocument, 'mouseup', function (e) {
|
||||
mousemoveUnbinder();
|
||||
mouseupUnbinder();
|
||||
return point &&
|
||||
point.series &&
|
||||
point.series.onMouseUp(point, e);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
addEvent(chart, 'destroy', function () {
|
||||
mousedownUnbinder();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,362 @@
|
||||
/* *
|
||||
*
|
||||
* Networkgraph series
|
||||
*
|
||||
* (c) 2010-2020 Paweł Fus
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
H.networkgraphIntegrations = {
|
||||
verlet: {
|
||||
/**
|
||||
* Attractive force funtion. Can be replaced by API's
|
||||
* `layoutAlgorithm.attractiveForce`
|
||||
*
|
||||
* @private
|
||||
* @param {number} d current distance between two nodes
|
||||
* @param {number} k expected distance between two nodes
|
||||
* @return {number} force
|
||||
*/
|
||||
attractiveForceFunction: function (d, k) {
|
||||
// Used in API:
|
||||
return (k - d) / d;
|
||||
},
|
||||
/**
|
||||
* Repulsive force funtion. Can be replaced by API's
|
||||
* `layoutAlgorithm.repulsiveForce`
|
||||
*
|
||||
* @private
|
||||
* @param {number} d current distance between two nodes
|
||||
* @param {number} k expected distance between two nodes
|
||||
* @return {number} force
|
||||
*/
|
||||
repulsiveForceFunction: function (d, k) {
|
||||
// Used in API:
|
||||
return (k - d) / d * (k > d ? 1 : 0); // Force only for close nodes
|
||||
},
|
||||
/**
|
||||
* Barycenter force. Calculate and applys barycenter forces on the
|
||||
* nodes. Making them closer to the center of their barycenter point.
|
||||
*
|
||||
* In Verlet integration, force is applied on a node immidatelly to it's
|
||||
* `plotX` and `plotY` position.
|
||||
*
|
||||
* @private
|
||||
* @return {void}
|
||||
*/
|
||||
barycenter: function () {
|
||||
var gravitationalConstant = this.options.gravitationalConstant, xFactor = this.barycenter.xFactor, yFactor = this.barycenter.yFactor;
|
||||
// To consider:
|
||||
xFactor = (xFactor - (this.box.left + this.box.width) / 2) *
|
||||
gravitationalConstant;
|
||||
yFactor = (yFactor - (this.box.top + this.box.height) / 2) *
|
||||
gravitationalConstant;
|
||||
this.nodes.forEach(function (node) {
|
||||
if (!node.fixedPosition) {
|
||||
node.plotX -=
|
||||
xFactor / node.mass / node.degree;
|
||||
node.plotY -=
|
||||
yFactor / node.mass / node.degree;
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Repulsive force.
|
||||
*
|
||||
* In Verlet integration, force is applied on a node immidatelly to it's
|
||||
* `plotX` and `plotY` position.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} node
|
||||
* Node that should be translated by force.
|
||||
* @param {number} force
|
||||
* Force calcualated in `repulsiveForceFunction`
|
||||
* @param {Highcharts.PositionObject} distance
|
||||
* Distance between two nodes e.g. `{x, y}`
|
||||
* @return {void}
|
||||
*/
|
||||
repulsive: function (node, force, distanceXY) {
|
||||
var factor = force * this.diffTemperature / node.mass / node.degree;
|
||||
if (!node.fixedPosition) {
|
||||
node.plotX += distanceXY.x * factor;
|
||||
node.plotY += distanceXY.y * factor;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Attractive force.
|
||||
*
|
||||
* In Verlet integration, force is applied on a node immidatelly to it's
|
||||
* `plotX` and `plotY` position.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} link
|
||||
* Link that connects two nodes
|
||||
* @param {number} force
|
||||
* Force calcualated in `repulsiveForceFunction`
|
||||
* @param {Highcharts.PositionObject} distance
|
||||
* Distance between two nodes e.g. `{x, y}`
|
||||
* @return {void}
|
||||
*/
|
||||
attractive: function (link, force, distanceXY) {
|
||||
var massFactor = link.getMass(), translatedX = -distanceXY.x * force * this.diffTemperature, translatedY = -distanceXY.y * force * this.diffTemperature;
|
||||
if (!link.fromNode.fixedPosition) {
|
||||
link.fromNode.plotX -=
|
||||
translatedX * massFactor.fromNode / link.fromNode.degree;
|
||||
link.fromNode.plotY -=
|
||||
translatedY * massFactor.fromNode / link.fromNode.degree;
|
||||
}
|
||||
if (!link.toNode.fixedPosition) {
|
||||
link.toNode.plotX +=
|
||||
translatedX * massFactor.toNode / link.toNode.degree;
|
||||
link.toNode.plotY +=
|
||||
translatedY * massFactor.toNode / link.toNode.degree;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Integration method.
|
||||
*
|
||||
* In Verlet integration, forces are applied on node immidatelly to it's
|
||||
* `plotX` and `plotY` position.
|
||||
*
|
||||
* Verlet without velocity:
|
||||
*
|
||||
* x(n+1) = 2 * x(n) - x(n-1) + A(T) * deltaT ^ 2
|
||||
*
|
||||
* where:
|
||||
* - x(n+1) - new position
|
||||
* - x(n) - current position
|
||||
* - x(n-1) - previous position
|
||||
*
|
||||
* Assuming A(t) = 0 (no acceleration) and (deltaT = 1) we get:
|
||||
*
|
||||
* x(n+1) = x(n) + (x(n) - x(n-1))
|
||||
*
|
||||
* where:
|
||||
* - (x(n) - x(n-1)) - position change
|
||||
*
|
||||
* TO DO:
|
||||
* Consider Verlet with velocity to support additional
|
||||
* forces. Or even Time-Corrected Verlet by Jonathan
|
||||
* "lonesock" Dummer
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.NetworkgraphLayout} layout layout object
|
||||
* @param {Highcharts.Point} node node that should be translated
|
||||
* @return {void}
|
||||
*/
|
||||
integrate: function (layout, node) {
|
||||
var friction = -layout.options.friction, maxSpeed = layout.options.maxSpeed, prevX = node.prevX, prevY = node.prevY,
|
||||
// Apply friciton:
|
||||
diffX = ((node.plotX + node.dispX -
|
||||
prevX) * friction), diffY = ((node.plotY + node.dispY -
|
||||
prevY) * friction), abs = Math.abs, signX = abs(diffX) / (diffX || 1), // need to deal with 0
|
||||
signY = abs(diffY) / (diffY || 1);
|
||||
// Apply max speed:
|
||||
diffX = signX * Math.min(maxSpeed, Math.abs(diffX));
|
||||
diffY = signY * Math.min(maxSpeed, Math.abs(diffY));
|
||||
// Store for the next iteration:
|
||||
node.prevX = node.plotX + node.dispX;
|
||||
node.prevY = node.plotY + node.dispY;
|
||||
// Update positions:
|
||||
node.plotX += diffX;
|
||||
node.plotY += diffY;
|
||||
node.temperature = layout.vectorLength({
|
||||
x: diffX,
|
||||
y: diffY
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Estiamte the best possible distance between two nodes, making graph
|
||||
* readable.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.NetworkgraphLayout} layout layout object
|
||||
* @return {number}
|
||||
*/
|
||||
getK: function (layout) {
|
||||
return Math.pow(layout.box.width * layout.box.height / layout.nodes.length, 0.5);
|
||||
}
|
||||
},
|
||||
euler: {
|
||||
/**
|
||||
* Attractive force funtion. Can be replaced by API's
|
||||
* `layoutAlgorithm.attractiveForce`
|
||||
*
|
||||
* Other forces that can be used:
|
||||
*
|
||||
* basic, not recommended:
|
||||
* `function (d, k) { return d / k }`
|
||||
*
|
||||
* @private
|
||||
* @param {number} d current distance between two nodes
|
||||
* @param {number} k expected distance between two nodes
|
||||
* @return {number} force
|
||||
*/
|
||||
attractiveForceFunction: function (d, k) {
|
||||
return d * d / k;
|
||||
},
|
||||
/**
|
||||
* Repulsive force funtion. Can be replaced by API's
|
||||
* `layoutAlgorithm.repulsiveForce`.
|
||||
*
|
||||
* Other forces that can be used:
|
||||
*
|
||||
* basic, not recommended:
|
||||
* `function (d, k) { return k / d }`
|
||||
*
|
||||
* standard:
|
||||
* `function (d, k) { return k * k / d }`
|
||||
*
|
||||
* grid-variant:
|
||||
* `function (d, k) { return k * k / d * (2 * k - d > 0 ? 1 : 0) }`
|
||||
*
|
||||
* @private
|
||||
* @param {number} d current distance between two nodes
|
||||
* @param {number} k expected distance between two nodes
|
||||
* @return {number} force
|
||||
*/
|
||||
repulsiveForceFunction: function (d, k) {
|
||||
return k * k / d;
|
||||
},
|
||||
/**
|
||||
* Barycenter force. Calculate and applys barycenter forces on the
|
||||
* nodes. Making them closer to the center of their barycenter point.
|
||||
*
|
||||
* In Euler integration, force is stored in a node, not changing it's
|
||||
* position. Later, in `integrate()` forces are applied on nodes.
|
||||
*
|
||||
* @private
|
||||
* @return {void}
|
||||
*/
|
||||
barycenter: function () {
|
||||
var gravitationalConstant = this.options.gravitationalConstant, xFactor = this.barycenter.xFactor, yFactor = this.barycenter.yFactor;
|
||||
this.nodes.forEach(function (node) {
|
||||
if (!node.fixedPosition) {
|
||||
var degree = node.getDegree(), phi = degree * (1 + degree / 2);
|
||||
node.dispX += ((xFactor - node.plotX) *
|
||||
gravitationalConstant *
|
||||
phi / node.degree);
|
||||
node.dispY += ((yFactor - node.plotY) *
|
||||
gravitationalConstant *
|
||||
phi / node.degree);
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Repulsive force.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} node
|
||||
* Node that should be translated by force.
|
||||
* @param {number} force
|
||||
* Force calcualated in `repulsiveForceFunction`
|
||||
* @param {Highcharts.PositionObject} distanceXY
|
||||
* Distance between two nodes e.g. `{x, y}`
|
||||
* @return {void}
|
||||
*/
|
||||
repulsive: function (node, force, distanceXY, distanceR) {
|
||||
node.dispX +=
|
||||
(distanceXY.x / distanceR) * force / node.degree;
|
||||
node.dispY +=
|
||||
(distanceXY.y / distanceR) * force / node.degree;
|
||||
},
|
||||
/**
|
||||
* Attractive force.
|
||||
*
|
||||
* In Euler integration, force is stored in a node, not changing it's
|
||||
* position. Later, in `integrate()` forces are applied on nodes.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.Point} link
|
||||
* Link that connects two nodes
|
||||
* @param {number} force
|
||||
* Force calcualated in `repulsiveForceFunction`
|
||||
* @param {Highcharts.PositionObject} distanceXY
|
||||
* Distance between two nodes e.g. `{x, y}`
|
||||
* @param {number} distanceR
|
||||
* @return {void}
|
||||
*/
|
||||
attractive: function (link, force, distanceXY, distanceR) {
|
||||
var massFactor = link.getMass(), translatedX = (distanceXY.x / distanceR) * force, translatedY = (distanceXY.y / distanceR) * force;
|
||||
if (!link.fromNode.fixedPosition) {
|
||||
link.fromNode.dispX -=
|
||||
translatedX * massFactor.fromNode / link.fromNode.degree;
|
||||
link.fromNode.dispY -=
|
||||
translatedY * massFactor.fromNode / link.fromNode.degree;
|
||||
}
|
||||
if (!link.toNode.fixedPosition) {
|
||||
link.toNode.dispX +=
|
||||
translatedX * massFactor.toNode / link.toNode.degree;
|
||||
link.toNode.dispY +=
|
||||
translatedY * massFactor.toNode / link.toNode.degree;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Integration method.
|
||||
*
|
||||
* In Euler integration, force were stored in a node, not changing it's
|
||||
* position. Now, in the integrator method, we apply changes.
|
||||
*
|
||||
* Euler:
|
||||
*
|
||||
* Basic form: `x(n+1) = x(n) + v(n)`
|
||||
*
|
||||
* With Rengoild-Fruchterman we get:
|
||||
* `x(n+1) = x(n) + v(n) / length(v(n)) * min(v(n), temperature(n))`
|
||||
* where:
|
||||
* - `x(n+1)`: next position
|
||||
* - `x(n)`: current position
|
||||
* - `v(n)`: velocity (comes from net force)
|
||||
* - `temperature(n)`: current temperature
|
||||
*
|
||||
* Known issues:
|
||||
* Oscillations when force vector has the same magnitude but opposite
|
||||
* direction in the next step. Potentially solved by decreasing force by
|
||||
* `v * (1 / node.degree)`
|
||||
*
|
||||
* Note:
|
||||
* Actually `min(v(n), temperature(n))` replaces simulated annealing.
|
||||
*
|
||||
* @private
|
||||
* @param {Highcharts.NetworkgraphLayout} layout
|
||||
* Layout object
|
||||
* @param {Highcharts.Point} node
|
||||
* Node that should be translated
|
||||
* @return {void}
|
||||
*/
|
||||
integrate: function (layout, node) {
|
||||
var distanceR;
|
||||
node.dispX +=
|
||||
node.dispX * layout.options.friction;
|
||||
node.dispY +=
|
||||
node.dispY * layout.options.friction;
|
||||
distanceR = node.temperature = layout.vectorLength({
|
||||
x: node.dispX,
|
||||
y: node.dispY
|
||||
});
|
||||
if (distanceR !== 0) {
|
||||
node.plotX += (node.dispX / distanceR *
|
||||
Math.min(Math.abs(node.dispX), layout.temperature));
|
||||
node.plotY += (node.dispY / distanceR *
|
||||
Math.min(Math.abs(node.dispY), layout.temperature));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Estiamte the best possible distance between two nodes, making graph
|
||||
* readable.
|
||||
*
|
||||
* @private
|
||||
* @param {object} layout layout object
|
||||
* @return {number}
|
||||
*/
|
||||
getK: function (layout) {
|
||||
return Math.pow(layout.box.width * layout.box.height / layout.nodes.length, 0.3);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,553 @@
|
||||
/* *
|
||||
*
|
||||
* Networkgraph series
|
||||
*
|
||||
* (c) 2010-2020 Paweł Fus
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Chart from '../../Core/Chart/Chart.js';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, clamp = U.clamp, defined = U.defined, extend = U.extend, isFunction = U.isFunction, pick = U.pick, setAnimation = U.setAnimation;
|
||||
import './Integrations.js';
|
||||
import './QuadTree.js';
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
H.layouts = {
|
||||
'reingold-fruchterman': function () {
|
||||
}
|
||||
};
|
||||
extend(
|
||||
/**
|
||||
* Reingold-Fruchterman algorithm from
|
||||
* "Graph Drawing by Force-directed Placement" paper.
|
||||
* @private
|
||||
*/
|
||||
H.layouts['reingold-fruchterman'].prototype, {
|
||||
init: function (options) {
|
||||
this.options = options;
|
||||
this.nodes = [];
|
||||
this.links = [];
|
||||
this.series = [];
|
||||
this.box = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
this.setInitialRendering(true);
|
||||
this.integration =
|
||||
H.networkgraphIntegrations[options.integration];
|
||||
this.enableSimulation = options.enableSimulation;
|
||||
this.attractiveForce = pick(options.attractiveForce, this.integration.attractiveForceFunction);
|
||||
this.repulsiveForce = pick(options.repulsiveForce, this.integration.repulsiveForceFunction);
|
||||
this.approximation = options.approximation;
|
||||
},
|
||||
updateSimulation: function (enable) {
|
||||
this.enableSimulation = pick(enable, this.options.enableSimulation);
|
||||
},
|
||||
start: function () {
|
||||
var layout = this, series = this.series, options = this.options;
|
||||
layout.currentStep = 0;
|
||||
layout.forces = series[0] && series[0].forces || [];
|
||||
layout.chart = series[0] && series[0].chart;
|
||||
if (layout.initialRendering) {
|
||||
layout.initPositions();
|
||||
// Render elements in initial positions:
|
||||
series.forEach(function (s) {
|
||||
s.finishedAnimating = true; // #13169
|
||||
s.render();
|
||||
});
|
||||
}
|
||||
layout.setK();
|
||||
layout.resetSimulation(options);
|
||||
if (layout.enableSimulation) {
|
||||
layout.step();
|
||||
}
|
||||
},
|
||||
step: function () {
|
||||
var layout = this, series = this.series, options = this.options;
|
||||
// Algorithm:
|
||||
layout.currentStep++;
|
||||
if (layout.approximation === 'barnes-hut') {
|
||||
layout.createQuadTree();
|
||||
layout.quadTree.calculateMassAndCenter();
|
||||
}
|
||||
layout.forces.forEach(function (forceName) {
|
||||
layout[forceName + 'Forces'](layout.temperature);
|
||||
});
|
||||
// Limit to the plotting area and cool down:
|
||||
layout.applyLimits(layout.temperature);
|
||||
// Cool down the system:
|
||||
layout.temperature = layout.coolDown(layout.startTemperature, layout.diffTemperature, layout.currentStep);
|
||||
layout.prevSystemTemperature = layout.systemTemperature;
|
||||
layout.systemTemperature = layout.getSystemTemperature();
|
||||
if (layout.enableSimulation) {
|
||||
series.forEach(function (s) {
|
||||
// Chart could be destroyed during the simulation
|
||||
if (s.chart) {
|
||||
s.render();
|
||||
}
|
||||
});
|
||||
if (layout.maxIterations-- &&
|
||||
isFinite(layout.temperature) &&
|
||||
!layout.isStable()) {
|
||||
if (layout.simulation) {
|
||||
H.win.cancelAnimationFrame(layout.simulation);
|
||||
}
|
||||
layout.simulation = H.win.requestAnimationFrame(function () {
|
||||
layout.step();
|
||||
});
|
||||
}
|
||||
else {
|
||||
layout.simulation = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
stop: function () {
|
||||
if (this.simulation) {
|
||||
H.win.cancelAnimationFrame(this.simulation);
|
||||
}
|
||||
},
|
||||
setArea: function (x, y, w, h) {
|
||||
this.box = {
|
||||
left: x,
|
||||
top: y,
|
||||
width: w,
|
||||
height: h
|
||||
};
|
||||
},
|
||||
setK: function () {
|
||||
// Optimal distance between nodes,
|
||||
// available space around the node:
|
||||
this.k = this.options.linkLength || this.integration.getK(this);
|
||||
},
|
||||
addElementsToCollection: function (elements, collection) {
|
||||
elements.forEach(function (elem) {
|
||||
if (collection.indexOf(elem) === -1) {
|
||||
collection.push(elem);
|
||||
}
|
||||
});
|
||||
},
|
||||
removeElementFromCollection: function (element, collection) {
|
||||
var index = collection.indexOf(element);
|
||||
if (index !== -1) {
|
||||
collection.splice(index, 1);
|
||||
}
|
||||
},
|
||||
clear: function () {
|
||||
this.nodes.length = 0;
|
||||
this.links.length = 0;
|
||||
this.series.length = 0;
|
||||
this.resetSimulation();
|
||||
},
|
||||
resetSimulation: function () {
|
||||
this.forcedStop = false;
|
||||
this.systemTemperature = 0;
|
||||
this.setMaxIterations();
|
||||
this.setTemperature();
|
||||
this.setDiffTemperature();
|
||||
},
|
||||
restartSimulation: function () {
|
||||
if (!this.simulation) {
|
||||
// When dragging nodes, we don't need to calculate
|
||||
// initial positions and rendering nodes:
|
||||
this.setInitialRendering(false);
|
||||
// Start new simulation:
|
||||
if (!this.enableSimulation) {
|
||||
// Run only one iteration to speed things up:
|
||||
this.setMaxIterations(1);
|
||||
}
|
||||
else {
|
||||
this.start();
|
||||
}
|
||||
if (this.chart) {
|
||||
this.chart.redraw();
|
||||
}
|
||||
// Restore defaults:
|
||||
this.setInitialRendering(true);
|
||||
}
|
||||
else {
|
||||
// Extend current simulation:
|
||||
this.resetSimulation();
|
||||
}
|
||||
},
|
||||
setMaxIterations: function (maxIterations) {
|
||||
this.maxIterations = pick(maxIterations, this.options.maxIterations);
|
||||
},
|
||||
setTemperature: function () {
|
||||
this.temperature = this.startTemperature =
|
||||
Math.sqrt(this.nodes.length);
|
||||
},
|
||||
setDiffTemperature: function () {
|
||||
this.diffTemperature = this.startTemperature /
|
||||
(this.options.maxIterations + 1);
|
||||
},
|
||||
setInitialRendering: function (enable) {
|
||||
this.initialRendering = enable;
|
||||
},
|
||||
createQuadTree: function () {
|
||||
this.quadTree = new H.QuadTree(this.box.left, this.box.top, this.box.width, this.box.height);
|
||||
this.quadTree.insertNodes(this.nodes);
|
||||
},
|
||||
initPositions: function () {
|
||||
var initialPositions = this.options.initialPositions;
|
||||
if (isFunction(initialPositions)) {
|
||||
initialPositions.call(this);
|
||||
this.nodes.forEach(function (node) {
|
||||
if (!defined(node.prevX)) {
|
||||
node.prevX = node.plotX;
|
||||
}
|
||||
if (!defined(node.prevY)) {
|
||||
node.prevY = node.plotY;
|
||||
}
|
||||
node.dispX = 0;
|
||||
node.dispY = 0;
|
||||
});
|
||||
}
|
||||
else if (initialPositions === 'circle') {
|
||||
this.setCircularPositions();
|
||||
}
|
||||
else {
|
||||
this.setRandomPositions();
|
||||
}
|
||||
},
|
||||
setCircularPositions: function () {
|
||||
var box = this.box, nodes = this.nodes, nodesLength = nodes.length + 1, angle = 2 * Math.PI / nodesLength, rootNodes = nodes.filter(function (node) {
|
||||
return node.linksTo.length === 0;
|
||||
}), sortedNodes = [], visitedNodes = {}, radius = this.options.initialPositionRadius;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function addToNodes(node) {
|
||||
node.linksFrom.forEach(function (link) {
|
||||
if (!visitedNodes[link.toNode.id]) {
|
||||
visitedNodes[link.toNode.id] = true;
|
||||
sortedNodes.push(link.toNode);
|
||||
addToNodes(link.toNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Start with identified root nodes an sort the nodes by their
|
||||
// hierarchy. In trees, this ensures that branches don't cross
|
||||
// eachother.
|
||||
rootNodes.forEach(function (rootNode) {
|
||||
sortedNodes.push(rootNode);
|
||||
addToNodes(rootNode);
|
||||
});
|
||||
// Cyclic tree, no root node found
|
||||
if (!sortedNodes.length) {
|
||||
sortedNodes = nodes;
|
||||
// Dangling, cyclic trees
|
||||
}
|
||||
else {
|
||||
nodes.forEach(function (node) {
|
||||
if (sortedNodes.indexOf(node) === -1) {
|
||||
sortedNodes.push(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Initial positions are laid out along a small circle, appearing
|
||||
// as a cluster in the middle
|
||||
sortedNodes.forEach(function (node, index) {
|
||||
node.plotX = node.prevX = pick(node.plotX, box.width / 2 + radius * Math.cos(index * angle));
|
||||
node.plotY = node.prevY = pick(node.plotY, box.height / 2 + radius * Math.sin(index * angle));
|
||||
node.dispX = 0;
|
||||
node.dispY = 0;
|
||||
});
|
||||
},
|
||||
setRandomPositions: function () {
|
||||
var box = this.box, nodes = this.nodes, nodesLength = nodes.length + 1;
|
||||
/**
|
||||
* Return a repeatable, quasi-random number based on an integer
|
||||
* input. For the initial positions
|
||||
* @private
|
||||
*/
|
||||
function unrandom(n) {
|
||||
var rand = n * n / Math.PI;
|
||||
rand = rand - Math.floor(rand);
|
||||
return rand;
|
||||
}
|
||||
// Initial positions:
|
||||
nodes.forEach(function (node, index) {
|
||||
node.plotX = node.prevX = pick(node.plotX, box.width * unrandom(index));
|
||||
node.plotY = node.prevY = pick(node.plotY, box.height * unrandom(nodesLength + index));
|
||||
node.dispX = 0;
|
||||
node.dispY = 0;
|
||||
});
|
||||
},
|
||||
force: function (name) {
|
||||
this.integration[name].apply(this, Array.prototype.slice.call(arguments, 1));
|
||||
},
|
||||
barycenterForces: function () {
|
||||
this.getBarycenter();
|
||||
this.force('barycenter');
|
||||
},
|
||||
getBarycenter: function () {
|
||||
var systemMass = 0, cx = 0, cy = 0;
|
||||
this.nodes.forEach(function (node) {
|
||||
cx += node.plotX * node.mass;
|
||||
cy += node.plotY * node.mass;
|
||||
systemMass += node.mass;
|
||||
});
|
||||
this.barycenter = {
|
||||
x: cx,
|
||||
y: cy,
|
||||
xFactor: cx / systemMass,
|
||||
yFactor: cy / systemMass
|
||||
};
|
||||
return this.barycenter;
|
||||
},
|
||||
barnesHutApproximation: function (node, quadNode) {
|
||||
var layout = this, distanceXY = layout.getDistXY(node, quadNode), distanceR = layout.vectorLength(distanceXY), goDeeper, force;
|
||||
if (node !== quadNode && distanceR !== 0) {
|
||||
if (quadNode.isInternal) {
|
||||
// Internal node:
|
||||
if (quadNode.boxSize / distanceR <
|
||||
layout.options.theta &&
|
||||
distanceR !== 0) {
|
||||
// Treat as an external node:
|
||||
force = layout.repulsiveForce(distanceR, layout.k);
|
||||
layout.force('repulsive', node, force * quadNode.mass, distanceXY, distanceR);
|
||||
goDeeper = false;
|
||||
}
|
||||
else {
|
||||
// Go deeper:
|
||||
goDeeper = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// External node, direct force:
|
||||
force = layout.repulsiveForce(distanceR, layout.k);
|
||||
layout.force('repulsive', node, force * quadNode.mass, distanceXY, distanceR);
|
||||
}
|
||||
}
|
||||
return goDeeper;
|
||||
},
|
||||
repulsiveForces: function () {
|
||||
var layout = this;
|
||||
if (layout.approximation === 'barnes-hut') {
|
||||
layout.nodes.forEach(function (node) {
|
||||
layout.quadTree.visitNodeRecursive(null, function (quadNode) {
|
||||
return layout.barnesHutApproximation(node, quadNode);
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
layout.nodes.forEach(function (node) {
|
||||
layout.nodes.forEach(function (repNode) {
|
||||
var force, distanceR, distanceXY;
|
||||
if (
|
||||
// Node can not repulse itself:
|
||||
node !== repNode &&
|
||||
// Only close nodes affect each other:
|
||||
// layout.getDistR(node, repNode) < 2 * k &&
|
||||
// Not dragged:
|
||||
!node.fixedPosition) {
|
||||
distanceXY = layout.getDistXY(node, repNode);
|
||||
distanceR = layout.vectorLength(distanceXY);
|
||||
if (distanceR !== 0) {
|
||||
force = layout.repulsiveForce(distanceR, layout.k);
|
||||
layout.force('repulsive', node, force * repNode.mass, distanceXY, distanceR);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
attractiveForces: function () {
|
||||
var layout = this, distanceXY, distanceR, force;
|
||||
layout.links.forEach(function (link) {
|
||||
if (link.fromNode && link.toNode) {
|
||||
distanceXY = layout.getDistXY(link.fromNode, link.toNode);
|
||||
distanceR = layout.vectorLength(distanceXY);
|
||||
if (distanceR !== 0) {
|
||||
force = layout.attractiveForce(distanceR, layout.k);
|
||||
layout.force('attractive', link, force, distanceXY, distanceR);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
applyLimits: function () {
|
||||
var layout = this, nodes = layout.nodes;
|
||||
nodes.forEach(function (node) {
|
||||
if (node.fixedPosition) {
|
||||
return;
|
||||
}
|
||||
layout.integration.integrate(layout, node);
|
||||
layout.applyLimitBox(node, layout.box);
|
||||
// Reset displacement:
|
||||
node.dispX = 0;
|
||||
node.dispY = 0;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* External box that nodes should fall. When hitting an edge, node
|
||||
* should stop or bounce.
|
||||
* @private
|
||||
*/
|
||||
applyLimitBox: function (node, box) {
|
||||
var radius = node.radius;
|
||||
/*
|
||||
TO DO: Consider elastic collision instead of stopping.
|
||||
o' means end position when hitting plotting area edge:
|
||||
|
||||
- "inelastic":
|
||||
o
|
||||
\
|
||||
______
|
||||
| o'
|
||||
| \
|
||||
| \
|
||||
|
||||
- "elastic"/"bounced":
|
||||
o
|
||||
\
|
||||
______
|
||||
| ^
|
||||
| / \
|
||||
|o' \
|
||||
|
||||
Euler sample:
|
||||
if (plotX < 0) {
|
||||
plotX = 0;
|
||||
dispX *= -1;
|
||||
}
|
||||
|
||||
if (plotX > box.width) {
|
||||
plotX = box.width;
|
||||
dispX *= -1;
|
||||
}
|
||||
|
||||
*/
|
||||
// Limit X-coordinates:
|
||||
node.plotX = clamp(node.plotX, box.left + radius, box.width - radius);
|
||||
// Limit Y-coordinates:
|
||||
node.plotY = clamp(node.plotY, box.top + radius, box.height - radius);
|
||||
},
|
||||
/**
|
||||
* From "A comparison of simulated annealing cooling strategies" by
|
||||
* Nourani and Andresen work.
|
||||
* @private
|
||||
*/
|
||||
coolDown: function (temperature, temperatureStep, currentStep) {
|
||||
// Logarithmic:
|
||||
/*
|
||||
return Math.sqrt(this.nodes.length) -
|
||||
Math.log(
|
||||
currentStep * layout.diffTemperature
|
||||
);
|
||||
*/
|
||||
// Exponential:
|
||||
/*
|
||||
var alpha = 0.1;
|
||||
layout.temperature = Math.sqrt(layout.nodes.length) *
|
||||
Math.pow(alpha, layout.diffTemperature);
|
||||
*/
|
||||
// Linear:
|
||||
return temperature - temperatureStep * currentStep;
|
||||
},
|
||||
isStable: function () {
|
||||
return Math.abs(this.systemTemperature -
|
||||
this.prevSystemTemperature) < 0.00001 || this.temperature <= 0;
|
||||
},
|
||||
getSystemTemperature: function () {
|
||||
return this.nodes.reduce(function (value, node) {
|
||||
return value + node.temperature;
|
||||
}, 0);
|
||||
},
|
||||
vectorLength: function (vector) {
|
||||
return Math.sqrt(vector.x * vector.x + vector.y * vector.y);
|
||||
},
|
||||
getDistR: function (nodeA, nodeB) {
|
||||
var distance = this.getDistXY(nodeA, nodeB);
|
||||
return this.vectorLength(distance);
|
||||
},
|
||||
getDistXY: function (nodeA, nodeB) {
|
||||
var xDist = nodeA.plotX - nodeB.plotX, yDist = nodeA.plotY - nodeB.plotY;
|
||||
return {
|
||||
x: xDist,
|
||||
y: yDist,
|
||||
absX: Math.abs(xDist),
|
||||
absY: Math.abs(yDist)
|
||||
};
|
||||
}
|
||||
});
|
||||
/* ************************************************************************** *
|
||||
* Multiple series support:
|
||||
* ************************************************************************** */
|
||||
// Clear previous layouts
|
||||
addEvent(Chart, 'predraw', function () {
|
||||
if (this.graphLayoutsLookup) {
|
||||
this.graphLayoutsLookup.forEach(function (layout) {
|
||||
layout.stop();
|
||||
});
|
||||
}
|
||||
});
|
||||
addEvent(Chart, 'render', function () {
|
||||
var systemsStable, afterRender = false;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function layoutStep(layout) {
|
||||
if (layout.maxIterations-- &&
|
||||
isFinite(layout.temperature) &&
|
||||
!layout.isStable() &&
|
||||
!layout.enableSimulation) {
|
||||
// Hook similar to build-in addEvent, but instead of
|
||||
// creating whole events logic, use just a function.
|
||||
// It's faster which is important for rAF code.
|
||||
// Used e.g. in packed-bubble series for bubble radius
|
||||
// calculations
|
||||
if (layout.beforeStep) {
|
||||
layout.beforeStep();
|
||||
}
|
||||
layout.step();
|
||||
systemsStable = false;
|
||||
afterRender = true;
|
||||
}
|
||||
}
|
||||
if (this.graphLayoutsLookup) {
|
||||
setAnimation(false, this);
|
||||
// Start simulation
|
||||
this.graphLayoutsLookup.forEach(function (layout) {
|
||||
layout.start();
|
||||
});
|
||||
// Just one sync step, to run different layouts similar to
|
||||
// async mode.
|
||||
while (!systemsStable) {
|
||||
systemsStable = true;
|
||||
this.graphLayoutsLookup.forEach(layoutStep);
|
||||
}
|
||||
if (afterRender) {
|
||||
this.series.forEach(function (s) {
|
||||
if (s && s.layout) {
|
||||
s.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// disable simulation before print if enabled
|
||||
addEvent(Chart, 'beforePrint', function () {
|
||||
if (this.graphLayoutsLookup) {
|
||||
this.graphLayoutsLookup.forEach(function (layout) {
|
||||
layout.updateSimulation(false);
|
||||
});
|
||||
this.redraw();
|
||||
}
|
||||
});
|
||||
// re-enable simulation after print
|
||||
addEvent(Chart, 'afterPrint', function () {
|
||||
if (this.graphLayoutsLookup) {
|
||||
this.graphLayoutsLookup.forEach(function (layout) {
|
||||
// return to default simulation
|
||||
layout.updateSimulation();
|
||||
});
|
||||
}
|
||||
this.redraw();
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,354 @@
|
||||
/* *
|
||||
*
|
||||
* Networkgraph series
|
||||
*
|
||||
* (c) 2010-2020 Paweł Fus
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../../Core/Globals.js';
|
||||
import U from '../../Core/Utilities.js';
|
||||
var extend = U.extend;
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
/**
|
||||
* The QuadTree node class. Used in Networkgraph chart as a base for Barnes-Hut
|
||||
* approximation.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.QuadTreeNode
|
||||
*
|
||||
* @param {Highcharts.Dictionary<number>} box Available space for the node
|
||||
*/
|
||||
var QuadTreeNode = H.QuadTreeNode = function (box) {
|
||||
/**
|
||||
* Read only. The available space for node.
|
||||
*
|
||||
* @name Highcharts.QuadTreeNode#box
|
||||
* @type {Highcharts.Dictionary<number>}
|
||||
*/
|
||||
this.box = box;
|
||||
/**
|
||||
* Read only. The minium of width and height values.
|
||||
*
|
||||
* @name Highcharts.QuadTreeNode#boxSize
|
||||
* @type {number}
|
||||
*/
|
||||
this.boxSize = Math.min(box.width, box.height);
|
||||
/**
|
||||
* Read only. Array of subnodes. Empty if QuadTreeNode has just one Point.
|
||||
* When added another Point to this QuadTreeNode, array is filled with four
|
||||
* subnodes.
|
||||
*
|
||||
* @name Highcharts.QuadTreeNode#nodes
|
||||
* @type {Array<Highcharts.QuadTreeNode>}
|
||||
*/
|
||||
this.nodes = [];
|
||||
/**
|
||||
* Read only. Flag to determine if QuadTreeNode is internal (and has
|
||||
* subnodes with mass and central position) or external (bound to Point).
|
||||
*
|
||||
* @name Highcharts.QuadTreeNode#isInternal
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isInternal = false;
|
||||
/**
|
||||
* Read only. If QuadTreeNode is an external node, Point is stored in
|
||||
* `this.body`.
|
||||
*
|
||||
* @name Highcharts.QuadTreeNode#body
|
||||
* @type {boolean|Highcharts.Point}
|
||||
*/
|
||||
this.body = false;
|
||||
/**
|
||||
* Read only. Internal nodes when created are empty to reserve the space. If
|
||||
* Point is added to this QuadTreeNode, QuadTreeNode is no longer empty.
|
||||
*
|
||||
* @name Highcharts.QuadTreeNode#isEmpty
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.isEmpty = true;
|
||||
};
|
||||
extend(QuadTreeNode.prototype,
|
||||
/** @lends Highcharts.QuadTreeNode.prototype */
|
||||
{
|
||||
/**
|
||||
* Insert recursively point(node) into the QuadTree. If the given
|
||||
* quadrant is already occupied, divide it into smaller quadrants.
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
* Point/node to be inserted
|
||||
* @param {number} depth
|
||||
* Max depth of the QuadTree
|
||||
*/
|
||||
insert: function (point, depth) {
|
||||
var newQuadTreeNode;
|
||||
if (this.isInternal) {
|
||||
// Internal node:
|
||||
this.nodes[this.getBoxPosition(point)].insert(point, depth - 1);
|
||||
}
|
||||
else {
|
||||
this.isEmpty = false;
|
||||
if (!this.body) {
|
||||
// First body in a quadrant:
|
||||
this.isInternal = false;
|
||||
this.body = point;
|
||||
}
|
||||
else {
|
||||
if (depth) {
|
||||
// Every other body in a quadrant:
|
||||
this.isInternal = true;
|
||||
this.divideBox();
|
||||
// Reinsert main body only once:
|
||||
if (this.body !== true) {
|
||||
this.nodes[this.getBoxPosition(this.body)]
|
||||
.insert(this.body, depth - 1);
|
||||
this.body = true;
|
||||
}
|
||||
// Add second body:
|
||||
this.nodes[this.getBoxPosition(point)]
|
||||
.insert(point, depth - 1);
|
||||
}
|
||||
else {
|
||||
// We are below max allowed depth. That means either:
|
||||
// - really huge number of points
|
||||
// - falling two points into exactly the same position
|
||||
// In this case, create another node in the QuadTree.
|
||||
//
|
||||
// Alternatively we could add some noise to the
|
||||
// position, but that could result in different
|
||||
// rendered chart in exporting.
|
||||
newQuadTreeNode = new QuadTreeNode({
|
||||
top: point.plotX,
|
||||
left: point.plotY,
|
||||
// Width/height below 1px
|
||||
width: 0.1,
|
||||
height: 0.1
|
||||
});
|
||||
newQuadTreeNode.body = point;
|
||||
newQuadTreeNode.isInternal = false;
|
||||
this.nodes.push(newQuadTreeNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Each quad node requires it's mass and center position. That mass and
|
||||
* position is used to imitate real node in the layout by approximation.
|
||||
*/
|
||||
updateMassAndCenter: function () {
|
||||
var mass = 0, plotX = 0, plotY = 0;
|
||||
if (this.isInternal) {
|
||||
// Calcualte weightened mass of the quad node:
|
||||
this.nodes.forEach(function (pointMass) {
|
||||
if (!pointMass.isEmpty) {
|
||||
mass += pointMass.mass;
|
||||
plotX +=
|
||||
pointMass.plotX * pointMass.mass;
|
||||
plotY +=
|
||||
pointMass.plotY * pointMass.mass;
|
||||
}
|
||||
});
|
||||
plotX /= mass;
|
||||
plotY /= mass;
|
||||
}
|
||||
else if (this.body) {
|
||||
// Just one node, use coordinates directly:
|
||||
mass = this.body.mass;
|
||||
plotX = this.body.plotX;
|
||||
plotY = this.body.plotY;
|
||||
}
|
||||
// Store details:
|
||||
this.mass = mass;
|
||||
this.plotX = plotX;
|
||||
this.plotY = plotY;
|
||||
},
|
||||
/**
|
||||
* When inserting another node into the box, that already hove one node,
|
||||
* divide the available space into another four quadrants.
|
||||
*
|
||||
* Indexes of quadrants are:
|
||||
* ```
|
||||
* ------------- -------------
|
||||
* | | | | |
|
||||
* | | | 0 | 1 |
|
||||
* | | divide() | | |
|
||||
* | 1 | -----------> -------------
|
||||
* | | | | |
|
||||
* | | | 3 | 2 |
|
||||
* | | | | |
|
||||
* ------------- -------------
|
||||
* ```
|
||||
*/
|
||||
divideBox: function () {
|
||||
var halfWidth = this.box.width / 2, halfHeight = this.box.height / 2;
|
||||
// Top left
|
||||
this.nodes[0] = new QuadTreeNode({
|
||||
left: this.box.left,
|
||||
top: this.box.top,
|
||||
width: halfWidth,
|
||||
height: halfHeight
|
||||
});
|
||||
// Top right
|
||||
this.nodes[1] = new QuadTreeNode({
|
||||
left: this.box.left + halfWidth,
|
||||
top: this.box.top,
|
||||
width: halfWidth,
|
||||
height: halfHeight
|
||||
});
|
||||
// Bottom right
|
||||
this.nodes[2] = new QuadTreeNode({
|
||||
left: this.box.left + halfWidth,
|
||||
top: this.box.top + halfHeight,
|
||||
width: halfWidth,
|
||||
height: halfHeight
|
||||
});
|
||||
// Bottom left
|
||||
this.nodes[3] = new QuadTreeNode({
|
||||
left: this.box.left,
|
||||
top: this.box.top + halfHeight,
|
||||
width: halfWidth,
|
||||
height: halfHeight
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Determine which of the quadrants should be used when placing node in
|
||||
* the QuadTree. Returned index is always in range `< 0 , 3 >`.
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
* @return {number}
|
||||
*/
|
||||
getBoxPosition: function (point) {
|
||||
var left = point.plotX < this.box.left + this.box.width / 2, top = point.plotY < this.box.top + this.box.height / 2, index;
|
||||
if (left) {
|
||||
if (top) {
|
||||
// Top left
|
||||
index = 0;
|
||||
}
|
||||
else {
|
||||
// Bottom left
|
||||
index = 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (top) {
|
||||
// Top right
|
||||
index = 1;
|
||||
}
|
||||
else {
|
||||
// Bottom right
|
||||
index = 2;
|
||||
}
|
||||
}
|
||||
return index;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The QuadTree class. Used in Networkgraph chart as a base for Barnes-Hut
|
||||
* approximation.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.QuadTree
|
||||
*
|
||||
* @param {number} x left position of the plotting area
|
||||
* @param {number} y top position of the plotting area
|
||||
* @param {number} width width of the plotting area
|
||||
* @param {number} height height of the plotting area
|
||||
*/
|
||||
var QuadTree = H.QuadTree = function (x, y, width, height) {
|
||||
// Boundary rectangle:
|
||||
this.box = {
|
||||
left: x,
|
||||
top: y,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
this.maxDepth = 25;
|
||||
this.root = new QuadTreeNode(this.box, '0');
|
||||
this.root.isInternal = true;
|
||||
this.root.isRoot = true;
|
||||
this.root.divideBox();
|
||||
};
|
||||
extend(QuadTree.prototype,
|
||||
/** @lends Highcharts.QuadTree.prototype */
|
||||
{
|
||||
/**
|
||||
* Insert nodes into the QuadTree
|
||||
*
|
||||
* @param {Array<Highcharts.Point>} points
|
||||
*/
|
||||
insertNodes: function (points) {
|
||||
points.forEach(function (point) {
|
||||
this.root.insert(point, this.maxDepth);
|
||||
}, this);
|
||||
},
|
||||
/**
|
||||
* Depfth first treversal (DFS). Using `before` and `after` callbacks,
|
||||
* we can get two results: preorder and postorder traversals, reminder:
|
||||
*
|
||||
* ```
|
||||
* (a)
|
||||
* / \
|
||||
* (b) (c)
|
||||
* / \
|
||||
* (d) (e)
|
||||
* ```
|
||||
*
|
||||
* DFS (preorder): `a -> b -> d -> e -> c`
|
||||
*
|
||||
* DFS (postorder): `d -> e -> b -> c -> a`
|
||||
*
|
||||
* @param {Highcharts.QuadTreeNode|null} node
|
||||
* @param {Function} [beforeCallback] function to be called before
|
||||
* visiting children nodes
|
||||
* @param {Function} [afterCallback] function to be called after
|
||||
* visiting children nodes
|
||||
*/
|
||||
visitNodeRecursive: function (node, beforeCallback, afterCallback) {
|
||||
var goFurther;
|
||||
if (!node) {
|
||||
node = this.root;
|
||||
}
|
||||
if (node === this.root && beforeCallback) {
|
||||
goFurther = beforeCallback(node);
|
||||
}
|
||||
if (goFurther === false) {
|
||||
return;
|
||||
}
|
||||
node.nodes.forEach(function (qtNode) {
|
||||
if (qtNode.isInternal) {
|
||||
if (beforeCallback) {
|
||||
goFurther = beforeCallback(qtNode);
|
||||
}
|
||||
if (goFurther === false) {
|
||||
return;
|
||||
}
|
||||
this.visitNodeRecursive(qtNode, beforeCallback, afterCallback);
|
||||
}
|
||||
else if (qtNode.body) {
|
||||
if (beforeCallback) {
|
||||
beforeCallback(qtNode.body);
|
||||
}
|
||||
}
|
||||
if (afterCallback) {
|
||||
afterCallback(qtNode);
|
||||
}
|
||||
}, this);
|
||||
if (node === this.root && afterCallback) {
|
||||
afterCallback(node);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Calculate mass of the each QuadNode in the tree.
|
||||
*/
|
||||
calculateMassAndCenter: function () {
|
||||
this.visitNodeRecursive(null, null, function (node) {
|
||||
node.updateMassAndCenter();
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,355 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Point from '../Core/Series/Point.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
var seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* The ohlc series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.ohlc
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('ohlc', 'column'
|
||||
/**
|
||||
* An OHLC chart is a style of financial chart used to describe price
|
||||
* movements over time. It displays open, high, low and close values per
|
||||
* data point.
|
||||
*
|
||||
* @sample stock/demo/ohlc/
|
||||
* OHLC chart
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @excluding borderColor, borderRadius, borderWidth, crisp, stacking,
|
||||
* stack
|
||||
* @product highstock
|
||||
* @optionparent plotOptions.ohlc
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* The approximate pixel width of each group. If for example a series
|
||||
* with 30 points is displayed over a 600 pixel wide plot area, no
|
||||
* grouping is performed. If however the series contains so many points
|
||||
* that the spacing is less than the groupPixelWidth, Highcharts will
|
||||
* try to group it into appropriate groups so that each is more or less
|
||||
* two pixels wide. Defaults to `5`.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 5
|
||||
* @product highstock
|
||||
* @apioption plotOptions.ohlc.dataGrouping.groupPixelWidth
|
||||
*/
|
||||
/**
|
||||
* The pixel width of the line/border. Defaults to `1`.
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/ohlc-linewidth/
|
||||
* A greater line width
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @product highstock
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
lineWidth: 1,
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> ' +
|
||||
'<b> {series.name}</b><br/>' +
|
||||
'Open: {point.open}<br/>' +
|
||||
'High: {point.high}<br/>' +
|
||||
'Low: {point.low}<br/>' +
|
||||
'Close: {point.close}<br/>'
|
||||
},
|
||||
threshold: null,
|
||||
states: {
|
||||
/**
|
||||
* @extends plotOptions.column.states.hover
|
||||
* @product highstock
|
||||
*/
|
||||
hover: {
|
||||
/**
|
||||
* The pixel width of the line representing the OHLC point.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 3
|
||||
* @product highstock
|
||||
*/
|
||||
lineWidth: 3
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determines which one of `open`, `high`, `low`, `close` values should
|
||||
* be represented as `point.y`, which is later used to set dataLabel
|
||||
* position and [compare](#plotOptions.series.compare).
|
||||
*
|
||||
* @sample {highstock} stock/plotoptions/ohlc-pointvalkey/
|
||||
* Possible values
|
||||
*
|
||||
* @type {string}
|
||||
* @default close
|
||||
* @validvalue ["open", "high", "low", "close"]
|
||||
* @product highstock
|
||||
* @apioption plotOptions.ohlc.pointValKey
|
||||
*/
|
||||
/**
|
||||
* @default close
|
||||
* @apioption plotOptions.ohlc.colorKey
|
||||
*/
|
||||
/**
|
||||
* Line color for up points.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highstock
|
||||
* @apioption plotOptions.ohlc.upColor
|
||||
*/
|
||||
stickyTracking: true
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.seriesTypes.ohlc
|
||||
*/
|
||||
{
|
||||
/* eslint-disable valid-jsdoc */
|
||||
directTouch: false,
|
||||
pointArrayMap: ['open', 'high', 'low', 'close'],
|
||||
toYData: function (point) {
|
||||
// return a plain array for speedy calculation
|
||||
return [point.open, point.high, point.low, point.close];
|
||||
},
|
||||
pointValKey: 'close',
|
||||
pointAttrToOptions: {
|
||||
stroke: 'color',
|
||||
'stroke-width': 'lineWidth'
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcarts.seriesTypes.ohlc#init
|
||||
* @return {void}
|
||||
*/
|
||||
init: function () {
|
||||
seriesTypes.column.prototype.init.apply(this, arguments);
|
||||
this.options.stacking = void 0; // #8817
|
||||
},
|
||||
/**
|
||||
* Postprocess mapping between options and SVG attributes
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.ohlc#pointAttribs
|
||||
* @param {Highcharts.OHLCPoint} point
|
||||
* @param {string} state
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
*/
|
||||
pointAttribs: function (point, state) {
|
||||
var attribs = seriesTypes.column.prototype.pointAttribs.call(this, point, state), options = this.options;
|
||||
delete attribs.fill;
|
||||
if (!point.options.color &&
|
||||
options.upColor &&
|
||||
point.open < point.close) {
|
||||
attribs.stroke = options.upColor;
|
||||
}
|
||||
return attribs;
|
||||
},
|
||||
/**
|
||||
* Translate data points from raw values x and y to plotX and plotY
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.ohlc#translate
|
||||
* @return {void}
|
||||
*/
|
||||
translate: function () {
|
||||
var series = this, yAxis = series.yAxis, hasModifyValue = !!series.modifyValue, translated = [
|
||||
'plotOpen',
|
||||
'plotHigh',
|
||||
'plotLow',
|
||||
'plotClose',
|
||||
'yBottom'
|
||||
]; // translate OHLC for
|
||||
seriesTypes.column.prototype.translate.apply(series);
|
||||
// Do the translation
|
||||
series.points.forEach(function (point) {
|
||||
[point.open, point.high, point.low, point.close, point.low]
|
||||
.forEach(function (value, i) {
|
||||
if (value !== null) {
|
||||
if (hasModifyValue) {
|
||||
value = series.modifyValue(value);
|
||||
}
|
||||
point[translated[i]] =
|
||||
yAxis.toPixels(value, true);
|
||||
}
|
||||
});
|
||||
// Align the tooltip to the high value to avoid covering the
|
||||
// point
|
||||
point.tooltipPos[1] =
|
||||
point.plotHigh + yAxis.pos - series.chart.plotTop;
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Draw the data points
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.ohlc#drawPoints
|
||||
* @return {void}
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, points = series.points, chart = series.chart,
|
||||
/**
|
||||
* Extend vertical stem to open and close values.
|
||||
*/
|
||||
extendStem = function (path, halfStrokeWidth, openOrClose) {
|
||||
var start = path[0];
|
||||
var end = path[1];
|
||||
// We don't need to worry about crisp - openOrClose value
|
||||
// is already crisped and halfStrokeWidth should remove it.
|
||||
if (typeof start[2] === 'number') {
|
||||
start[2] = Math.max(openOrClose + halfStrokeWidth, start[2]);
|
||||
}
|
||||
if (typeof end[2] === 'number') {
|
||||
end[2] = Math.min(openOrClose - halfStrokeWidth, end[2]);
|
||||
}
|
||||
};
|
||||
points.forEach(function (point) {
|
||||
var plotOpen, plotClose, crispCorr, halfWidth, path, graphic = point.graphic, crispX, isNew = !graphic, strokeWidth;
|
||||
if (typeof point.plotY !== 'undefined') {
|
||||
// Create and/or update the graphic
|
||||
if (!graphic) {
|
||||
point.graphic = graphic = chart.renderer.path()
|
||||
.add(series.group);
|
||||
}
|
||||
if (!chart.styledMode) {
|
||||
graphic.attr(series.pointAttribs(point, (point.selected && 'select'))); // #3897
|
||||
}
|
||||
// crisp vector coordinates
|
||||
strokeWidth = graphic.strokeWidth();
|
||||
crispCorr = (strokeWidth % 2) / 2;
|
||||
// #2596:
|
||||
crispX = Math.round(point.plotX) - crispCorr;
|
||||
halfWidth = Math.round(point.shapeArgs.width / 2);
|
||||
// the vertical stem
|
||||
path = [
|
||||
['M', crispX, Math.round(point.yBottom)],
|
||||
['L', crispX, Math.round(point.plotHigh)]
|
||||
];
|
||||
// open
|
||||
if (point.open !== null) {
|
||||
plotOpen = Math.round(point.plotOpen) + crispCorr;
|
||||
path.push(['M', crispX, plotOpen], ['L', crispX - halfWidth, plotOpen]);
|
||||
extendStem(path, strokeWidth / 2, plotOpen);
|
||||
}
|
||||
// close
|
||||
if (point.close !== null) {
|
||||
plotClose = Math.round(point.plotClose) + crispCorr;
|
||||
path.push(['M', crispX, plotClose], ['L', crispX + halfWidth, plotClose]);
|
||||
extendStem(path, strokeWidth / 2, plotClose);
|
||||
}
|
||||
graphic[isNew ? 'attr' : 'animate']({ d: path })
|
||||
.addClass(point.getClassName(), true);
|
||||
}
|
||||
});
|
||||
},
|
||||
animate: null // Disable animation
|
||||
/* eslint-enable valid-jsdoc */
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.seriesTypes.ohlc.prototype.pointClass.prototype
|
||||
*/
|
||||
{
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Extend the parent method by adding up or down to the class name.
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.ohlc#getClassName
|
||||
* @return {string}
|
||||
*/
|
||||
getClassName: function () {
|
||||
return Point.prototype.getClassName.call(this) +
|
||||
(this.open < this.close ?
|
||||
' highcharts-point-up' :
|
||||
' highcharts-point-down');
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* A `ohlc` series. If the [type](#series.ohlc.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.ohlc
|
||||
* @excluding dataParser, dataURL
|
||||
* @product highstock
|
||||
* @apioption series.ohlc
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `ohlc` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 5 or 4 values. In this case, the values correspond
|
||||
* to `x,open,high,low,close`. If the first value is a string, it is applied
|
||||
* as the name of the point, and the `x` value is inferred. The `x` value can
|
||||
* also be omitted, in which case the inner arrays should be of length 4\.
|
||||
* Then the `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 6, 5, 6, 7],
|
||||
* [1, 9, 4, 8, 2],
|
||||
* [2, 6, 3, 4, 10]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.ohlc.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* open: 3,
|
||||
* high: 4,
|
||||
* low: 5,
|
||||
* close: 2,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* open: 4,
|
||||
* high: 3,
|
||||
* low: 6,
|
||||
* close: 7,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<Array<(number|string),number,number,number>|Array<(number|string),number,number,number,number>|*>}
|
||||
* @extends series.arearange.data
|
||||
* @excluding y, marker
|
||||
* @product highstock
|
||||
* @apioption series.ohlc.data
|
||||
*/
|
||||
/**
|
||||
* The closing value of each data point.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highstock
|
||||
* @apioption series.ohlc.data.close
|
||||
*/
|
||||
/**
|
||||
* The opening value of each data point.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highstock
|
||||
* @apioption series.ohlc.data.open
|
||||
*/
|
||||
''; // adds doclets above to transpilat
|
||||
@@ -0,0 +1,516 @@
|
||||
/* *
|
||||
*
|
||||
* Organization chart module
|
||||
*
|
||||
* (c) 2018-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var css = U.css, pick = U.pick, seriesType = U.seriesType, wrap = U.wrap;
|
||||
/**
|
||||
* Layout value for the child nodes in an organization chart. If `hanging`, this
|
||||
* node's children will hang below their parent, allowing a tighter packing of
|
||||
* nodes in the diagram.
|
||||
*
|
||||
* @typedef {"normal"|"hanging"} Highcharts.SeriesOrganizationNodesLayoutValue
|
||||
*/
|
||||
var base = H.seriesTypes.sankey.prototype;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.organization
|
||||
*
|
||||
* @augments Highcharts.seriesTypes.sankey
|
||||
*/
|
||||
seriesType('organization', 'sankey',
|
||||
/**
|
||||
* An organization chart is a diagram that shows the structure of an
|
||||
* organization and the relationships and relative ranks of its parts and
|
||||
* positions.
|
||||
*
|
||||
* @sample highcharts/demo/organization-chart/
|
||||
* Organization chart
|
||||
* @sample highcharts/series-organization/horizontal/
|
||||
* Horizontal organization chart
|
||||
* @sample highcharts/series-organization/borderless
|
||||
* Borderless design
|
||||
* @sample highcharts/series-organization/center-layout
|
||||
* Centered layout
|
||||
*
|
||||
* @extends plotOptions.sankey
|
||||
* @excluding allowPointSelect, curveFactor, dataSorting
|
||||
* @since 7.1.0
|
||||
* @product highcharts
|
||||
* @requires modules/organization
|
||||
* @optionparent plotOptions.organization
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* The border color of the node cards.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
* @private
|
||||
*/
|
||||
borderColor: '#666666',
|
||||
/**
|
||||
* The border radius of the node cards.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
borderRadius: 3,
|
||||
/**
|
||||
* Radius for the rounded corners of the links between nodes.
|
||||
*
|
||||
* @sample highcharts/series-organization/link-options
|
||||
* Square links
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
linkRadius: 10,
|
||||
borderWidth: 1,
|
||||
/**
|
||||
* @declare Highcharts.SeriesOrganizationDataLabelsOptionsObject
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dataLabels: {
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* A callback for defining the format for _nodes_ in the
|
||||
* organization chart. The `nodeFormat` option takes precedence
|
||||
* over `nodeFormatter`.
|
||||
*
|
||||
* In an organization chart, the `nodeFormatter` is a quite complex
|
||||
* function of the available options, striving for a good default
|
||||
* layout of cards with or without images. In organization chart,
|
||||
* the data labels come with `useHTML` set to true, meaning they
|
||||
* will be rendered as true HTML above the SVG.
|
||||
*
|
||||
* @sample highcharts/series-organization/datalabels-nodeformatter
|
||||
* Modify the default label format output
|
||||
*
|
||||
* @type {Highcharts.SeriesSankeyDataLabelsFormatterCallbackFunction}
|
||||
* @since 6.0.2
|
||||
*/
|
||||
nodeFormatter: function () {
|
||||
var outerStyle = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
'flex-direction': 'row',
|
||||
'align-items': 'center',
|
||||
'justify-content': 'center'
|
||||
}, imageStyle = {
|
||||
'max-height': '100%',
|
||||
'border-radius': '50%'
|
||||
}, innerStyle = {
|
||||
width: '100%',
|
||||
padding: 0,
|
||||
'text-align': 'center',
|
||||
'white-space': 'normal'
|
||||
}, nameStyle = {
|
||||
margin: 0
|
||||
}, titleStyle = {
|
||||
margin: 0
|
||||
}, descriptionStyle = {
|
||||
opacity: 0.75,
|
||||
margin: '5px'
|
||||
};
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function styleAttr(style) {
|
||||
return Object.keys(style).reduce(function (str, key) {
|
||||
return str + key + ':' + style[key] + ';';
|
||||
}, 'style="') + '"';
|
||||
}
|
||||
if (this.point.image) {
|
||||
imageStyle['max-width'] = '30%';
|
||||
innerStyle.width = '70%';
|
||||
}
|
||||
// PhantomJS doesn't support flex, roll back to absolute
|
||||
// positioning
|
||||
if (this.series.chart.renderer.forExport) {
|
||||
outerStyle.display = 'block';
|
||||
innerStyle.position = 'absolute';
|
||||
innerStyle.left = this.point.image ? '30%' : 0;
|
||||
innerStyle.top = 0;
|
||||
}
|
||||
var html = '<div ' + styleAttr(outerStyle) + '>';
|
||||
if (this.point.image) {
|
||||
html += '<img src="' + this.point.image + '" ' +
|
||||
styleAttr(imageStyle) + '>';
|
||||
}
|
||||
html += '<div ' + styleAttr(innerStyle) + '>';
|
||||
if (this.point.name) {
|
||||
html += '<h4 ' + styleAttr(nameStyle) + '>' +
|
||||
this.point.name + '</h4>';
|
||||
}
|
||||
if (this.point.title) {
|
||||
html += '<p ' + styleAttr(titleStyle) + '>' +
|
||||
(this.point.title || '') + '</p>';
|
||||
}
|
||||
if (this.point.description) {
|
||||
html += '<p ' + styleAttr(descriptionStyle) + '>' +
|
||||
this.point.description + '</p>';
|
||||
}
|
||||
html += '</div>' +
|
||||
'</div>';
|
||||
return html;
|
||||
},
|
||||
/* eslint-enable valid-jsdoc */
|
||||
style: {
|
||||
/** @internal */
|
||||
fontWeight: 'normal',
|
||||
/** @internal */
|
||||
fontSize: '13px'
|
||||
},
|
||||
useHTML: true
|
||||
},
|
||||
/**
|
||||
* The indentation in pixels of hanging nodes, nodes which parent has
|
||||
* [layout](#series.organization.nodes.layout) set to `hanging`.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
hangingIndent: 20,
|
||||
/**
|
||||
* The color of the links between nodes.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
* @private
|
||||
*/
|
||||
linkColor: '#666666',
|
||||
/**
|
||||
* The line width of the links connecting nodes, in pixels.
|
||||
*
|
||||
* @sample highcharts/series-organization/link-options
|
||||
* Square links
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
linkLineWidth: 1,
|
||||
/**
|
||||
* In a horizontal chart, the width of the nodes in pixels. Node that
|
||||
* most organization charts are vertical, so the name of this option
|
||||
* is counterintuitive.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
nodeWidth: 50,
|
||||
tooltip: {
|
||||
nodeFormat: '{point.name}<br>{point.title}<br>{point.description}'
|
||||
}
|
||||
}, {
|
||||
pointAttribs: function (point, state) {
|
||||
var series = this, attribs = base.pointAttribs.call(series, point, state), level = point.isNode ? point.level : point.fromNode.level, levelOptions = series.mapOptionsToLevel[level || 0] || {}, options = point.options, stateOptions = (levelOptions.states && levelOptions.states[state]) || {}, values = ['borderRadius', 'linkColor', 'linkLineWidth']
|
||||
.reduce(function (obj, key) {
|
||||
obj[key] = pick(stateOptions[key], options[key], levelOptions[key], series.options[key]);
|
||||
return obj;
|
||||
}, {});
|
||||
if (!point.isNode) {
|
||||
attribs.stroke = values.linkColor;
|
||||
attribs['stroke-width'] = values.linkLineWidth;
|
||||
delete attribs.fill;
|
||||
}
|
||||
else {
|
||||
if (values.borderRadius) {
|
||||
attribs.r = values.borderRadius;
|
||||
}
|
||||
}
|
||||
return attribs;
|
||||
},
|
||||
createNode: function (id) {
|
||||
var node = base.createNode
|
||||
.call(this, id);
|
||||
// All nodes in an org chart are equal width
|
||||
node.getSum = function () {
|
||||
return 1;
|
||||
};
|
||||
return node;
|
||||
},
|
||||
createNodeColumn: function () {
|
||||
var column = base.createNodeColumn.call(this);
|
||||
// Wrap the offset function so that the hanging node's children are
|
||||
// aligned to their parent
|
||||
wrap(column, 'offset', function (proceed, node, factor) {
|
||||
var offset = proceed.call(this, node, factor); // eslint-disable-line no-invalid-this
|
||||
// Modify the default output if the parent's layout is 'hanging'
|
||||
if (node.hangsFrom) {
|
||||
return {
|
||||
absoluteTop: node.hangsFrom.nodeY
|
||||
};
|
||||
}
|
||||
return offset;
|
||||
});
|
||||
return column;
|
||||
},
|
||||
translateNode: function (node, column) {
|
||||
base.translateNode.call(this, node, column);
|
||||
if (node.hangsFrom) {
|
||||
node.shapeArgs.height -=
|
||||
this.options.hangingIndent;
|
||||
if (!this.chart.inverted) {
|
||||
node.shapeArgs.y += this.options.hangingIndent;
|
||||
}
|
||||
}
|
||||
node.nodeHeight = this.chart.inverted ?
|
||||
node.shapeArgs.width :
|
||||
node.shapeArgs.height;
|
||||
},
|
||||
// General function to apply corner radius to a path - can be lifted to
|
||||
// renderer or utilities if we need it elsewhere.
|
||||
curvedPath: function (path, r) {
|
||||
var d = [];
|
||||
for (var i = 0; i < path.length; i++) {
|
||||
var x = path[i][1];
|
||||
var y = path[i][2];
|
||||
if (typeof x === 'number' && typeof y === 'number') {
|
||||
// moveTo
|
||||
if (i === 0) {
|
||||
d.push(['M', x, y]);
|
||||
}
|
||||
else if (i === path.length - 1) {
|
||||
d.push(['L', x, y]);
|
||||
// curveTo
|
||||
}
|
||||
else if (r) {
|
||||
var prevSeg = path[i - 1];
|
||||
var nextSeg = path[i + 1];
|
||||
if (prevSeg && nextSeg) {
|
||||
var x1 = prevSeg[1], y1 = prevSeg[2], x2 = nextSeg[1], y2 = nextSeg[2];
|
||||
// Only apply to breaks
|
||||
if (typeof x1 === 'number' &&
|
||||
typeof x2 === 'number' &&
|
||||
typeof y1 === 'number' &&
|
||||
typeof y2 === 'number' &&
|
||||
x1 !== x2 &&
|
||||
y1 !== y2) {
|
||||
var directionX = x1 < x2 ? 1 : -1, directionY = y1 < y2 ? 1 : -1;
|
||||
d.push([
|
||||
'L',
|
||||
x - directionX * Math.min(Math.abs(x - x1), r),
|
||||
y - directionY * Math.min(Math.abs(y - y1), r)
|
||||
], [
|
||||
'C',
|
||||
x,
|
||||
y,
|
||||
x,
|
||||
y,
|
||||
x + directionX * Math.min(Math.abs(x - x2), r),
|
||||
y + directionY * Math.min(Math.abs(y - y2), r)
|
||||
]);
|
||||
}
|
||||
}
|
||||
// lineTo
|
||||
}
|
||||
else {
|
||||
d.push(['L', x, y]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return d;
|
||||
},
|
||||
translateLink: function (point) {
|
||||
var fromNode = point.fromNode, toNode = point.toNode, crisp = Math.round(this.options.linkLineWidth) % 2 / 2, x1 = Math.floor(fromNode.shapeArgs.x +
|
||||
fromNode.shapeArgs.width) + crisp, y1 = Math.floor(fromNode.shapeArgs.y +
|
||||
fromNode.shapeArgs.height / 2) + crisp, x2 = Math.floor(toNode.shapeArgs.x) + crisp, y2 = Math.floor(toNode.shapeArgs.y +
|
||||
toNode.shapeArgs.height / 2) + crisp, xMiddle, hangingIndent = this.options.hangingIndent, toOffset = toNode.options.offset, percentOffset = /%$/.test(toOffset) && parseInt(toOffset, 10), inverted = this.chart.inverted;
|
||||
if (inverted) {
|
||||
x1 -= fromNode.shapeArgs.width;
|
||||
x2 += toNode.shapeArgs.width;
|
||||
}
|
||||
xMiddle = Math.floor(x2 +
|
||||
(inverted ? 1 : -1) *
|
||||
(this.colDistance - this.nodeWidth) / 2) + crisp;
|
||||
// Put the link on the side of the node when an offset is given. HR
|
||||
// node in the main demo.
|
||||
if (percentOffset &&
|
||||
(percentOffset >= 50 || percentOffset <= -50)) {
|
||||
xMiddle = x2 = Math.floor(x2 + (inverted ? -0.5 : 0.5) *
|
||||
toNode.shapeArgs.width) + crisp;
|
||||
y2 = toNode.shapeArgs.y;
|
||||
if (percentOffset > 0) {
|
||||
y2 += toNode.shapeArgs.height;
|
||||
}
|
||||
}
|
||||
if (toNode.hangsFrom === fromNode) {
|
||||
if (this.chart.inverted) {
|
||||
y1 = Math.floor(fromNode.shapeArgs.y +
|
||||
fromNode.shapeArgs.height -
|
||||
hangingIndent / 2) + crisp;
|
||||
y2 = (toNode.shapeArgs.y +
|
||||
toNode.shapeArgs.height);
|
||||
}
|
||||
else {
|
||||
y1 = Math.floor(fromNode.shapeArgs.y +
|
||||
hangingIndent / 2) + crisp;
|
||||
}
|
||||
xMiddle = x2 = Math.floor(toNode.shapeArgs.x +
|
||||
toNode.shapeArgs.width / 2) + crisp;
|
||||
}
|
||||
point.plotY = 1;
|
||||
point.shapeType = 'path';
|
||||
point.shapeArgs = {
|
||||
d: this.curvedPath([
|
||||
['M', x1, y1],
|
||||
['L', xMiddle, y1],
|
||||
['L', xMiddle, y2],
|
||||
['L', x2, y2]
|
||||
], this.options.linkRadius)
|
||||
};
|
||||
},
|
||||
alignDataLabel: function (point, dataLabel, options) {
|
||||
// Align the data label to the point graphic
|
||||
if (options.useHTML) {
|
||||
var width = point.shapeArgs.width, height = point.shapeArgs.height, padjust = (this.options.borderWidth +
|
||||
2 * this.options.dataLabels.padding);
|
||||
if (this.chart.inverted) {
|
||||
width = height;
|
||||
height = point.shapeArgs.width;
|
||||
}
|
||||
height -= padjust;
|
||||
width -= padjust;
|
||||
// Set the size of the surrounding div emulating `g`
|
||||
var text = dataLabel.text;
|
||||
if (text) {
|
||||
css(text.element.parentNode, {
|
||||
width: width + 'px',
|
||||
height: height + 'px'
|
||||
});
|
||||
// Set properties for the span emulating `text`
|
||||
css(text.element, {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflow: 'hidden'
|
||||
});
|
||||
}
|
||||
// The getBBox function is used in `alignDataLabel` to align
|
||||
// inside the box
|
||||
dataLabel.getBBox = function () {
|
||||
return {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
};
|
||||
// Overwrite dataLabel dimensions (#13100).
|
||||
dataLabel.width = width;
|
||||
dataLabel.height = height;
|
||||
}
|
||||
H.seriesTypes.column.prototype.alignDataLabel.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
/**
|
||||
* An `organization` series. If the [type](#series.organization.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.organization
|
||||
* @exclude dataSorting, boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/organization
|
||||
* @apioption series.organization
|
||||
*/
|
||||
/**
|
||||
* @type {Highcharts.SeriesOrganizationDataLabelsOptionsObject|Array<Highcharts.SeriesOrganizationDataLabelsOptionsObject>}
|
||||
* @product highcharts
|
||||
* @apioption series.organization.data.dataLabels
|
||||
*/
|
||||
/**
|
||||
* A collection of options for the individual nodes. The nodes in an org chart
|
||||
* are auto-generated instances of `Highcharts.Point`, but options can be
|
||||
* applied here and linked by the `id`.
|
||||
*
|
||||
* @extends series.sankey.nodes
|
||||
* @type {Array<*>}
|
||||
* @product highcharts
|
||||
* @apioption series.organization.nodes
|
||||
*/
|
||||
/**
|
||||
* Individual data label for each node. The options are the same as
|
||||
* the ones for [series.organization.dataLabels](#series.organization.dataLabels).
|
||||
*
|
||||
* @type {Highcharts.SeriesOrganizationDataLabelsOptionsObject|Array<Highcharts.SeriesOrganizationDataLabelsOptionsObject>}
|
||||
*
|
||||
* @apioption series.organization.nodes.dataLabels
|
||||
*/
|
||||
/**
|
||||
* The job description for the node card, will be inserted by the default
|
||||
* `dataLabel.nodeFormatter`.
|
||||
*
|
||||
* @sample highcharts/demo/organization-chart
|
||||
* Org chart with job descriptions
|
||||
*
|
||||
* @type {string}
|
||||
* @product highcharts
|
||||
* @apioption series.organization.nodes.description
|
||||
*/
|
||||
/**
|
||||
* An image for the node card, will be inserted by the default
|
||||
* `dataLabel.nodeFormatter`.
|
||||
*
|
||||
* @sample highcharts/demo/organization-chart
|
||||
* Org chart with images
|
||||
*
|
||||
* @type {string}
|
||||
* @product highcharts
|
||||
* @apioption series.organization.nodes.image
|
||||
*/
|
||||
/**
|
||||
* Layout for the node's children. If `hanging`, this node's children will hang
|
||||
* below their parent, allowing a tighter packing of nodes in the diagram.
|
||||
*
|
||||
* @sample highcharts/demo/organization-chart
|
||||
* Hanging layout
|
||||
*
|
||||
* @type {Highcharts.SeriesOrganizationNodesLayoutValue}
|
||||
* @default normal
|
||||
* @product highcharts
|
||||
* @apioption series.organization.nodes.layout
|
||||
*/
|
||||
/**
|
||||
* The job title for the node card, will be inserted by the default
|
||||
* `dataLabel.nodeFormatter`.
|
||||
*
|
||||
* @sample highcharts/demo/organization-chart
|
||||
* Org chart with job titles
|
||||
*
|
||||
* @type {string}
|
||||
* @product highcharts
|
||||
* @apioption series.organization.nodes.title
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `organization` series
|
||||
* type, points can be given in the following way:
|
||||
*
|
||||
* 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: [{
|
||||
* from: 'Category1',
|
||||
* to: 'Category2',
|
||||
* weight: 2
|
||||
* }, {
|
||||
* from: 'Category1',
|
||||
* to: 'Category3',
|
||||
* weight: 5
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<*>}
|
||||
* @extends series.sankey.data
|
||||
* @product highcharts
|
||||
* @apioption series.organization.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,140 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2017 Sebastian Bochan
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, merge = U.merge, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import derivedSeriesMixin from '../Mixins/DerivedSeries.js';
|
||||
/**
|
||||
* The pareto series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.pareto
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('pareto', 'line'
|
||||
/**
|
||||
* A pareto diagram is a type of chart that contains both bars and a line
|
||||
* graph, where individual values are represented in descending order by
|
||||
* bars, and the cumulative total is represented by the line.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/pareto/
|
||||
* Pareto diagram
|
||||
*
|
||||
* @extends plotOptions.line
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @excluding allAreas, boostThreshold, borderColor, borderRadius,
|
||||
* borderWidth, crisp, colorAxis, depth, data, dragDrop,
|
||||
* edgeColor, edgeWidth, findNearestPointBy, gapSize, gapUnit,
|
||||
* grouping, groupPadding, groupZPadding, maxPointWidth, keys,
|
||||
* negativeColor, pointInterval, pointIntervalUnit,
|
||||
* pointPadding, pointPlacement, pointRange, pointStart,
|
||||
* pointWidth, shadow, step, softThreshold, stacking,
|
||||
* threshold, zoneAxis, zones, boostBlending
|
||||
* @requires modules/pareto
|
||||
* @optionparent plotOptions.pareto
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* Higher zIndex than column series to draw line above shapes.
|
||||
*/
|
||||
zIndex: 3
|
||||
},
|
||||
/* eslint-disable no-invalid-this, valid-jsdoc */
|
||||
merge(derivedSeriesMixin, {
|
||||
/**
|
||||
* Calculate sum and return percent points.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#setDerivedData
|
||||
* @requires modules/pareto
|
||||
*/
|
||||
setDerivedData: function () {
|
||||
var xValues = this.baseSeries.xData, yValues = this.baseSeries.yData, sum = this.sumPointsPercents(yValues, xValues, null, true);
|
||||
this.setData(this.sumPointsPercents(yValues, xValues, sum, false), false);
|
||||
},
|
||||
/**
|
||||
* Calculate y sum and each percent point.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#sumPointsPercents
|
||||
*
|
||||
* @param {Array<number>} yValues
|
||||
* Y values
|
||||
*
|
||||
* @param {Array<number>} xValues
|
||||
* X values
|
||||
*
|
||||
* @param {number} sum
|
||||
* Sum of all y values
|
||||
*
|
||||
* @param {boolean} [isSum]
|
||||
* Declares if calculate sum of all points
|
||||
*
|
||||
* @return {number|Array<number,number>}
|
||||
* Returns sum of points or array of points [x,sum]
|
||||
*
|
||||
* @requires modules/pareto
|
||||
*/
|
||||
sumPointsPercents: function (yValues, xValues, sum, isSum) {
|
||||
var sumY = 0, sumPercent = 0, percentPoints = [], percentPoint;
|
||||
yValues.forEach(function (point, i) {
|
||||
if (point !== null) {
|
||||
if (isSum) {
|
||||
sumY += point;
|
||||
}
|
||||
else {
|
||||
percentPoint = (point / sum) * 100;
|
||||
percentPoints.push([
|
||||
xValues[i],
|
||||
correctFloat(sumPercent + percentPoint)
|
||||
]);
|
||||
sumPercent += percentPoint;
|
||||
}
|
||||
}
|
||||
});
|
||||
return (isSum ? sumY : percentPoints);
|
||||
}
|
||||
})
|
||||
/* eslint-enable no-invalid-this, valid-jsdoc */
|
||||
);
|
||||
/**
|
||||
* A `pareto` series. If the [type](#series.pareto.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.pareto
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @excluding data, dataParser, dataURL, boostThreshold, boostBlending
|
||||
* @requires modules/pareto
|
||||
* @apioption series.pareto
|
||||
*/
|
||||
/**
|
||||
* An integer identifying the index to use for the base series, or a string
|
||||
* representing the id of the series.
|
||||
*
|
||||
* @type {number|string}
|
||||
* @default undefined
|
||||
* @apioption series.pareto.baseSeries
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `pareto` series type,
|
||||
* points are calculated dynamically.
|
||||
*
|
||||
* @type {Array<Array<number|string>|*>}
|
||||
* @extends series.column.data
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption series.pareto.data
|
||||
*/
|
||||
''; // adds the doclets above to the transpiled file
|
||||
@@ -0,0 +1,141 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* 3D pie series
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var pick = U.pick, wrap = U.wrap;
|
||||
var deg2rad = H.deg2rad, seriesTypes = H.seriesTypes, svg = H.svg;
|
||||
/**
|
||||
* The thickness of a 3D pie.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
* @since 4.0
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @apioption plotOptions.pie.depth
|
||||
*/
|
||||
/* eslint-disable no-invalid-this */
|
||||
wrap(seriesTypes.pie.prototype, 'translate', function (proceed) {
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
// Do not do this if the chart is not 3D
|
||||
if (!this.chart.is3d()) {
|
||||
return;
|
||||
}
|
||||
var series = this, seriesOptions = series.options, depth = seriesOptions.depth || 0, options3d = series.chart.options.chart.options3d, alpha = options3d.alpha, beta = options3d.beta, z = seriesOptions.stacking ?
|
||||
(seriesOptions.stack || 0) * depth :
|
||||
series._i * depth;
|
||||
z += depth / 2;
|
||||
if (seriesOptions.grouping !== false) {
|
||||
z = 0;
|
||||
}
|
||||
series.data.forEach(function (point) {
|
||||
var shapeArgs = point.shapeArgs, angle;
|
||||
point.shapeType = 'arc3d';
|
||||
shapeArgs.z = z;
|
||||
shapeArgs.depth = depth * 0.75;
|
||||
shapeArgs.alpha = alpha;
|
||||
shapeArgs.beta = beta;
|
||||
shapeArgs.center = series.center;
|
||||
angle = (shapeArgs.end + shapeArgs.start) / 2;
|
||||
point.slicedTranslation = {
|
||||
translateX: Math.round(Math.cos(angle) *
|
||||
seriesOptions.slicedOffset *
|
||||
Math.cos(alpha * deg2rad)),
|
||||
translateY: Math.round(Math.sin(angle) *
|
||||
seriesOptions.slicedOffset *
|
||||
Math.cos(alpha * deg2rad))
|
||||
};
|
||||
});
|
||||
});
|
||||
wrap(seriesTypes.pie.prototype.pointClass.prototype, 'haloPath', function (proceed) {
|
||||
var args = arguments;
|
||||
return this.series.chart.is3d() ? [] : proceed.call(this, args[1]);
|
||||
});
|
||||
wrap(seriesTypes.pie.prototype, 'pointAttribs', function (proceed, point, state) {
|
||||
var attr = proceed.call(this, point, state), options = this.options;
|
||||
if (this.chart.is3d() && !this.chart.styledMode) {
|
||||
attr.stroke = options.edgeColor || point.color || this.color;
|
||||
attr['stroke-width'] = pick(options.edgeWidth, 1);
|
||||
}
|
||||
return attr;
|
||||
});
|
||||
wrap(seriesTypes.pie.prototype, 'drawDataLabels', function (proceed) {
|
||||
if (this.chart.is3d()) {
|
||||
var series = this, chart = series.chart, options3d = chart.options.chart.options3d;
|
||||
series.data.forEach(function (point) {
|
||||
var shapeArgs = point.shapeArgs, r = shapeArgs.r,
|
||||
// #3240 issue with datalabels for 0 and null values
|
||||
a1 = (shapeArgs.alpha || options3d.alpha) * deg2rad, b1 = (shapeArgs.beta || options3d.beta) * deg2rad, a2 = (shapeArgs.start + shapeArgs.end) / 2, labelPosition = point.labelPosition, connectorPosition = labelPosition.connectorPosition, yOffset = (-r * (1 - Math.cos(a1)) * Math.sin(a2)), xOffset = r * (Math.cos(b1) - 1) * Math.cos(a2);
|
||||
// Apply perspective on label positions
|
||||
[
|
||||
labelPosition.natural,
|
||||
connectorPosition.breakAt,
|
||||
connectorPosition.touchingSliceAt
|
||||
].forEach(function (coordinates) {
|
||||
coordinates.x += xOffset;
|
||||
coordinates.y += yOffset;
|
||||
});
|
||||
});
|
||||
}
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
});
|
||||
wrap(seriesTypes.pie.prototype, 'addPoint', function (proceed) {
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
if (this.chart.is3d()) {
|
||||
// destroy (and rebuild) everything!!!
|
||||
this.update(this.userOptions, true); // #3845 pass the old options
|
||||
}
|
||||
});
|
||||
wrap(seriesTypes.pie.prototype, 'animate', function (proceed) {
|
||||
if (!this.chart.is3d()) {
|
||||
proceed.apply(this, [].slice.call(arguments, 1));
|
||||
}
|
||||
else {
|
||||
var args = arguments, init = args[1], animation = this.options.animation, attribs, center = this.center, group = this.group, markerGroup = this.markerGroup;
|
||||
if (svg) { // VML is too slow anyway
|
||||
if (animation === true) {
|
||||
animation = {};
|
||||
}
|
||||
// Initialize the animation
|
||||
if (init) {
|
||||
// Scale down the group and place it in the center
|
||||
group.oldtranslateX = pick(group.oldtranslateX, group.translateX);
|
||||
group.oldtranslateY = pick(group.oldtranslateY, group.translateY);
|
||||
attribs = {
|
||||
translateX: center[0],
|
||||
translateY: center[1],
|
||||
scaleX: 0.001,
|
||||
scaleY: 0.001
|
||||
};
|
||||
group.attr(attribs);
|
||||
if (markerGroup) {
|
||||
markerGroup.attrSetters = group.attrSetters;
|
||||
markerGroup.attr(attribs);
|
||||
}
|
||||
// Run the animation
|
||||
}
|
||||
else {
|
||||
attribs = {
|
||||
translateX: group.oldtranslateX,
|
||||
translateY: group.oldtranslateY,
|
||||
scaleX: 1,
|
||||
scaleY: 1
|
||||
};
|
||||
group.animate(attribs, animation);
|
||||
if (markerGroup) {
|
||||
markerGroup.animate(attribs, animation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,144 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Legend.js';
|
||||
import '../Series/ScatterSeries.js';
|
||||
var noop = H.noop, Series = H.Series, seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* A polygon series can be used to draw any freeform shape in the cartesian
|
||||
* coordinate system. A fill is applied with the `color` option, and
|
||||
* stroke is applied through `lineWidth` and `lineColor` options.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/polygon/
|
||||
* Polygon
|
||||
* @sample {highstock} highcharts/demo/polygon/
|
||||
* Polygon
|
||||
*
|
||||
* @extends plotOptions.scatter
|
||||
* @since 4.1.0
|
||||
* @excluding jitter, softThreshold, threshold, cluster, boostThreshold,
|
||||
* boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.polygon
|
||||
*/
|
||||
seriesType('polygon', 'scatter', {
|
||||
marker: {
|
||||
enabled: false,
|
||||
states: {
|
||||
hover: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
},
|
||||
stickyTracking: false,
|
||||
tooltip: {
|
||||
followPointer: true,
|
||||
pointFormat: ''
|
||||
},
|
||||
trackByArea: true
|
||||
// Prototype members
|
||||
}, {
|
||||
type: 'polygon',
|
||||
getGraphPath: function () {
|
||||
var graphPath = Series.prototype.getGraphPath.call(this), i = graphPath.length + 1;
|
||||
// Close all segments
|
||||
while (i--) {
|
||||
if ((i === graphPath.length || graphPath[i][0] === 'M') && i > 0) {
|
||||
graphPath.splice(i, 0, ['Z']);
|
||||
}
|
||||
}
|
||||
this.areaPath = graphPath;
|
||||
return graphPath;
|
||||
},
|
||||
drawGraph: function () {
|
||||
// Hack into the fill logic in area.drawGraph
|
||||
this.options.fillColor = this.color;
|
||||
seriesTypes.area.prototype.drawGraph.call(this);
|
||||
},
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle,
|
||||
drawTracker: Series.prototype.drawTracker,
|
||||
setStackedPoints: noop // No stacking points on polygons (#5310)
|
||||
});
|
||||
/**
|
||||
* A `polygon` series. If the [type](#series.polygon.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.polygon
|
||||
* @excluding dataParser, dataURL, stack, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires highcharts-more
|
||||
* @apioption series.polygon
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `polygon` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 10],
|
||||
* [1, 3],
|
||||
* [2, 1]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.polygon.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 1,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 8,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.polygon.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,121 @@
|
||||
/* *
|
||||
*
|
||||
* Highcharts pyramid3d series module
|
||||
*
|
||||
* (c) 2010-2020 Highsoft AS
|
||||
* Author: Kacper Madej
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
import '../Series/ColumnSeries.js';
|
||||
import '../Core/Renderer/SVG/SVGRenderer.js';
|
||||
/**
|
||||
* The pyramid3d series type.
|
||||
*
|
||||
* @constructor seriesTypes.pyramid3d
|
||||
* @augments seriesTypes.funnel3d
|
||||
* @requires highcharts-3d
|
||||
* @requires modules/cylinder
|
||||
* @requires modules/funnel3d
|
||||
* @requires modules/pyramid3d
|
||||
*/
|
||||
seriesType('pyramid3d', 'funnel3d',
|
||||
/**
|
||||
* A pyramid3d is a 3d version of pyramid series type. Pyramid charts are
|
||||
* a type of chart often used to visualize stages in a sales project,
|
||||
* where the top are the initial stages with the most clients.
|
||||
*
|
||||
* @sample highcharts/demo/pyramid3d/
|
||||
* Pyramid3d
|
||||
*
|
||||
* @extends plotOptions.funnel3d
|
||||
* @excluding neckHeight, neckWidth, dataSorting
|
||||
* @product highcharts
|
||||
* @since 7.1.0
|
||||
* @requires highcharts-3d
|
||||
* @requires modules/cylinder
|
||||
* @requires modules/funnel3d
|
||||
* @requires modules/pyramid3d
|
||||
* @optionparent plotOptions.pyramid3d
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* A reversed pyramid3d is funnel3d, but the latter supports neck
|
||||
* related options: neckHeight and neckWidth
|
||||
*
|
||||
* @product highcharts
|
||||
*/
|
||||
reversed: true,
|
||||
neckHeight: 0,
|
||||
neckWidth: 0,
|
||||
dataLabels: {
|
||||
verticalAlign: 'top'
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `pyramid3d` series. If the [type](#series.pyramid3d.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @since 7.1.0
|
||||
* @extends series.pyramid,plotOptions.pyramid3d
|
||||
* @excluding allAreas,boostThreshold,colorAxis,compare,compareBase,dataSorting
|
||||
* @product highcharts
|
||||
* @sample {highcharts} highcharts/demo/pyramid3d/ Pyramid3d
|
||||
* @requires modules/pyramid3d
|
||||
* @apioption series.pyramid3d
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `pyramid3d` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values
|
||||
* will be interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from `pointStart`
|
||||
* and `pointInterval` given in the series options. If the axis has
|
||||
* categories, these will be used. Example:
|
||||
*
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.pyramid3d.turboThreshold),
|
||||
* this option is not available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 2,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<number>|*>}
|
||||
* @extends series.funnel3d.data
|
||||
* @product highcharts
|
||||
* @apioption series.pyramid3d.data
|
||||
*/
|
||||
''; // adds doclets above to the transpiled file
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* Scatter 3D series.
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import Math3D from '../Extensions/Math3D.js';
|
||||
var pointCameraDistance = Math3D.pointCameraDistance;
|
||||
import Point from '../Core/Series/Point.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
var seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.scatter3d
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('scatter3d', 'scatter',
|
||||
/**
|
||||
* A 3D scatter plot uses x, y and z coordinates to display values for three
|
||||
* variables for a set of data.
|
||||
*
|
||||
* @sample {highcharts} highcharts/3d/scatter/
|
||||
* Simple 3D scatter
|
||||
* @sample {highcharts} highcharts/demo/3d-scatter-draggable
|
||||
* Draggable 3d scatter
|
||||
*
|
||||
* @extends plotOptions.scatter
|
||||
* @excluding dragDrop, cluster, boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @optionparent plotOptions.scatter3d
|
||||
*/
|
||||
{
|
||||
tooltip: {
|
||||
pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>z: <b>{point.z}</b><br/>'
|
||||
}
|
||||
// Series class
|
||||
}, {
|
||||
pointAttribs: function (point) {
|
||||
var attribs = seriesTypes.scatter.prototype.pointAttribs
|
||||
.apply(this, arguments);
|
||||
if (this.chart.is3d() && point) {
|
||||
attribs.zIndex =
|
||||
pointCameraDistance(point, this.chart);
|
||||
}
|
||||
return attribs;
|
||||
},
|
||||
axisTypes: ['xAxis', 'yAxis', 'zAxis'],
|
||||
pointArrayMap: ['x', 'y', 'z'],
|
||||
parallelArrays: ['x', 'y', 'z'],
|
||||
// Require direct touch rather than using the k-d-tree, because the
|
||||
// k-d-tree currently doesn't take the xyz coordinate system into
|
||||
// account (#4552)
|
||||
directTouch: true
|
||||
// Point class
|
||||
}, {
|
||||
applyOptions: function () {
|
||||
Point.prototype.applyOptions.apply(this, arguments);
|
||||
if (typeof this.z === 'undefined') {
|
||||
this.z = 0;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `scatter3d` series. If the [type](#series.scatter3d.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* scatter3d](#plotOptions.scatter3d).
|
||||
*
|
||||
* @extends series,plotOptions.scatter3d
|
||||
* @excluding boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-3d
|
||||
* @apioption series.scatter3d
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `scatter3d` 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,y,z`. If the first value is a string, it is applied as the name
|
||||
* of the point, and the `x` value is inferred.
|
||||
*
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 0, 1],
|
||||
* [1, 8, 7],
|
||||
* [2, 9, 2]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.scatter3d.turboThreshold), this option is not
|
||||
* available.
|
||||
*
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 2,
|
||||
* z: 24,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 4,
|
||||
* z: 12,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|*>}
|
||||
* @extends series.scatter.data
|
||||
* @product highcharts
|
||||
* @apioption series.scatter3d.data
|
||||
*/
|
||||
/**
|
||||
* The z value for each data point.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.scatter3d.data.z
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,252 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Series.js';
|
||||
var Series = H.Series;
|
||||
/**
|
||||
* Scatter series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.scatter
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('scatter', 'line',
|
||||
/**
|
||||
* A scatter plot uses cartesian coordinates to display values for two
|
||||
* variables for a set of data.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/scatter/
|
||||
* Scatter plot
|
||||
*
|
||||
* @extends plotOptions.line
|
||||
* @excluding cropThreshold, pointPlacement, shadow, useOhlcData
|
||||
* @product highcharts highstock
|
||||
* @optionparent plotOptions.scatter
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* The width of the line connecting the data points.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/scatter-linewidth-none/
|
||||
* 0 by default
|
||||
* @sample {highcharts} highcharts/plotoptions/scatter-linewidth-1/
|
||||
* 1px
|
||||
*
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
lineWidth: 0,
|
||||
findNearestPointBy: 'xy',
|
||||
/**
|
||||
* Apply a jitter effect for the rendered markers. When plotting
|
||||
* discrete values, a little random noise may help telling the points
|
||||
* apart. The jitter setting applies a random displacement of up to `n`
|
||||
* axis units in either direction. So for example on a horizontal X
|
||||
* axis, setting the `jitter.x` to 0.24 will render the point in a
|
||||
* random position between 0.24 units to the left and 0.24 units to the
|
||||
* right of the true axis position. On a category axis, setting it to
|
||||
* 0.5 will fill up the bin and make the data appear continuous.
|
||||
*
|
||||
* When rendered on top of a box plot or a column series, a jitter value
|
||||
* of 0.24 will correspond to the underlying series' default
|
||||
* [groupPadding](
|
||||
* https://api.highcharts.com/highcharts/plotOptions.column.groupPadding)
|
||||
* and [pointPadding](
|
||||
* https://api.highcharts.com/highcharts/plotOptions.column.pointPadding)
|
||||
* settings.
|
||||
*
|
||||
* @sample {highcharts} highcharts/series-scatter/jitter
|
||||
* Jitter on a scatter plot
|
||||
*
|
||||
* @sample {highcharts} highcharts/series-scatter/jitter-boxplot
|
||||
* Jittered scatter plot on top of a box plot
|
||||
*
|
||||
* @product highcharts highstock
|
||||
* @since 7.0.2
|
||||
*/
|
||||
jitter: {
|
||||
/**
|
||||
* The maximal X offset for the random jitter effect.
|
||||
*/
|
||||
x: 0,
|
||||
/**
|
||||
* The maximal Y offset for the random jitter effect.
|
||||
*/
|
||||
y: 0
|
||||
},
|
||||
marker: {
|
||||
enabled: true // Overrides auto-enabling in line series (#3647)
|
||||
},
|
||||
/**
|
||||
* Sticky tracking of mouse events. When true, the `mouseOut` event
|
||||
* on a series isn't triggered until the mouse moves over another
|
||||
* series, or out of the plot area. When false, the `mouseOut` event on
|
||||
* a series is triggered when the mouse leaves the area around the
|
||||
* series' graph or markers. This also implies the tooltip. When
|
||||
* `stickyTracking` is false and `tooltip.shared` is false, the tooltip
|
||||
* will be hidden when moving the mouse between series.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @product highcharts highstock
|
||||
* @apioption plotOptions.scatter.stickyTracking
|
||||
*/
|
||||
/**
|
||||
* A configuration object for the tooltip rendering of each single
|
||||
* series. Properties are inherited from [tooltip](#tooltip).
|
||||
* Overridable properties are `headerFormat`, `pointFormat`,
|
||||
* `yDecimals`, `xDateFormat`, `yPrefix` and `ySuffix`. Unlike other
|
||||
* series, in a scatter plot the series.name by default shows in the
|
||||
* headerFormat and point.x and point.y in the pointFormat.
|
||||
*
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
tooltip: {
|
||||
headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
|
||||
'<span style="font-size: 10px"> {series.name}</span><br/>',
|
||||
pointFormat: 'x: <b>{point.x}</b><br/>y: <b>{point.y}</b><br/>'
|
||||
}
|
||||
// Prototype members
|
||||
}, {
|
||||
sorted: false,
|
||||
requireSorting: false,
|
||||
noSharedTooltip: true,
|
||||
trackerGroups: ['group', 'markerGroup', 'dataLabelsGroup'],
|
||||
takeOrdinalPosition: false,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.scatter#drawGraph
|
||||
*/
|
||||
drawGraph: function () {
|
||||
if (this.options.lineWidth) {
|
||||
Series.prototype.drawGraph.call(this);
|
||||
}
|
||||
},
|
||||
// Optionally add the jitter effect
|
||||
applyJitter: function () {
|
||||
var series = this, jitter = this.options.jitter, len = this.points.length;
|
||||
/**
|
||||
* Return a repeatable, pseudo-random number based on an integer
|
||||
* seed.
|
||||
* @private
|
||||
*/
|
||||
function unrandom(seed) {
|
||||
var rand = Math.sin(seed) * 10000;
|
||||
return rand - Math.floor(rand);
|
||||
}
|
||||
if (jitter) {
|
||||
this.points.forEach(function (point, i) {
|
||||
['x', 'y'].forEach(function (dim, j) {
|
||||
var axis, plotProp = 'plot' + dim.toUpperCase(), min, max, translatedJitter;
|
||||
if (jitter[dim] && !point.isNull) {
|
||||
axis = series[dim + 'Axis'];
|
||||
translatedJitter =
|
||||
jitter[dim] * axis.transA;
|
||||
if (axis && !axis.isLog) {
|
||||
// Identify the outer bounds of the jitter range
|
||||
min = Math.max(0, point[plotProp] - translatedJitter);
|
||||
max = Math.min(axis.len, point[plotProp] + translatedJitter);
|
||||
// Find a random position within this range
|
||||
point[plotProp] = min +
|
||||
(max - min) * unrandom(i + j * len);
|
||||
// Update clientX for the tooltip k-d-tree
|
||||
if (dim === 'x') {
|
||||
point.clientX = point.plotX;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/* eslint-disable no-invalid-this */
|
||||
addEvent(Series, 'afterTranslate', function () {
|
||||
if (this.applyJitter) {
|
||||
this.applyJitter();
|
||||
}
|
||||
});
|
||||
/* eslint-enable no-invalid-this */
|
||||
/**
|
||||
* A `scatter` series. If the [type](#series.scatter.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.scatter
|
||||
* @excluding cropThreshold, dataParser, dataURL, useOhlcData
|
||||
* @product highcharts highstock
|
||||
* @apioption series.scatter
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `scatter` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 0],
|
||||
* [1, 8],
|
||||
* [2, 9]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.scatter.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 2,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.scatter.data
|
||||
*/
|
||||
''; // adds doclets above to transpilat
|
||||
@@ -0,0 +1,450 @@
|
||||
/* *
|
||||
*
|
||||
* Solid angular gauge module
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Color from '../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import H from '../Core/Globals.js';
|
||||
import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var clamp = U.clamp, extend = U.extend, isNumber = U.isNumber, merge = U.merge, pick = U.pick, pInt = U.pInt, seriesType = U.seriesType, wrap = U.wrap;
|
||||
/**
|
||||
* Additional options, depending on the actual symbol drawn.
|
||||
*
|
||||
* @interface Highcharts.SymbolOptionsObject
|
||||
*/ /**
|
||||
* Whether to draw rounded edges.
|
||||
* @name Highcharts.SymbolOptionsObject#rounded
|
||||
* @type {boolean|undefined}
|
||||
*/
|
||||
import '../Core/Options.js';
|
||||
import '../Series/GaugeSeries.js';
|
||||
var Renderer = H.Renderer;
|
||||
/**
|
||||
* Symbol definition of an arc with round edges.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Renderer#symbols.arc
|
||||
*
|
||||
* @param {number} x
|
||||
* The X coordinate for the top left position.
|
||||
*
|
||||
* @param {number} y
|
||||
* The Y coordinate for the top left position.
|
||||
*
|
||||
* @param {number} w
|
||||
* The pixel width.
|
||||
*
|
||||
* @param {number} h
|
||||
* The pixel height.
|
||||
*
|
||||
* @param {Highcharts.SymbolOptionsObject} [options]
|
||||
* Additional options, depending on the actual symbol drawn.
|
||||
*
|
||||
* @return {Highcharts.SVGPathArray}
|
||||
* Path of the created arc.
|
||||
*/
|
||||
wrap(Renderer.prototype.symbols, 'arc', function (proceed, x, y, w, h, options) {
|
||||
var arc = proceed, path = arc(x, y, w, h, options);
|
||||
if (options.rounded) {
|
||||
var r = options.r || w, smallR = (r - (options.innerR || 0)) / 2, outerArcStart = path[0], innerArcStart = path[2];
|
||||
if (outerArcStart[0] === 'M' && innerArcStart[0] === 'L') {
|
||||
var x1 = outerArcStart[1], y1 = outerArcStart[2], x2 = innerArcStart[1], y2 = innerArcStart[2], roundStart = ['A', smallR, smallR, 0, 1, 1, x1, y1], roundEnd = ['A', smallR, smallR, 0, 1, 1, x2, y2];
|
||||
// Replace the line segment and the last close segment
|
||||
path[2] = roundEnd;
|
||||
path[4] = roundStart;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
});
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var SolidGaugeAxis;
|
||||
(function (SolidGaugeAxis) {
|
||||
/* *
|
||||
*
|
||||
* Interfaces
|
||||
*
|
||||
* */
|
||||
/* *
|
||||
*
|
||||
* Constants
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* These methods are defined in the ColorAxis object, and copied here.
|
||||
* @private
|
||||
*
|
||||
* @todo
|
||||
* If we implement an AMD system we should make ColorAxis a dependency.
|
||||
*/
|
||||
var methods = {
|
||||
initDataClasses: function (userOptions) {
|
||||
var chart = this.chart, dataClasses, colorCounter = 0, options = this.options;
|
||||
this.dataClasses = dataClasses = [];
|
||||
userOptions.dataClasses.forEach(function (dataClass, i) {
|
||||
var colors;
|
||||
dataClass = merge(dataClass);
|
||||
dataClasses.push(dataClass);
|
||||
if (!dataClass.color) {
|
||||
if (options.dataClassColor === 'category') {
|
||||
colors = chart.options.colors;
|
||||
dataClass.color = colors[colorCounter++];
|
||||
// loop back to zero
|
||||
if (colorCounter === colors.length) {
|
||||
colorCounter = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
dataClass.color = color(options.minColor).tweenTo(color(options.maxColor), i / (userOptions.dataClasses.length - 1));
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
initStops: function (userOptions) {
|
||||
this.stops = userOptions.stops || [
|
||||
[0, this.options.minColor],
|
||||
[1, this.options.maxColor]
|
||||
];
|
||||
this.stops.forEach(function (stop) {
|
||||
stop.color = color(stop[1]);
|
||||
});
|
||||
},
|
||||
// Translate from a value to a color
|
||||
toColor: function (value, point) {
|
||||
var pos, stops = this.stops, from, to, color, dataClasses = this.dataClasses, dataClass, i;
|
||||
if (dataClasses) {
|
||||
i = dataClasses.length;
|
||||
while (i--) {
|
||||
dataClass = dataClasses[i];
|
||||
from = dataClass.from;
|
||||
to = dataClass.to;
|
||||
if ((typeof from === 'undefined' || value >= from) &&
|
||||
(typeof to === 'undefined' || value <= to)) {
|
||||
color = dataClass.color;
|
||||
if (point) {
|
||||
point.dataClass = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.logarithmic) {
|
||||
value = this.val2lin(value);
|
||||
}
|
||||
pos = 1 - ((this.max - value) / (this.max - this.min));
|
||||
i = stops.length;
|
||||
while (i--) {
|
||||
if (pos > stops[i][0]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
from = stops[i] || stops[i + 1];
|
||||
to = stops[i + 1] || from;
|
||||
// The position within the gradient
|
||||
pos = (1 - (to[0] - pos) / ((to[0] -
|
||||
from[0]) || 1));
|
||||
color = from.color.tweenTo(to.color, pos);
|
||||
}
|
||||
return color;
|
||||
}
|
||||
};
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function init(axis) {
|
||||
extend(axis, methods);
|
||||
}
|
||||
SolidGaugeAxis.init = init;
|
||||
})(SolidGaugeAxis || (SolidGaugeAxis = {}));
|
||||
/**
|
||||
* A solid gauge is a circular gauge where the value is indicated by a filled
|
||||
* arc, and the color of the arc may variate with the value.
|
||||
*
|
||||
* @sample highcharts/demo/gauge-solid/
|
||||
* Solid gauges
|
||||
*
|
||||
* @extends plotOptions.gauge
|
||||
* @excluding dial, pivot, wrap
|
||||
* @product highcharts
|
||||
* @requires modules/solid-gauge
|
||||
* @optionparent plotOptions.solidgauge
|
||||
*/
|
||||
var solidGaugeOptions = {
|
||||
/**
|
||||
* The inner radius for points in a solid gauge. Can be given as a number
|
||||
* (pixels) or percentage string.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/solidgauge-radius/
|
||||
* Individual radius and innerRadius
|
||||
*
|
||||
* @type {number|string}
|
||||
* @default 60
|
||||
* @since 4.1.6
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.solidgauge.innerRadius
|
||||
*/
|
||||
/**
|
||||
* Whether the strokes of the solid gauge should be `round` or `square`.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/gauge-activity/
|
||||
* Rounded gauge
|
||||
*
|
||||
* @type {string}
|
||||
* @default round
|
||||
* @since 4.2.2
|
||||
* @product highcharts
|
||||
* @validvalue ["square", "round"]
|
||||
* @apioption plotOptions.solidgauge.linecap
|
||||
*/
|
||||
/**
|
||||
* Allow the gauge to overshoot the end of the perimeter axis by this
|
||||
* many degrees. Say if the gauge axis goes from 0 to 60, a value of
|
||||
* 100, or 1000, will show 5 degrees beyond the end of the axis when this
|
||||
* option is set to 5.
|
||||
*
|
||||
* @type {number}
|
||||
* @default 0
|
||||
* @since 3.0.10
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.solidgauge.overshoot
|
||||
*/
|
||||
/**
|
||||
* The outer radius for points in a solid gauge. Can be given as a number
|
||||
* (pixels) or percentage string.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/solidgauge-radius/
|
||||
* Individual radius and innerRadius
|
||||
*
|
||||
* @type {number|string}
|
||||
* @default 100
|
||||
* @since 4.1.6
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.solidgauge.radius
|
||||
*/
|
||||
/**
|
||||
* Wether to draw rounded edges on the gauge.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/gauge-activity/
|
||||
* Activity Gauge
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 5.0.8
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.solidgauge.rounded
|
||||
*/
|
||||
/**
|
||||
* The threshold or base level for the gauge.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/solidgauge-threshold/
|
||||
* Zero threshold with negative and positive values
|
||||
*
|
||||
* @type {number}
|
||||
* @since 5.0.3
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.solidgauge.threshold
|
||||
*/
|
||||
/**
|
||||
* Whether to give each point an individual color.
|
||||
*/
|
||||
colorByPoint: true,
|
||||
dataLabels: {
|
||||
y: 0
|
||||
}
|
||||
};
|
||||
// The solidgauge series type
|
||||
seriesType('solidgauge', 'gauge', solidGaugeOptions, {
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle,
|
||||
// Extend the translate function to extend the Y axis with the necessary
|
||||
// decoration (#5895).
|
||||
translate: function () {
|
||||
var axis = this.yAxis;
|
||||
SolidGaugeAxis.init(axis);
|
||||
// Prepare data classes
|
||||
if (!axis.dataClasses && axis.options.dataClasses) {
|
||||
axis.initDataClasses(axis.options);
|
||||
}
|
||||
axis.initStops(axis.options);
|
||||
// Generate points and inherit data label position
|
||||
H.seriesTypes.gauge.prototype.translate.call(this);
|
||||
},
|
||||
// Draw the points where each point is one needle.
|
||||
drawPoints: function () {
|
||||
var series = this, yAxis = series.yAxis, center = yAxis.center, options = series.options, renderer = series.chart.renderer, overshoot = options.overshoot, overshootVal = isNumber(overshoot) ?
|
||||
overshoot / 180 * Math.PI :
|
||||
0, thresholdAngleRad;
|
||||
// Handle the threshold option
|
||||
if (isNumber(options.threshold)) {
|
||||
thresholdAngleRad = yAxis.startAngleRad + yAxis.translate(options.threshold, null, null, null, true);
|
||||
}
|
||||
this.thresholdAngleRad = pick(thresholdAngleRad, yAxis.startAngleRad);
|
||||
series.points.forEach(function (point) {
|
||||
// #10630 null point should not be draw
|
||||
if (!point.isNull) { // condition like in pie chart
|
||||
var graphic = point.graphic, rotation = (yAxis.startAngleRad +
|
||||
yAxis.translate(point.y, null, null, null, true)), radius = ((pInt(pick(point.options.radius, options.radius, 100)) * center[2]) / 200), innerRadius = ((pInt(pick(point.options.innerRadius, options.innerRadius, 60)) * center[2]) / 200), shapeArgs, d, toColor = yAxis.toColor(point.y, point), axisMinAngle = Math.min(yAxis.startAngleRad, yAxis.endAngleRad), axisMaxAngle = Math.max(yAxis.startAngleRad, yAxis.endAngleRad), minAngle, maxAngle;
|
||||
if (toColor === 'none') { // #3708
|
||||
toColor = point.color || series.color || 'none';
|
||||
}
|
||||
if (toColor !== 'none') {
|
||||
point.color = toColor;
|
||||
}
|
||||
// Handle overshoot and clipping to axis max/min
|
||||
rotation = clamp(rotation, axisMinAngle - overshootVal, axisMaxAngle + overshootVal);
|
||||
// Handle the wrap option
|
||||
if (options.wrap === false) {
|
||||
rotation = clamp(rotation, axisMinAngle, axisMaxAngle);
|
||||
}
|
||||
minAngle = Math.min(rotation, series.thresholdAngleRad);
|
||||
maxAngle = Math.max(rotation, series.thresholdAngleRad);
|
||||
if (maxAngle - minAngle > 2 * Math.PI) {
|
||||
maxAngle = minAngle + 2 * Math.PI;
|
||||
}
|
||||
point.shapeArgs = shapeArgs = {
|
||||
x: center[0],
|
||||
y: center[1],
|
||||
r: radius,
|
||||
innerR: innerRadius,
|
||||
start: minAngle,
|
||||
end: maxAngle,
|
||||
rounded: options.rounded
|
||||
};
|
||||
point.startR = radius; // For PieSeries.animate
|
||||
if (graphic) {
|
||||
d = shapeArgs.d;
|
||||
graphic.animate(extend({ fill: toColor }, shapeArgs));
|
||||
if (d) {
|
||||
shapeArgs.d = d; // animate alters it
|
||||
}
|
||||
}
|
||||
else {
|
||||
point.graphic = graphic = renderer.arc(shapeArgs)
|
||||
.attr({
|
||||
fill: toColor,
|
||||
'sweep-flag': 0
|
||||
})
|
||||
.add(series.group);
|
||||
}
|
||||
if (!series.chart.styledMode) {
|
||||
if (options.linecap !== 'square') {
|
||||
graphic.attr({
|
||||
'stroke-linecap': 'round',
|
||||
'stroke-linejoin': 'round'
|
||||
});
|
||||
}
|
||||
graphic.attr({
|
||||
stroke: options.borderColor || 'none',
|
||||
'stroke-width': options.borderWidth || 0
|
||||
});
|
||||
}
|
||||
if (graphic) {
|
||||
graphic.addClass(point.getClassName(), true);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// Extend the pie slice animation by animating from start angle and up.
|
||||
animate: function (init) {
|
||||
if (!init) {
|
||||
this.startAngleRad = this.thresholdAngleRad;
|
||||
H.seriesTypes.pie.prototype.animate.call(this, init);
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `solidgauge` series. If the [type](#series.solidgauge.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
*
|
||||
* @extends series,plotOptions.solidgauge
|
||||
* @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
|
||||
* cropThreshold, dashStyle, dataParser, dataURL, dial,
|
||||
* findNearestPointBy, getExtremesFromAll, marker, negativeColor,
|
||||
* pointPlacement, pivot, shadow, softThreshold, stack, stacking,
|
||||
* states, step, threshold, turboThreshold, wrap, zoneAxis, zones,
|
||||
* dataSorting, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/solid-gauge
|
||||
* @apioption series.solidgauge
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `solidgauge` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 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.solidgauge.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 5,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 7,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* The typical gauge only contains a single data value.
|
||||
*
|
||||
* @sample {highcharts} highcharts/chart/reflow-true/
|
||||
* Numerical values
|
||||
* @sample {highcharts} highcharts/series/data-array-of-objects/
|
||||
* Config objects
|
||||
*
|
||||
* @type {Array<number|null|*>}
|
||||
* @extends series.gauge.data
|
||||
* @product highcharts
|
||||
* @apioption series.solidgauge.data
|
||||
*/
|
||||
/**
|
||||
* The inner radius of an individual point in a solid gauge. Can be given as a
|
||||
* number (pixels) or percentage string.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/solidgauge-radius/
|
||||
* Individual radius and innerRadius
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 4.1.6
|
||||
* @product highcharts
|
||||
* @apioption series.solidgauge.data.innerRadius
|
||||
*/
|
||||
/**
|
||||
* The outer radius of an individual point in a solid gauge. Can be
|
||||
* given as a number (pixels) or percentage string.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/solidgauge-radius/
|
||||
* Individual radius and innerRadius
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 4.1.6
|
||||
* @product highcharts
|
||||
* @apioption series.solidgauge.data.radius
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
export default SolidGaugeAxis;
|
||||
@@ -0,0 +1,246 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import U from '../Core/Utilities.js';
|
||||
var pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Series.js';
|
||||
/**
|
||||
* Spline series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.spline
|
||||
*
|
||||
* @augments Highcarts.Series
|
||||
*/
|
||||
seriesType('spline', 'line',
|
||||
/**
|
||||
* A spline series is a special type of line series, where the segments
|
||||
* between the data points are smoothed.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/spline-irregular-time/
|
||||
* Spline chart
|
||||
* @sample {highstock} stock/demo/spline/
|
||||
* Spline chart
|
||||
*
|
||||
* @extends plotOptions.series
|
||||
* @excluding step, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @optionparent plotOptions.spline
|
||||
*/
|
||||
{},
|
||||
/**
|
||||
* @lends seriesTypes.spline.prototype
|
||||
*/
|
||||
{
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Get the spline segment from a given point's previous neighbour to the
|
||||
* given point.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.spline#getPointSpline
|
||||
*
|
||||
* @param {Array<Highcharts.Point>}
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
*
|
||||
* @param {number} i
|
||||
*
|
||||
* @return {Highcharts.SVGPathArray}
|
||||
*/
|
||||
getPointSpline: function (points, point, i) {
|
||||
var
|
||||
// 1 means control points midway between points, 2 means 1/3
|
||||
// from the point, 3 is 1/4 etc
|
||||
smoothing = 1.5, denom = smoothing + 1, plotX = point.plotX || 0, plotY = point.plotY || 0, lastPoint = points[i - 1], nextPoint = points[i + 1], leftContX, leftContY, rightContX, rightContY, ret;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function doCurve(otherPoint) {
|
||||
return otherPoint &&
|
||||
!otherPoint.isNull &&
|
||||
otherPoint.doCurve !== false &&
|
||||
// #6387, area splines next to null:
|
||||
!point.isCliff;
|
||||
}
|
||||
// Find control points
|
||||
if (doCurve(lastPoint) && doCurve(nextPoint)) {
|
||||
var lastX = lastPoint.plotX || 0, lastY = lastPoint.plotY || 0, nextX = nextPoint.plotX || 0, nextY = nextPoint.plotY || 0, correction = 0;
|
||||
leftContX = (smoothing * plotX + lastX) / denom;
|
||||
leftContY = (smoothing * plotY + lastY) / denom;
|
||||
rightContX = (smoothing * plotX + nextX) / denom;
|
||||
rightContY = (smoothing * plotY + nextY) / denom;
|
||||
// Have the two control points make a straight line through main
|
||||
// point
|
||||
if (rightContX !== leftContX) { // #5016, division by zero
|
||||
correction = (((rightContY - leftContY) *
|
||||
(rightContX - plotX)) /
|
||||
(rightContX - leftContX) + plotY - rightContY);
|
||||
}
|
||||
leftContY += correction;
|
||||
rightContY += correction;
|
||||
// to prevent false extremes, check that control points are
|
||||
// between neighbouring points' y values
|
||||
if (leftContY > lastY && leftContY > plotY) {
|
||||
leftContY = Math.max(lastY, plotY);
|
||||
// mirror of left control point
|
||||
rightContY = 2 * plotY - leftContY;
|
||||
}
|
||||
else if (leftContY < lastY && leftContY < plotY) {
|
||||
leftContY = Math.min(lastY, plotY);
|
||||
rightContY = 2 * plotY - leftContY;
|
||||
}
|
||||
if (rightContY > nextY && rightContY > plotY) {
|
||||
rightContY = Math.max(nextY, plotY);
|
||||
leftContY = 2 * plotY - rightContY;
|
||||
}
|
||||
else if (rightContY < nextY && rightContY < plotY) {
|
||||
rightContY = Math.min(nextY, plotY);
|
||||
leftContY = 2 * plotY - rightContY;
|
||||
}
|
||||
// record for drawing in next point
|
||||
point.rightContX = rightContX;
|
||||
point.rightContY = rightContY;
|
||||
}
|
||||
// Visualize control points for debugging
|
||||
/*
|
||||
if (leftContX) {
|
||||
this.chart.renderer.circle(
|
||||
leftContX + this.chart.plotLeft,
|
||||
leftContY + this.chart.plotTop,
|
||||
2
|
||||
)
|
||||
.attr({
|
||||
stroke: 'red',
|
||||
'stroke-width': 2,
|
||||
fill: 'none',
|
||||
zIndex: 9
|
||||
})
|
||||
.add();
|
||||
this.chart.renderer.path(['M', leftContX + this.chart.plotLeft,
|
||||
leftContY + this.chart.plotTop,
|
||||
'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
|
||||
.attr({
|
||||
stroke: 'red',
|
||||
'stroke-width': 2,
|
||||
zIndex: 9
|
||||
})
|
||||
.add();
|
||||
}
|
||||
if (rightContX) {
|
||||
this.chart.renderer.circle(
|
||||
rightContX + this.chart.plotLeft,
|
||||
rightContY + this.chart.plotTop,
|
||||
2
|
||||
)
|
||||
.attr({
|
||||
stroke: 'green',
|
||||
'stroke-width': 2,
|
||||
fill: 'none',
|
||||
zIndex: 9
|
||||
})
|
||||
.add();
|
||||
this.chart.renderer.path(['M', rightContX + this.chart.plotLeft,
|
||||
rightContY + this.chart.plotTop,
|
||||
'L', plotX + this.chart.plotLeft, plotY + this.chart.plotTop])
|
||||
.attr({
|
||||
stroke: 'green',
|
||||
'stroke-width': 2,
|
||||
zIndex: 9
|
||||
})
|
||||
.add();
|
||||
}
|
||||
// */
|
||||
ret = [
|
||||
'C',
|
||||
pick(lastPoint.rightContX, lastPoint.plotX, 0),
|
||||
pick(lastPoint.rightContY, lastPoint.plotY, 0),
|
||||
pick(leftContX, plotX, 0),
|
||||
pick(leftContY, plotY, 0),
|
||||
plotX,
|
||||
plotY
|
||||
];
|
||||
// reset for updating series later
|
||||
lastPoint.rightContX = lastPoint.rightContY = void 0;
|
||||
return ret;
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* A `spline` series. If the [type](#series.spline.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.spline
|
||||
* @excluding dataParser, dataURL, step, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @apioption series.spline
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `spline` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 9],
|
||||
* [1, 2],
|
||||
* [2, 8]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.spline.turboThreshold),
|
||||
* this option is not available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 9,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 0,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.spline.data
|
||||
*/
|
||||
''; // adds doclets above intro transpilat
|
||||
@@ -0,0 +1,127 @@
|
||||
/* *
|
||||
*
|
||||
* Streamgraph module
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import '../Series/AreaSeries.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var seriesType = U.seriesType;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.streamgraph
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('streamgraph', 'areaspline'
|
||||
/**
|
||||
* A streamgraph is a type of stacked area graph which is displaced around a
|
||||
* central axis, resulting in a flowing, organic shape.
|
||||
*
|
||||
* @sample {highcharts|highstock} highcharts/demo/streamgraph/
|
||||
* Streamgraph
|
||||
*
|
||||
* @extends plotOptions.areaspline
|
||||
* @since 6.0.0
|
||||
* @product highcharts highstock
|
||||
* @requires modules/streamgraph
|
||||
* @optionparent plotOptions.streamgraph
|
||||
*/
|
||||
, {
|
||||
fillOpacity: 1,
|
||||
lineWidth: 0,
|
||||
marker: {
|
||||
enabled: false
|
||||
},
|
||||
stacking: 'stream'
|
||||
// Prototype functions
|
||||
}, {
|
||||
negStacks: false,
|
||||
// Modifier function for stream stacks. It simply moves the point up or
|
||||
// down in order to center the full stack vertically.
|
||||
streamStacker: function (pointExtremes, stack, i) {
|
||||
// Y bottom value
|
||||
pointExtremes[0] -= stack.total / 2;
|
||||
// Y value
|
||||
pointExtremes[1] -= stack.total / 2;
|
||||
// Record the Y data for use when getting axis extremes
|
||||
this.stackedYData[i] = pointExtremes;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `streamgraph` series. If the [type](#series.streamgraph.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.streamgraph
|
||||
* @excluding dataParser, dataURL, step, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires modules/streamgraph
|
||||
* @apioption series.streamgraph
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `streamgraph` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 9],
|
||||
* [1, 7],
|
||||
* [2, 6]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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: 1,
|
||||
* y: 9,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 6,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.streamgraph.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,958 @@
|
||||
/* *
|
||||
*
|
||||
* This module implements sunburst charts in Highcharts.
|
||||
*
|
||||
* (c) 2016-2020 Highsoft AS
|
||||
*
|
||||
* Authors: Jon Arild Nygard
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var correctFloat = U.correctFloat, error = U.error, extend = U.extend, isNumber = U.isNumber, isObject = U.isObject, isString = U.isString, merge = U.merge, seriesType = U.seriesType, splat = U.splat;
|
||||
import centeredSeriesMixin from '../Mixins/CenteredSeries.js';
|
||||
import drawPointModule from '../Mixins/DrawPoint.js';
|
||||
var drawPoint = drawPointModule.drawPoint;
|
||||
import mixinTreeSeries from '../Mixins/TreeSeries.js';
|
||||
var getColor = mixinTreeSeries.getColor, getLevelOptions = mixinTreeSeries.getLevelOptions, setTreeValues = mixinTreeSeries.setTreeValues, updateRootId = mixinTreeSeries.updateRootId;
|
||||
import '../Core/Series/Series.js';
|
||||
import './TreemapSeries.js';
|
||||
var Series = H.Series, getCenter = centeredSeriesMixin.getCenter, getStartAndEndRadians = centeredSeriesMixin.getStartAndEndRadians, isBoolean = function (x) {
|
||||
return typeof x === 'boolean';
|
||||
}, noop = H.noop, rad2deg = 180 / Math.PI, seriesTypes = H.seriesTypes;
|
||||
// TODO introduce step, which should default to 1.
|
||||
var range = function range(from, to) {
|
||||
var result = [], i;
|
||||
if (isNumber(from) && isNumber(to) && from <= to) {
|
||||
for (i = from; i <= to; i++) {
|
||||
result.push(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @function calculateLevelSizes
|
||||
*
|
||||
* @param {object} levelOptions
|
||||
* Map of level to its options.
|
||||
*
|
||||
* @param {Highcharts.Dictionary<number>} params
|
||||
* Object containing number parameters `innerRadius` and `outerRadius`.
|
||||
*
|
||||
* @return {Highcharts.SunburstSeriesLevelsOptions|undefined}
|
||||
* Returns the modified options, or undefined.
|
||||
*/
|
||||
var calculateLevelSizes = function calculateLevelSizes(levelOptions, params) {
|
||||
var result, p = isObject(params) ? params : {}, totalWeight = 0, diffRadius, levels, levelsNotIncluded, remainingSize, from, to;
|
||||
if (isObject(levelOptions)) {
|
||||
result = merge({}, levelOptions);
|
||||
from = isNumber(p.from) ? p.from : 0;
|
||||
to = isNumber(p.to) ? p.to : 0;
|
||||
levels = range(from, to);
|
||||
levelsNotIncluded = Object.keys(result).filter(function (k) {
|
||||
return levels.indexOf(+k) === -1;
|
||||
});
|
||||
diffRadius = remainingSize = isNumber(p.diffRadius) ? p.diffRadius : 0;
|
||||
// Convert percentage to pixels.
|
||||
// Calculate the remaining size to divide between "weight" levels.
|
||||
// Calculate total weight to use in convertion from weight to pixels.
|
||||
levels.forEach(function (level) {
|
||||
var options = result[level], unit = options.levelSize.unit, value = options.levelSize.value;
|
||||
if (unit === 'weight') {
|
||||
totalWeight += value;
|
||||
}
|
||||
else if (unit === 'percentage') {
|
||||
options.levelSize = {
|
||||
unit: 'pixels',
|
||||
value: (value / 100) * diffRadius
|
||||
};
|
||||
remainingSize -= options.levelSize.value;
|
||||
}
|
||||
else if (unit === 'pixels') {
|
||||
remainingSize -= value;
|
||||
}
|
||||
});
|
||||
// Convert weight to pixels.
|
||||
levels.forEach(function (level) {
|
||||
var options = result[level], weight;
|
||||
if (options.levelSize.unit === 'weight') {
|
||||
weight = options.levelSize.value;
|
||||
result[level].levelSize = {
|
||||
unit: 'pixels',
|
||||
value: (weight / totalWeight) * remainingSize
|
||||
};
|
||||
}
|
||||
});
|
||||
// Set all levels not included in interval [from,to] to have 0 pixels.
|
||||
levelsNotIncluded.forEach(function (level) {
|
||||
result[level].levelSize = {
|
||||
value: 0,
|
||||
unit: 'pixels'
|
||||
};
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
/**
|
||||
* Find a set of coordinates given a start coordinates, an angle, and a
|
||||
* distance.
|
||||
*
|
||||
* @private
|
||||
* @function getEndPoint
|
||||
*
|
||||
* @param {number} x
|
||||
* Start coordinate x
|
||||
*
|
||||
* @param {number} y
|
||||
* Start coordinate y
|
||||
*
|
||||
* @param {number} angle
|
||||
* Angle in radians
|
||||
*
|
||||
* @param {number} distance
|
||||
* Distance from start to end coordinates
|
||||
*
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
* Returns the end coordinates, x and y.
|
||||
*/
|
||||
var getEndPoint = function getEndPoint(x, y, angle, distance) {
|
||||
return {
|
||||
x: x + (Math.cos(angle) * distance),
|
||||
y: y + (Math.sin(angle) * distance)
|
||||
};
|
||||
};
|
||||
var layoutAlgorithm = function layoutAlgorithm(parent, children, options) {
|
||||
var startAngle = parent.start, range = parent.end - startAngle, total = parent.val, x = parent.x, y = parent.y, radius = ((options &&
|
||||
isObject(options.levelSize) &&
|
||||
isNumber(options.levelSize.value)) ?
|
||||
options.levelSize.value :
|
||||
0), innerRadius = parent.r, outerRadius = innerRadius + radius, slicedOffset = options && isNumber(options.slicedOffset) ?
|
||||
options.slicedOffset :
|
||||
0;
|
||||
return (children || []).reduce(function (arr, child) {
|
||||
var percentage = (1 / total) * child.val, radians = percentage * range, radiansCenter = startAngle + (radians / 2), offsetPosition = getEndPoint(x, y, radiansCenter, slicedOffset), values = {
|
||||
x: child.sliced ? offsetPosition.x : x,
|
||||
y: child.sliced ? offsetPosition.y : y,
|
||||
innerR: innerRadius,
|
||||
r: outerRadius,
|
||||
radius: radius,
|
||||
start: startAngle,
|
||||
end: startAngle + radians
|
||||
};
|
||||
arr.push(values);
|
||||
startAngle = values.end;
|
||||
return arr;
|
||||
}, []);
|
||||
};
|
||||
var getDlOptions = function getDlOptions(params) {
|
||||
// Set options to new object to avoid problems with scope
|
||||
var point = params.point, shape = isObject(params.shapeArgs) ? params.shapeArgs : {}, optionsPoint = (isObject(params.optionsPoint) ?
|
||||
params.optionsPoint.dataLabels :
|
||||
{}),
|
||||
// The splat was used because levels dataLabels
|
||||
// options doesn't work as an array
|
||||
optionsLevel = splat(isObject(params.level) ?
|
||||
params.level.dataLabels :
|
||||
{})[0], options = merge({
|
||||
style: {}
|
||||
}, optionsLevel, optionsPoint), rotationRad, rotation, rotationMode = options.rotationMode;
|
||||
if (!isNumber(options.rotation)) {
|
||||
if (rotationMode === 'auto' || rotationMode === 'circular') {
|
||||
if (point.innerArcLength < 1 &&
|
||||
point.outerArcLength > shape.radius) {
|
||||
rotationRad = 0;
|
||||
// Triger setTextPath function to get textOutline etc.
|
||||
if (point.dataLabelPath && rotationMode === 'circular') {
|
||||
options.textPath = {
|
||||
enabled: true
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (point.innerArcLength > 1 &&
|
||||
point.outerArcLength > 1.5 * shape.radius) {
|
||||
if (rotationMode === 'circular') {
|
||||
options.textPath = {
|
||||
enabled: true,
|
||||
attributes: {
|
||||
dy: 5
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
rotationMode = 'parallel';
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Trigger the destroyTextPath function
|
||||
if (point.dataLabel &&
|
||||
point.dataLabel.textPathWrapper &&
|
||||
rotationMode === 'circular') {
|
||||
options.textPath = {
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
rotationMode = 'perpendicular';
|
||||
}
|
||||
}
|
||||
if (rotationMode !== 'auto' && rotationMode !== 'circular') {
|
||||
rotationRad = (shape.end -
|
||||
(shape.end - shape.start) / 2);
|
||||
}
|
||||
if (rotationMode === 'parallel') {
|
||||
options.style.width = Math.min(shape.radius * 2.5, (point.outerArcLength + point.innerArcLength) / 2);
|
||||
}
|
||||
else {
|
||||
options.style.width = shape.radius;
|
||||
}
|
||||
if (rotationMode === 'perpendicular' &&
|
||||
point.series.chart.renderer.fontMetrics(options.style.fontSize).h > point.outerArcLength) {
|
||||
options.style.width = 1;
|
||||
}
|
||||
// Apply padding (#8515)
|
||||
options.style.width = Math.max(options.style.width - 2 * (options.padding || 0), 1);
|
||||
rotation = (rotationRad * rad2deg) % 180;
|
||||
if (rotationMode === 'parallel') {
|
||||
rotation -= 90;
|
||||
}
|
||||
// Prevent text from rotating upside down
|
||||
if (rotation > 90) {
|
||||
rotation -= 180;
|
||||
}
|
||||
else if (rotation < -90) {
|
||||
rotation += 180;
|
||||
}
|
||||
options.rotation = rotation;
|
||||
}
|
||||
if (options.textPath) {
|
||||
if (point.shapeExisting.innerR === 0 &&
|
||||
options.textPath.enabled) {
|
||||
// Enable rotation to render text
|
||||
options.rotation = 0;
|
||||
// Center dataLabel - disable textPath
|
||||
options.textPath.enabled = false;
|
||||
// Setting width and padding
|
||||
options.style.width = Math.max((point.shapeExisting.r * 2) -
|
||||
2 * (options.padding || 0), 1);
|
||||
}
|
||||
else if (point.dlOptions &&
|
||||
point.dlOptions.textPath &&
|
||||
!point.dlOptions.textPath.enabled &&
|
||||
(rotationMode === 'circular')) {
|
||||
// Bring dataLabel back if was a center dataLabel
|
||||
options.textPath.enabled = true;
|
||||
}
|
||||
if (options.textPath.enabled) {
|
||||
// Enable rotation to render text
|
||||
options.rotation = 0;
|
||||
// Setting width and padding
|
||||
options.style.width = Math.max((point.outerArcLength +
|
||||
point.innerArcLength) / 2 -
|
||||
2 * (options.padding || 0), 1);
|
||||
}
|
||||
}
|
||||
// NOTE: alignDataLabel positions the data label differntly when rotation is
|
||||
// 0. Avoiding this by setting rotation to a small number.
|
||||
if (options.rotation === 0) {
|
||||
options.rotation = 0.001;
|
||||
}
|
||||
return options;
|
||||
};
|
||||
var getAnimation = function getAnimation(shape, params) {
|
||||
var point = params.point, radians = params.radians, innerR = params.innerR, idRoot = params.idRoot, idPreviousRoot = params.idPreviousRoot, shapeExisting = params.shapeExisting, shapeRoot = params.shapeRoot, shapePreviousRoot = params.shapePreviousRoot, visible = params.visible, from = {}, to = {
|
||||
end: shape.end,
|
||||
start: shape.start,
|
||||
innerR: shape.innerR,
|
||||
r: shape.r,
|
||||
x: shape.x,
|
||||
y: shape.y
|
||||
};
|
||||
if (visible) {
|
||||
// Animate points in
|
||||
if (!point.graphic && shapePreviousRoot) {
|
||||
if (idRoot === point.id) {
|
||||
from = {
|
||||
start: radians.start,
|
||||
end: radians.end
|
||||
};
|
||||
}
|
||||
else {
|
||||
from = (shapePreviousRoot.end <= shape.start) ? {
|
||||
start: radians.end,
|
||||
end: radians.end
|
||||
} : {
|
||||
start: radians.start,
|
||||
end: radians.start
|
||||
};
|
||||
}
|
||||
// Animate from center and outwards.
|
||||
from.innerR = from.r = innerR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Animate points out
|
||||
if (point.graphic) {
|
||||
if (idPreviousRoot === point.id) {
|
||||
to = {
|
||||
innerR: innerR,
|
||||
r: innerR
|
||||
};
|
||||
}
|
||||
else if (shapeRoot) {
|
||||
to = (shapeRoot.end <= shapeExisting.start) ?
|
||||
{
|
||||
innerR: innerR,
|
||||
r: innerR,
|
||||
start: radians.end,
|
||||
end: radians.end
|
||||
} : {
|
||||
innerR: innerR,
|
||||
r: innerR,
|
||||
start: radians.start,
|
||||
end: radians.start
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
from: from,
|
||||
to: to
|
||||
};
|
||||
};
|
||||
var getDrillId = function getDrillId(point, idRoot, mapIdToNode) {
|
||||
var drillId, node = point.node, nodeRoot;
|
||||
if (!node.isLeaf) {
|
||||
// When it is the root node, the drillId should be set to parent.
|
||||
if (idRoot === point.id) {
|
||||
nodeRoot = mapIdToNode[idRoot];
|
||||
drillId = nodeRoot.parent;
|
||||
}
|
||||
else {
|
||||
drillId = point.id;
|
||||
}
|
||||
}
|
||||
return drillId;
|
||||
};
|
||||
var getLevelFromAndTo = function getLevelFromAndTo(_a) {
|
||||
var level = _a.level, height = _a.height;
|
||||
// Never displays level below 1
|
||||
var from = level > 0 ? level : 1;
|
||||
var to = level + height;
|
||||
return { from: from, to: to };
|
||||
};
|
||||
var cbSetTreeValuesBefore = function before(node, options) {
|
||||
var mapIdToNode = options.mapIdToNode, nodeParent = mapIdToNode[node.parent], series = options.series, chart = series.chart, points = series.points, point = points[node.i], colors = (series.options.colors || chart && chart.options.colors), colorInfo = getColor(node, {
|
||||
colors: colors,
|
||||
colorIndex: series.colorIndex,
|
||||
index: options.index,
|
||||
mapOptionsToLevel: options.mapOptionsToLevel,
|
||||
parentColor: nodeParent && nodeParent.color,
|
||||
parentColorIndex: nodeParent && nodeParent.colorIndex,
|
||||
series: options.series,
|
||||
siblings: options.siblings
|
||||
});
|
||||
node.color = colorInfo.color;
|
||||
node.colorIndex = colorInfo.colorIndex;
|
||||
if (point) {
|
||||
point.color = node.color;
|
||||
point.colorIndex = node.colorIndex;
|
||||
// Set slicing on node, but avoid slicing the top node.
|
||||
node.sliced = (node.id !== options.idRoot) ? point.sliced : false;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
/**
|
||||
* A Sunburst displays hierarchical data, where a level in the hierarchy is
|
||||
* represented by a circle. The center represents the root node of the tree.
|
||||
* The visualization bears a resemblance to both treemap and pie charts.
|
||||
*
|
||||
* @sample highcharts/demo/sunburst
|
||||
* Sunburst chart
|
||||
*
|
||||
* @extends plotOptions.pie
|
||||
* @excluding allAreas, clip, colorAxis, colorKey, compare, compareBase,
|
||||
* dataGrouping, depth, dragDrop, endAngle, gapSize, gapUnit,
|
||||
* ignoreHiddenPoint, innerSize, joinBy, legendType, linecap,
|
||||
* minSize, navigatorOptions, pointRange
|
||||
* @product highcharts
|
||||
* @requires modules/sunburst.js
|
||||
* @optionparent plotOptions.sunburst
|
||||
* @private
|
||||
*/
|
||||
var sunburstOptions = {
|
||||
/**
|
||||
* Set options on specific levels. Takes precedence over series options,
|
||||
* but not point options.
|
||||
*
|
||||
* @sample highcharts/demo/sunburst
|
||||
* Sunburst chart
|
||||
*
|
||||
* @type {Array<*>}
|
||||
* @apioption plotOptions.sunburst.levels
|
||||
*/
|
||||
/**
|
||||
* Can set a `borderColor` on all points which lies on the same level.
|
||||
*
|
||||
* @type {Highcharts.ColorString}
|
||||
* @apioption plotOptions.sunburst.levels.borderColor
|
||||
*/
|
||||
/**
|
||||
* Can set a `borderWidth` on all points which lies on the same level.
|
||||
*
|
||||
* @type {number}
|
||||
* @apioption plotOptions.sunburst.levels.borderWidth
|
||||
*/
|
||||
/**
|
||||
* Can set a `borderDashStyle` on all points which lies on the same level.
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @apioption plotOptions.sunburst.levels.borderDashStyle
|
||||
*/
|
||||
/**
|
||||
* Can set a `color` on all points which lies on the same level.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @apioption plotOptions.sunburst.levels.color
|
||||
*/
|
||||
/**
|
||||
* Can set a `colorVariation` on all points which lies on the same level.
|
||||
*
|
||||
* @apioption plotOptions.sunburst.levels.colorVariation
|
||||
*/
|
||||
/**
|
||||
* The key of a color variation. Currently supports `brightness` only.
|
||||
*
|
||||
* @type {string}
|
||||
* @apioption plotOptions.sunburst.levels.colorVariation.key
|
||||
*/
|
||||
/**
|
||||
* The ending value of a color variation. The last sibling will receive this
|
||||
* value.
|
||||
*
|
||||
* @type {number}
|
||||
* @apioption plotOptions.sunburst.levels.colorVariation.to
|
||||
*/
|
||||
/**
|
||||
* Can set `dataLabels` on all points which lies on the same level.
|
||||
*
|
||||
* @extends plotOptions.sunburst.dataLabels
|
||||
* @apioption plotOptions.sunburst.levels.dataLabels
|
||||
*/
|
||||
/**
|
||||
* Can set a `levelSize` on all points which lies on the same level.
|
||||
*
|
||||
* @type {object}
|
||||
* @apioption plotOptions.sunburst.levels.levelSize
|
||||
*/
|
||||
/**
|
||||
* Can set a `rotation` on all points which lies on the same level.
|
||||
*
|
||||
* @type {number}
|
||||
* @apioption plotOptions.sunburst.levels.rotation
|
||||
*/
|
||||
/**
|
||||
* Can set a `rotationMode` on all points which lies on the same level.
|
||||
*
|
||||
* @type {string}
|
||||
* @apioption plotOptions.sunburst.levels.rotationMode
|
||||
*/
|
||||
/**
|
||||
* When enabled the user can click on a point which is a parent and
|
||||
* zoom in on its children. Deprecated and replaced by
|
||||
* [allowTraversingTree](#plotOptions.sunburst.allowTraversingTree).
|
||||
*
|
||||
* @deprecated
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.sunburst.allowDrillToNode
|
||||
*/
|
||||
/**
|
||||
* When enabled the user can click on a point which is a parent and
|
||||
* zoom in on its children.
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 7.0.3
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.sunburst.allowTraversingTree
|
||||
*/
|
||||
/**
|
||||
* The center of the sunburst chart relative to the plot area. Can be
|
||||
* percentages or pixel values.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/pie-center/
|
||||
* Centered at 100, 100
|
||||
*
|
||||
* @type {Array<number|string>}
|
||||
* @default ["50%", "50%"]
|
||||
* @product highcharts
|
||||
*/
|
||||
center: ['50%', '50%'],
|
||||
colorByPoint: false,
|
||||
/**
|
||||
* Disable inherited opacity from Treemap series.
|
||||
*
|
||||
* @ignore-option
|
||||
*/
|
||||
opacity: 1,
|
||||
/**
|
||||
* @declare Highcharts.SeriesSunburstDataLabelsOptionsObject
|
||||
*/
|
||||
dataLabels: {
|
||||
allowOverlap: true,
|
||||
defer: true,
|
||||
/**
|
||||
* Decides how the data label will be rotated relative to the perimeter
|
||||
* of the sunburst. Valid values are `auto`, `circular`, `parallel` and
|
||||
* `perpendicular`. When `auto`, the best fit will be
|
||||
* computed for the point. The `circular` option works similiar
|
||||
* to `auto`, but uses the `textPath` feature - labels are curved,
|
||||
* resulting in a better layout, however multiple lines and
|
||||
* `textOutline` are not supported.
|
||||
*
|
||||
* The `series.rotation` option takes precedence over `rotationMode`.
|
||||
*
|
||||
* @type {string}
|
||||
* @sample {highcharts} highcharts/plotoptions/sunburst-datalabels-rotationmode-circular/
|
||||
* Circular rotation mode
|
||||
* @validvalue ["auto", "perpendicular", "parallel", "circular"]
|
||||
* @since 6.0.0
|
||||
*/
|
||||
rotationMode: 'auto',
|
||||
style: {
|
||||
/** @internal */
|
||||
textOverflow: 'ellipsis'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Which point to use as a root in the visualization.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
rootId: void 0,
|
||||
/**
|
||||
* Used together with the levels and `allowDrillToNode` options. When
|
||||
* set to false the first level visible when drilling is considered
|
||||
* to be level one. Otherwise the level will be the same as the tree
|
||||
* structure.
|
||||
*/
|
||||
levelIsConstant: true,
|
||||
/**
|
||||
* Determines the width of the ring per level.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/sunburst-levelsize/
|
||||
* Sunburst with various sizes per level
|
||||
*
|
||||
* @since 6.0.5
|
||||
*/
|
||||
levelSize: {
|
||||
/**
|
||||
* The value used for calculating the width of the ring. Its' affect is
|
||||
* determined by `levelSize.unit`.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/sunburst-levelsize/
|
||||
* Sunburst with various sizes per level
|
||||
*/
|
||||
value: 1,
|
||||
/**
|
||||
* How to interpret `levelSize.value`.
|
||||
*
|
||||
* - `percentage` gives a width relative to result of outer radius minus
|
||||
* inner radius.
|
||||
*
|
||||
* - `pixels` gives the ring a fixed width in pixels.
|
||||
*
|
||||
* - `weight` takes the remaining width after percentage and pixels, and
|
||||
* distributes it accross all "weighted" levels. The value relative to
|
||||
* the sum of all weights determines the width.
|
||||
*
|
||||
* @sample {highcharts} highcharts/plotoptions/sunburst-levelsize/
|
||||
* Sunburst with various sizes per level
|
||||
*
|
||||
* @validvalue ["percentage", "pixels", "weight"]
|
||||
*/
|
||||
unit: 'weight'
|
||||
},
|
||||
/**
|
||||
* Options for the button appearing when traversing down in a treemap.
|
||||
*
|
||||
* @extends plotOptions.treemap.traverseUpButton
|
||||
* @since 6.0.0
|
||||
* @apioption plotOptions.sunburst.traverseUpButton
|
||||
*/
|
||||
/**
|
||||
* If a point is sliced, moved out from the center, how many pixels
|
||||
* should it be moved?.
|
||||
*
|
||||
* @sample highcharts/plotoptions/sunburst-sliced
|
||||
* Sliced sunburst
|
||||
*
|
||||
* @since 6.0.4
|
||||
*/
|
||||
slicedOffset: 10
|
||||
};
|
||||
// Properties of the Sunburst series.
|
||||
var sunburstSeries = {
|
||||
drawDataLabels: noop,
|
||||
drawPoints: function drawPoints() {
|
||||
var series = this, mapOptionsToLevel = series.mapOptionsToLevel, shapeRoot = series.shapeRoot, group = series.group, hasRendered = series.hasRendered, idRoot = series.rootNode, idPreviousRoot = series.idPreviousRoot, nodeMap = series.nodeMap, nodePreviousRoot = nodeMap[idPreviousRoot], shapePreviousRoot = nodePreviousRoot && nodePreviousRoot.shapeArgs, points = series.points, radians = series.startAndEndRadians, chart = series.chart, optionsChart = chart && chart.options && chart.options.chart || {}, animation = (isBoolean(optionsChart.animation) ?
|
||||
optionsChart.animation :
|
||||
true), positions = series.center, center = {
|
||||
x: positions[0],
|
||||
y: positions[1]
|
||||
}, innerR = positions[3] / 2, renderer = series.chart.renderer, animateLabels, animateLabelsCalled = false, addedHack = false, hackDataLabelAnimation = !!(animation &&
|
||||
hasRendered &&
|
||||
idRoot !== idPreviousRoot &&
|
||||
series.dataLabelsGroup);
|
||||
if (hackDataLabelAnimation) {
|
||||
series.dataLabelsGroup.attr({ opacity: 0 });
|
||||
animateLabels = function () {
|
||||
var s = series;
|
||||
animateLabelsCalled = true;
|
||||
if (s.dataLabelsGroup) {
|
||||
s.dataLabelsGroup.animate({
|
||||
opacity: 1,
|
||||
visibility: 'visible'
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
points.forEach(function (point) {
|
||||
var node = point.node, level = mapOptionsToLevel[node.level], shapeExisting = point.shapeExisting || {}, shape = node.shapeArgs || {}, animationInfo, onComplete, visible = !!(node.visible && node.shapeArgs);
|
||||
if (hasRendered && animation) {
|
||||
animationInfo = getAnimation(shape, {
|
||||
center: center,
|
||||
point: point,
|
||||
radians: radians,
|
||||
innerR: innerR,
|
||||
idRoot: idRoot,
|
||||
idPreviousRoot: idPreviousRoot,
|
||||
shapeExisting: shapeExisting,
|
||||
shapeRoot: shapeRoot,
|
||||
shapePreviousRoot: shapePreviousRoot,
|
||||
visible: visible
|
||||
});
|
||||
}
|
||||
else {
|
||||
// When animation is disabled, attr is called from animation.
|
||||
animationInfo = {
|
||||
to: shape,
|
||||
from: {}
|
||||
};
|
||||
}
|
||||
extend(point, {
|
||||
shapeExisting: shape,
|
||||
tooltipPos: [shape.plotX, shape.plotY],
|
||||
drillId: getDrillId(point, idRoot, nodeMap),
|
||||
name: '' + (point.name || point.id || point.index),
|
||||
plotX: shape.plotX,
|
||||
plotY: shape.plotY,
|
||||
value: node.val,
|
||||
isNull: !visible // used for dataLabels & point.draw
|
||||
});
|
||||
point.dlOptions = getDlOptions({
|
||||
point: point,
|
||||
level: level,
|
||||
optionsPoint: point.options,
|
||||
shapeArgs: shape
|
||||
});
|
||||
if (!addedHack && visible) {
|
||||
addedHack = true;
|
||||
onComplete = animateLabels;
|
||||
}
|
||||
point.draw({
|
||||
animatableAttribs: animationInfo.to,
|
||||
attribs: extend(animationInfo.from, (!chart.styledMode && series.pointAttribs(point, (point.selected && 'select')))),
|
||||
onComplete: onComplete,
|
||||
group: group,
|
||||
renderer: renderer,
|
||||
shapeType: 'arc',
|
||||
shapeArgs: shape
|
||||
});
|
||||
});
|
||||
// Draw data labels after points
|
||||
// TODO draw labels one by one to avoid addtional looping
|
||||
if (hackDataLabelAnimation && addedHack) {
|
||||
series.hasRendered = false;
|
||||
series.options.dataLabels.defer = true;
|
||||
Series.prototype.drawDataLabels.call(series);
|
||||
series.hasRendered = true;
|
||||
// If animateLabels is called before labels were hidden, then call
|
||||
// it again.
|
||||
if (animateLabelsCalled) {
|
||||
animateLabels();
|
||||
}
|
||||
}
|
||||
else {
|
||||
Series.prototype.drawDataLabels.call(series);
|
||||
}
|
||||
},
|
||||
pointAttribs: seriesTypes.column.prototype.pointAttribs,
|
||||
// The layout algorithm for the levels
|
||||
layoutAlgorithm: layoutAlgorithm,
|
||||
// Set the shape arguments on the nodes. Recursive from root down.
|
||||
setShapeArgs: function (parent, parentValues, mapOptionsToLevel) {
|
||||
var childrenValues = [], level = parent.level + 1, options = mapOptionsToLevel[level],
|
||||
// Collect all children which should be included
|
||||
children = parent.children.filter(function (n) {
|
||||
return n.visible;
|
||||
}), twoPi = 6.28; // Two times Pi.
|
||||
childrenValues = this.layoutAlgorithm(parentValues, children, options);
|
||||
children.forEach(function (child, index) {
|
||||
var values = childrenValues[index], angle = values.start + ((values.end - values.start) / 2), radius = values.innerR + ((values.r - values.innerR) / 2), radians = (values.end - values.start), isCircle = (values.innerR === 0 && radians > twoPi), center = (isCircle ?
|
||||
{ x: values.x, y: values.y } :
|
||||
getEndPoint(values.x, values.y, angle, radius)), val = (child.val ?
|
||||
(child.childrenTotal > child.val ?
|
||||
child.childrenTotal :
|
||||
child.val) :
|
||||
child.childrenTotal);
|
||||
// The inner arc length is a convenience for data label filters.
|
||||
if (this.points[child.i]) {
|
||||
this.points[child.i].innerArcLength = radians * values.innerR;
|
||||
this.points[child.i].outerArcLength = radians * values.r;
|
||||
}
|
||||
child.shapeArgs = merge(values, {
|
||||
plotX: center.x,
|
||||
plotY: center.y + 4 * Math.abs(Math.cos(angle))
|
||||
});
|
||||
child.values = merge(values, {
|
||||
val: val
|
||||
});
|
||||
// If node has children, then call method recursively
|
||||
if (child.children.length) {
|
||||
this.setShapeArgs(child, child.values, mapOptionsToLevel);
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
translate: function translate() {
|
||||
var series = this, options = series.options, positions = series.center = getCenter.call(series), radians = series.startAndEndRadians = getStartAndEndRadians(options.startAngle, options.endAngle), innerRadius = positions[3] / 2, outerRadius = positions[2] / 2, diffRadius = outerRadius - innerRadius,
|
||||
// NOTE: updateRootId modifies series.
|
||||
rootId = updateRootId(series), mapIdToNode = series.nodeMap, mapOptionsToLevel, idTop, nodeRoot = mapIdToNode && mapIdToNode[rootId], nodeTop, tree, values, nodeIds = {};
|
||||
series.shapeRoot = nodeRoot && nodeRoot.shapeArgs;
|
||||
// Call prototype function
|
||||
Series.prototype.translate.call(series);
|
||||
// @todo Only if series.isDirtyData is true
|
||||
tree = series.tree = series.getTree();
|
||||
// Render traverseUpButton, after series.nodeMap i calculated.
|
||||
series.renderTraverseUpButton(rootId);
|
||||
mapIdToNode = series.nodeMap;
|
||||
nodeRoot = mapIdToNode[rootId];
|
||||
idTop = isString(nodeRoot.parent) ? nodeRoot.parent : '';
|
||||
nodeTop = mapIdToNode[idTop];
|
||||
var _a = getLevelFromAndTo(nodeRoot), from = _a.from, to = _a.to;
|
||||
mapOptionsToLevel = getLevelOptions({
|
||||
from: from,
|
||||
levels: series.options.levels,
|
||||
to: to,
|
||||
defaults: {
|
||||
colorByPoint: options.colorByPoint,
|
||||
dataLabels: options.dataLabels,
|
||||
levelIsConstant: options.levelIsConstant,
|
||||
levelSize: options.levelSize,
|
||||
slicedOffset: options.slicedOffset
|
||||
}
|
||||
});
|
||||
// NOTE consider doing calculateLevelSizes in a callback to
|
||||
// getLevelOptions
|
||||
mapOptionsToLevel = calculateLevelSizes(mapOptionsToLevel, {
|
||||
diffRadius: diffRadius,
|
||||
from: from,
|
||||
to: to
|
||||
});
|
||||
// TODO Try to combine setTreeValues & setColorRecursive to avoid
|
||||
// unnecessary looping.
|
||||
setTreeValues(tree, {
|
||||
before: cbSetTreeValuesBefore,
|
||||
idRoot: rootId,
|
||||
levelIsConstant: options.levelIsConstant,
|
||||
mapOptionsToLevel: mapOptionsToLevel,
|
||||
mapIdToNode: mapIdToNode,
|
||||
points: series.points,
|
||||
series: series
|
||||
});
|
||||
values = mapIdToNode[''].shapeArgs = {
|
||||
end: radians.end,
|
||||
r: innerRadius,
|
||||
start: radians.start,
|
||||
val: nodeRoot.val,
|
||||
x: positions[0],
|
||||
y: positions[1]
|
||||
};
|
||||
this.setShapeArgs(nodeTop, values, mapOptionsToLevel);
|
||||
// Set mapOptionsToLevel on series for use in drawPoints.
|
||||
series.mapOptionsToLevel = mapOptionsToLevel;
|
||||
// #10669 - verify if all nodes have unique ids
|
||||
series.data.forEach(function (child) {
|
||||
if (nodeIds[child.id]) {
|
||||
error(31, false, series.chart);
|
||||
}
|
||||
// map
|
||||
nodeIds[child.id] = true;
|
||||
});
|
||||
// reset object
|
||||
nodeIds = {};
|
||||
},
|
||||
alignDataLabel: function (point, dataLabel, labelOptions) {
|
||||
if (labelOptions.textPath && labelOptions.textPath.enabled) {
|
||||
return;
|
||||
}
|
||||
return seriesTypes.treemap.prototype.alignDataLabel
|
||||
.apply(this, arguments);
|
||||
},
|
||||
// Animate the slices in. Similar to the animation of polar charts.
|
||||
animate: function (init) {
|
||||
var chart = this.chart, center = [
|
||||
chart.plotWidth / 2,
|
||||
chart.plotHeight / 2
|
||||
], plotLeft = chart.plotLeft, plotTop = chart.plotTop, attribs, group = this.group;
|
||||
// Initialize the animation
|
||||
if (init) {
|
||||
// Scale down the group and place it in the center
|
||||
attribs = {
|
||||
translateX: center[0] + plotLeft,
|
||||
translateY: center[1] + plotTop,
|
||||
scaleX: 0.001,
|
||||
scaleY: 0.001,
|
||||
rotation: 10,
|
||||
opacity: 0.01
|
||||
};
|
||||
group.attr(attribs);
|
||||
// Run the animation
|
||||
}
|
||||
else {
|
||||
attribs = {
|
||||
translateX: plotLeft,
|
||||
translateY: plotTop,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
rotation: 0,
|
||||
opacity: 1
|
||||
};
|
||||
group.animate(attribs, this.options.animation);
|
||||
}
|
||||
},
|
||||
utils: {
|
||||
calculateLevelSizes: calculateLevelSizes,
|
||||
getLevelFromAndTo: getLevelFromAndTo,
|
||||
range: range
|
||||
}
|
||||
};
|
||||
// Properties of the Sunburst series.
|
||||
var sunburstPoint = {
|
||||
draw: drawPoint,
|
||||
shouldDraw: function shouldDraw() {
|
||||
return !this.isNull;
|
||||
},
|
||||
isValid: function isValid() {
|
||||
return true;
|
||||
},
|
||||
getDataLabelPath: function (label) {
|
||||
var renderer = this.series.chart.renderer, shapeArgs = this.shapeExisting, start = shapeArgs.start, end = shapeArgs.end, angle = start + (end - start) / 2, // arc middle value
|
||||
upperHalf = angle < 0 &&
|
||||
angle > -Math.PI ||
|
||||
angle > Math.PI, r = (shapeArgs.r + (label.options.distance || 0)), moreThanHalf;
|
||||
// Check if point is a full circle
|
||||
if (start === -Math.PI / 2 &&
|
||||
correctFloat(end) === correctFloat(Math.PI * 1.5)) {
|
||||
start = -Math.PI + Math.PI / 360;
|
||||
end = -Math.PI / 360;
|
||||
upperHalf = true;
|
||||
}
|
||||
// Check if dataLabels should be render in the
|
||||
// upper half of the circle
|
||||
if (end - start > Math.PI) {
|
||||
upperHalf = false;
|
||||
moreThanHalf = true;
|
||||
}
|
||||
if (this.dataLabelPath) {
|
||||
this.dataLabelPath = this.dataLabelPath.destroy();
|
||||
}
|
||||
this.dataLabelPath = renderer
|
||||
.arc({
|
||||
open: true,
|
||||
longArc: moreThanHalf ? 1 : 0
|
||||
})
|
||||
// Add it inside the data label group so it gets destroyed
|
||||
// with the label
|
||||
.add(label);
|
||||
this.dataLabelPath.attr({
|
||||
start: (upperHalf ? start : end),
|
||||
end: (upperHalf ? end : start),
|
||||
clockwise: +upperHalf,
|
||||
x: shapeArgs.x,
|
||||
y: shapeArgs.y,
|
||||
r: (r + shapeArgs.innerR) / 2
|
||||
});
|
||||
return this.dataLabelPath;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* A `sunburst` series. If the [type](#series.sunburst.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.sunburst
|
||||
* @excluding dataParser, dataURL, stack, dataSorting, boostThreshold,
|
||||
* boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/sunburst.js
|
||||
* @apioption series.sunburst
|
||||
*/
|
||||
/**
|
||||
* @type {Array<number|null|*>}
|
||||
* @extends series.treemap.data
|
||||
* @excluding x, y
|
||||
* @product highcharts
|
||||
* @apioption series.sunburst.data
|
||||
*/
|
||||
/**
|
||||
* @type {Highcharts.SeriesSunburstDataLabelsOptionsObject|Array<Highcharts.SeriesSunburstDataLabelsOptionsObject>}
|
||||
* @product highcharts
|
||||
* @apioption series.sunburst.data.dataLabels
|
||||
*/
|
||||
/**
|
||||
* The value of the point, resulting in a relative area of the point
|
||||
* in the sunburst.
|
||||
*
|
||||
* @type {number|null}
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption series.sunburst.data.value
|
||||
*/
|
||||
/**
|
||||
* Use this option to build a tree structure. The value should be the id of the
|
||||
* point which is the parent. If no points has a matching id, or this option is
|
||||
* undefined, then the parent will be set to the root.
|
||||
*
|
||||
* @type {string}
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption series.sunburst.data.parent
|
||||
*/
|
||||
/**
|
||||
* Whether to display a slice offset from the center. When a sunburst point is
|
||||
* sliced, its children are also offset.
|
||||
*
|
||||
* @sample highcharts/plotoptions/sunburst-sliced
|
||||
* Sliced sunburst
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @since 6.0.4
|
||||
* @product highcharts
|
||||
* @apioption series.sunburst.data.sliced
|
||||
*/
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.sunburst
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('sunburst', 'treemap', sunburstOptions, sunburstSeries, sunburstPoint);
|
||||
@@ -0,0 +1,582 @@
|
||||
/* *
|
||||
*
|
||||
* Tilemaps module
|
||||
*
|
||||
* (c) 2010-2017 Highsoft AS
|
||||
* Author: Øystein Moseng
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
/**
|
||||
* @typedef {"circle"|"diamond"|"hexagon"|"square"} Highcharts.TilemapShapeValue
|
||||
*/
|
||||
''; // detach doclets above
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, clamp = U.clamp, extend = U.extend, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Series/HeatmapSeries.js';
|
||||
/**
|
||||
* Utility func to get padding definition from tile size division
|
||||
* @private
|
||||
* @param {Highcharts.TilemapSeries} series
|
||||
* series
|
||||
* @param {Highcharts.number} xDiv
|
||||
* xDiv
|
||||
* @param {Highcharts.number} yDiv
|
||||
* yDiv
|
||||
* @return {Highcharts.TilemapPaddingObject}
|
||||
*/
|
||||
function tilePaddingFromTileSize(series, xDiv, yDiv) {
|
||||
var options = series.options;
|
||||
return {
|
||||
xPad: (options.colsize || 1) / -xDiv,
|
||||
yPad: (options.rowsize || 1) / -yDiv
|
||||
};
|
||||
}
|
||||
// Map of shape types.
|
||||
H.tileShapeTypes = {
|
||||
// Hexagon shape type.
|
||||
hexagon: {
|
||||
alignDataLabel: H.seriesTypes.scatter.prototype.alignDataLabel,
|
||||
getSeriesPadding: function (series) {
|
||||
return tilePaddingFromTileSize(series, 3, 2);
|
||||
},
|
||||
haloPath: function (size) {
|
||||
if (!size) {
|
||||
return [];
|
||||
}
|
||||
var hexagon = this.tileEdges;
|
||||
return [
|
||||
['M', hexagon.x2 - size, hexagon.y1 + size],
|
||||
['L', hexagon.x3 + size, hexagon.y1 + size],
|
||||
['L', hexagon.x4 + size * 1.5, hexagon.y2],
|
||||
['L', hexagon.x3 + size, hexagon.y3 - size],
|
||||
['L', hexagon.x2 - size, hexagon.y3 - size],
|
||||
['L', hexagon.x1 - size * 1.5, hexagon.y2],
|
||||
['Z']
|
||||
];
|
||||
},
|
||||
translate: function () {
|
||||
var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, seriesPointPadding = options.pointPadding || 0, xPad = (options.colsize || 1) / 3, yPad = (options.rowsize || 1) / 2, yShift;
|
||||
series.generatePoints();
|
||||
series.points.forEach(function (point) {
|
||||
var x1 = clamp(Math.floor(xAxis.len -
|
||||
xAxis.translate(point.x - xPad * 2, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), x2 = clamp(Math.floor(xAxis.len -
|
||||
xAxis.translate(point.x - xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), x3 = clamp(Math.floor(xAxis.len -
|
||||
xAxis.translate(point.x + xPad, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), x4 = clamp(Math.floor(xAxis.len -
|
||||
xAxis.translate(point.x + xPad * 2, 0, 1, 0, 1)), -xAxis.len, 2 * xAxis.len), y1 = clamp(Math.floor(yAxis.translate(point.y - yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len), y2 = clamp(Math.floor(yAxis.translate(point.y, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len), y3 = clamp(Math.floor(yAxis.translate(point.y + yPad, 0, 1, 0, 1)), -yAxis.len, 2 * yAxis.len), pointPadding = pick(point.pointPadding, seriesPointPadding),
|
||||
// We calculate the point padding of the midpoints to
|
||||
// preserve the angles of the shape.
|
||||
midPointPadding = pointPadding *
|
||||
Math.abs(x2 - x1) / Math.abs(y3 - y2), xMidPadding = xAxis.reversed ?
|
||||
-midPointPadding : midPointPadding, xPointPadding = xAxis.reversed ?
|
||||
-pointPadding : pointPadding, yPointPadding = yAxis.reversed ?
|
||||
-pointPadding : pointPadding;
|
||||
// Shift y-values for every second grid column
|
||||
if (point.x % 2) {
|
||||
yShift = yShift || Math.round(Math.abs(y3 - y1) / 2) *
|
||||
// We have to reverse the shift for reversed y-axes
|
||||
(yAxis.reversed ? -1 : 1);
|
||||
y1 += yShift;
|
||||
y2 += yShift;
|
||||
y3 += yShift;
|
||||
}
|
||||
// Set plotX and plotY for use in K-D-Tree and more
|
||||
point.plotX = point.clientX = (x2 + x3) / 2;
|
||||
point.plotY = y2;
|
||||
// Apply point padding to translated coordinates
|
||||
x1 += xMidPadding + xPointPadding;
|
||||
x2 += xPointPadding;
|
||||
x3 -= xPointPadding;
|
||||
x4 -= xMidPadding + xPointPadding;
|
||||
y1 -= yPointPadding;
|
||||
y3 += yPointPadding;
|
||||
// Store points for halo creation
|
||||
point.tileEdges = {
|
||||
x1: x1, x2: x2, x3: x3, x4: x4, y1: y1, y2: y2, y3: y3
|
||||
};
|
||||
// Finally set the shape for this point
|
||||
point.shapeType = 'path';
|
||||
point.shapeArgs = {
|
||||
d: [
|
||||
['M', x2, y1],
|
||||
['L', x3, y1],
|
||||
['L', x4, y2],
|
||||
['L', x3, y3],
|
||||
['L', x2, y3],
|
||||
['L', x1, y2],
|
||||
['Z']
|
||||
]
|
||||
};
|
||||
});
|
||||
series.translateColors();
|
||||
}
|
||||
},
|
||||
// Diamond shape type.
|
||||
diamond: {
|
||||
alignDataLabel: H.seriesTypes.scatter.prototype.alignDataLabel,
|
||||
getSeriesPadding: function (series) {
|
||||
return tilePaddingFromTileSize(series, 2, 2);
|
||||
},
|
||||
haloPath: function (size) {
|
||||
if (!size) {
|
||||
return [];
|
||||
}
|
||||
var diamond = this.tileEdges;
|
||||
return [
|
||||
['M', diamond.x2, diamond.y1 + size],
|
||||
['L', diamond.x3 + size, diamond.y2],
|
||||
['L', diamond.x2, diamond.y3 - size],
|
||||
['L', diamond.x1 - size, diamond.y2],
|
||||
['Z']
|
||||
];
|
||||
},
|
||||
translate: function () {
|
||||
var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, seriesPointPadding = options.pointPadding || 0, xPad = (options.colsize || 1), yPad = (options.rowsize || 1) / 2, yShift;
|
||||
series.generatePoints();
|
||||
series.points.forEach(function (point) {
|
||||
var x1 = clamp(Math.round(xAxis.len -
|
||||
xAxis.translate(point.x - xPad, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), x2 = clamp(Math.round(xAxis.len -
|
||||
xAxis.translate(point.x, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), x3 = clamp(Math.round(xAxis.len -
|
||||
xAxis.translate(point.x + xPad, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), y1 = clamp(Math.round(yAxis.translate(point.y - yPad, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), y2 = clamp(Math.round(yAxis.translate(point.y, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), y3 = clamp(Math.round(yAxis.translate(point.y + yPad, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), pointPadding = pick(point.pointPadding, seriesPointPadding),
|
||||
// We calculate the point padding of the midpoints to
|
||||
// preserve the angles of the shape.
|
||||
midPointPadding = pointPadding *
|
||||
Math.abs(x2 - x1) / Math.abs(y3 - y2), xPointPadding = xAxis.reversed ?
|
||||
-midPointPadding : midPointPadding, yPointPadding = yAxis.reversed ?
|
||||
-pointPadding : pointPadding;
|
||||
// Shift y-values for every second grid column
|
||||
// We have to reverse the shift for reversed y-axes
|
||||
if (point.x % 2) {
|
||||
yShift = Math.abs(y3 - y1) / 2 * (yAxis.reversed ? -1 : 1);
|
||||
y1 += yShift;
|
||||
y2 += yShift;
|
||||
y3 += yShift;
|
||||
}
|
||||
// Set plotX and plotY for use in K-D-Tree and more
|
||||
point.plotX = point.clientX = x2;
|
||||
point.plotY = y2;
|
||||
// Apply point padding to translated coordinates
|
||||
x1 += xPointPadding;
|
||||
x3 -= xPointPadding;
|
||||
y1 -= yPointPadding;
|
||||
y3 += yPointPadding;
|
||||
// Store points for halo creation
|
||||
point.tileEdges = {
|
||||
x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3
|
||||
};
|
||||
// Set this point's shape parameters
|
||||
point.shapeType = 'path';
|
||||
point.shapeArgs = {
|
||||
d: [
|
||||
['M', x2, y1],
|
||||
['L', x3, y2],
|
||||
['L', x2, y3],
|
||||
['L', x1, y2],
|
||||
['Z']
|
||||
]
|
||||
};
|
||||
});
|
||||
series.translateColors();
|
||||
}
|
||||
},
|
||||
// Circle shape type.
|
||||
circle: {
|
||||
alignDataLabel: H.seriesTypes.scatter.prototype.alignDataLabel,
|
||||
getSeriesPadding: function (series) {
|
||||
return tilePaddingFromTileSize(series, 2, 2);
|
||||
},
|
||||
haloPath: function (size) {
|
||||
return H.seriesTypes.scatter.prototype.pointClass.prototype.haloPath
|
||||
.call(this, size + (size && this.radius));
|
||||
},
|
||||
translate: function () {
|
||||
var series = this, options = series.options, xAxis = series.xAxis, yAxis = series.yAxis, seriesPointPadding = options.pointPadding || 0, yRadius = (options.rowsize || 1) / 2, colsize = (options.colsize || 1), colsizePx, yRadiusPx, xRadiusPx, radius, forceNextRadiusCompute = false;
|
||||
series.generatePoints();
|
||||
series.points.forEach(function (point) {
|
||||
var x = clamp(Math.round(xAxis.len -
|
||||
xAxis.translate(point.x, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len), y = clamp(Math.round(yAxis.translate(point.y, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len), pointPadding = seriesPointPadding, hasPerPointPadding = false;
|
||||
// If there is point padding defined on a single point, add it
|
||||
if (typeof point.pointPadding !== 'undefined') {
|
||||
pointPadding = point.pointPadding;
|
||||
hasPerPointPadding = true;
|
||||
forceNextRadiusCompute = true;
|
||||
}
|
||||
// Find radius if not found already.
|
||||
// Use the smallest one (x vs y) to avoid overlap.
|
||||
// Note that the radius will be recomputed for each series.
|
||||
// Ideal (max) x radius is dependent on y radius:
|
||||
/*
|
||||
* (circle 2)
|
||||
|
||||
* (circle 3)
|
||||
| yRadiusPx
|
||||
(circle 1) *-------|
|
||||
colsizePx
|
||||
|
||||
The distance between circle 1 and 3 (and circle 2 and 3) is
|
||||
2r, which is the hypotenuse of the triangle created by
|
||||
colsizePx and yRadiusPx. If the distance between circle 2
|
||||
and circle 1 is less than 2r, we use half of that distance
|
||||
instead (yRadiusPx).
|
||||
*/
|
||||
if (!radius || forceNextRadiusCompute) {
|
||||
colsizePx = Math.abs(clamp(Math.floor(xAxis.len -
|
||||
xAxis.translate(point.x + colsize, 0, 1, 0, 0)), -xAxis.len, 2 * xAxis.len) - x);
|
||||
yRadiusPx = Math.abs(clamp(Math.floor(yAxis.translate(point.y + yRadius, 0, 1, 0, 0)), -yAxis.len, 2 * yAxis.len) - y);
|
||||
xRadiusPx = Math.floor(Math.sqrt((colsizePx * colsizePx + yRadiusPx * yRadiusPx)) / 2);
|
||||
radius = Math.min(colsizePx, xRadiusPx, yRadiusPx) - pointPadding;
|
||||
// If we have per point padding we need to always compute
|
||||
// the radius for this point and the next. If we used to
|
||||
// have per point padding but don't anymore, don't force
|
||||
// compute next radius.
|
||||
if (forceNextRadiusCompute && !hasPerPointPadding) {
|
||||
forceNextRadiusCompute = false;
|
||||
}
|
||||
}
|
||||
// Shift y-values for every second grid column.
|
||||
// Note that we always use the optimal y axis radius for this.
|
||||
// Also note: We have to reverse the shift for reversed y-axes.
|
||||
if (point.x % 2) {
|
||||
y += yRadiusPx * (yAxis.reversed ? -1 : 1);
|
||||
}
|
||||
// Set plotX and plotY for use in K-D-Tree and more
|
||||
point.plotX = point.clientX = x;
|
||||
point.plotY = y;
|
||||
// Save radius for halo
|
||||
point.radius = radius;
|
||||
// Set this point's shape parameters
|
||||
point.shapeType = 'circle';
|
||||
point.shapeArgs = {
|
||||
x: x,
|
||||
y: y,
|
||||
r: radius
|
||||
};
|
||||
});
|
||||
series.translateColors();
|
||||
}
|
||||
},
|
||||
// Square shape type.
|
||||
square: {
|
||||
alignDataLabel: H.seriesTypes.heatmap.prototype.alignDataLabel,
|
||||
translate: H.seriesTypes.heatmap.prototype.translate,
|
||||
getSeriesPadding: function () {
|
||||
},
|
||||
haloPath: H.seriesTypes.heatmap.prototype.pointClass.prototype.haloPath
|
||||
}
|
||||
};
|
||||
/* eslint-disable no-invalid-this */
|
||||
// Extension to add pixel padding for series. Uses getSeriesPixelPadding on each
|
||||
// series and adds the largest padding required. If no series has this function
|
||||
// defined, we add nothing.
|
||||
addEvent(H.Axis, 'afterSetAxisTranslation', function () {
|
||||
if (this.recomputingForTilemap || this.coll === 'colorAxis') {
|
||||
return;
|
||||
}
|
||||
var axis = this,
|
||||
// Find which series' padding to use
|
||||
seriesPadding = axis.series
|
||||
.map(function (series) {
|
||||
return series.getSeriesPixelPadding &&
|
||||
series.getSeriesPixelPadding(axis);
|
||||
})
|
||||
.reduce(function (a, b) {
|
||||
return (a && a.padding) > (b && b.padding) ?
|
||||
a :
|
||||
b;
|
||||
}, void 0) ||
|
||||
{
|
||||
padding: 0,
|
||||
axisLengthFactor: 1
|
||||
}, lengthPadding = Math.round(seriesPadding.padding * seriesPadding.axisLengthFactor);
|
||||
// Don't waste time on this if we're not adding extra padding
|
||||
if (seriesPadding.padding) {
|
||||
// Recompute translation with new axis length now (minus padding)
|
||||
axis.len -= lengthPadding;
|
||||
axis.recomputingForTilemap = true;
|
||||
axis.setAxisTranslation();
|
||||
delete axis.recomputingForTilemap;
|
||||
axis.minPixelPadding += seriesPadding.padding;
|
||||
axis.len += lengthPadding;
|
||||
}
|
||||
});
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.tilemap
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('tilemap', 'heatmap'
|
||||
/**
|
||||
* A tilemap series is a type of heatmap where the tile shapes are
|
||||
* configurable.
|
||||
*
|
||||
* @sample highcharts/demo/honeycomb-usa/
|
||||
* Honeycomb tilemap, USA
|
||||
* @sample maps/plotoptions/honeycomb-brazil/
|
||||
* Honeycomb tilemap, Brazil
|
||||
* @sample maps/plotoptions/honeycomb-china/
|
||||
* Honeycomb tilemap, China
|
||||
* @sample maps/plotoptions/honeycomb-europe/
|
||||
* Honeycomb tilemap, Europe
|
||||
* @sample maps/demo/circlemap-africa/
|
||||
* Circlemap tilemap, Africa
|
||||
* @sample maps/demo/diamondmap
|
||||
* Diamondmap tilemap
|
||||
*
|
||||
* @extends plotOptions.heatmap
|
||||
* @since 6.0.0
|
||||
* @excluding jitter, joinBy, shadow, allAreas, mapData, marker, data,
|
||||
* dataSorting, boostThreshold, boostBlending
|
||||
* @product highcharts highmaps
|
||||
* @requires modules/tilemap.js
|
||||
* @optionparent plotOptions.tilemap
|
||||
*/
|
||||
, {
|
||||
// Remove marker from tilemap default options, as it was before
|
||||
// heatmap refactoring.
|
||||
marker: null,
|
||||
states: {
|
||||
hover: {
|
||||
halo: {
|
||||
enabled: true,
|
||||
size: 2,
|
||||
opacity: 0.5,
|
||||
attributes: {
|
||||
zIndex: 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The padding between points in the tilemap.
|
||||
*
|
||||
* @sample maps/plotoptions/tilemap-pointpadding
|
||||
* Point padding on tiles
|
||||
*/
|
||||
pointPadding: 2,
|
||||
/**
|
||||
* The column size - how many X axis units each column in the tilemap
|
||||
* should span. Works as in [Heatmaps](#plotOptions.heatmap.colsize).
|
||||
*
|
||||
* @sample {highcharts} maps/demo/heatmap/
|
||||
* One day
|
||||
* @sample {highmaps} maps/demo/heatmap/
|
||||
* One day
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.tilemap.colsize
|
||||
*/
|
||||
/**
|
||||
* The row size - how many Y axis units each tilemap row should span.
|
||||
* Analogous to [colsize](#plotOptions.tilemap.colsize).
|
||||
*
|
||||
* @sample {highcharts} maps/demo/heatmap/
|
||||
* 1 by default
|
||||
* @sample {highmaps} maps/demo/heatmap/
|
||||
* 1 by default
|
||||
*
|
||||
* @type {number}
|
||||
* @default 1
|
||||
* @product highcharts highmaps
|
||||
* @apioption plotOptions.tilemap.rowsize
|
||||
*/
|
||||
/**
|
||||
* The shape of the tiles in the tilemap. Possible values are `hexagon`,
|
||||
* `circle`, `diamond`, and `square`.
|
||||
*
|
||||
* @sample maps/demo/circlemap-africa
|
||||
* Circular tile shapes
|
||||
* @sample maps/demo/diamondmap
|
||||
* Diamond tile shapes
|
||||
*
|
||||
* @type {Highcharts.TilemapShapeValue}
|
||||
*/
|
||||
tileShape: 'hexagon'
|
||||
}, {
|
||||
// Use drawPoints, markerAttribs, pointAttribs methods from the old
|
||||
// heatmap implementation.
|
||||
// TODO: Consider standarizing heatmap and tilemap into more
|
||||
// consistent form.
|
||||
markerAttribs: H.seriesTypes.scatter.prototype.markerAttribs,
|
||||
pointAttribs: H.seriesTypes.column.prototype.pointAttribs,
|
||||
// Revert the noop on getSymbol.
|
||||
getSymbol: H.noop,
|
||||
drawPoints: function () {
|
||||
var _this = this;
|
||||
// In styled mode, use CSS, otherwise the fill used in the style
|
||||
// sheet will take precedence over the fill attribute.
|
||||
H.seriesTypes.column.prototype.drawPoints.call(this);
|
||||
this.points.forEach(function (point) {
|
||||
point.graphic &&
|
||||
point.graphic[_this.chart.styledMode ? 'css' : 'animate'](_this.colorAttribs(point));
|
||||
});
|
||||
},
|
||||
// Set tile shape object on series
|
||||
setOptions: function () {
|
||||
// Call original function
|
||||
var ret = H.seriesTypes.heatmap.prototype.setOptions.apply(this, Array.prototype.slice.call(arguments));
|
||||
this.tileShape = H.tileShapeTypes[ret.tileShape];
|
||||
return ret;
|
||||
},
|
||||
// Use the shape's defined data label alignment function
|
||||
alignDataLabel: function () {
|
||||
return this.tileShape.alignDataLabel.apply(this, Array.prototype.slice.call(arguments));
|
||||
},
|
||||
// Get metrics for padding of axis for this series
|
||||
getSeriesPixelPadding: function (axis) {
|
||||
var isX = axis.isXAxis, padding = this.tileShape.getSeriesPadding(this), coord1, coord2;
|
||||
// If the shape type does not require padding, return no-op padding
|
||||
if (!padding) {
|
||||
return {
|
||||
padding: 0,
|
||||
axisLengthFactor: 1
|
||||
};
|
||||
}
|
||||
// Use translate to compute how far outside the points we
|
||||
// draw, and use this difference as padding.
|
||||
coord1 = Math.round(axis.translate(isX ?
|
||||
padding.xPad * 2 :
|
||||
padding.yPad, 0, 1, 0, 1));
|
||||
coord2 = Math.round(axis.translate(isX ? padding.xPad : 0, 0, 1, 0, 1));
|
||||
return {
|
||||
padding: Math.abs(coord1 - coord2) || 0,
|
||||
// Offset the yAxis length to compensate for shift. Setting the
|
||||
// length factor to 2 would add the same margin to max as min.
|
||||
// Now we only add a slight bit of the min margin to max, as we
|
||||
// don't actually draw outside the max bounds. For the xAxis we
|
||||
// draw outside on both sides so we add the same margin to min
|
||||
// and max.
|
||||
axisLengthFactor: isX ? 2 : 1.1
|
||||
};
|
||||
},
|
||||
// Use translate from tileShape
|
||||
translate: function () {
|
||||
return this.tileShape.translate.apply(this, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
}, extend({
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Point#haloPath
|
||||
*
|
||||
* @return {Highcharts.SVGElement|Highcharts.SVGPathArray|Array<Highcharts.SVGElement>}
|
||||
*/
|
||||
haloPath: function () {
|
||||
return this.series.tileShape.haloPath.apply(this, Array.prototype.slice.call(arguments));
|
||||
}
|
||||
}, H.colorPointMixin));
|
||||
/**
|
||||
* A `tilemap` series. If the [type](#series.tilemap.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.tilemap
|
||||
* @excluding allAreas, dataParser, dataURL, joinBy, mapData, marker,
|
||||
* pointRange, shadow, stack, dataSorting, boostThreshold,
|
||||
* boostBlending
|
||||
* @product highcharts highmaps
|
||||
* @requires modules/tilemap.js
|
||||
* @apioption series.tilemap
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `tilemap` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,y,value`. If the first value is a string, it is applied as the name
|
||||
* of the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2\. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 9, 7],
|
||||
* [1, 10, 4],
|
||||
* [2, 6, 3]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of objects with named values. The objects are point configuration
|
||||
* objects as seen below. If the total number of data points exceeds the
|
||||
* series' [turboThreshold](#series.tilemap.turboThreshold), this option is
|
||||
* not available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 3,
|
||||
* value: 10,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 7,
|
||||
* value: 10,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* Note that for some [tileShapes](#plotOptions.tilemap.tileShape) the grid
|
||||
* coordinates are offset.
|
||||
*
|
||||
* @sample maps/series/tilemap-gridoffset
|
||||
* Offset grid coordinates
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.heatmap.data
|
||||
* @excluding marker
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.tilemap.data
|
||||
*/
|
||||
/**
|
||||
* The color of the point. In tilemaps the point color is rarely set
|
||||
* explicitly, as we use the color to denote the `value`. Options for
|
||||
* this are set in the [colorAxis](#colorAxis) configuration.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.tilemap.data.color
|
||||
*/
|
||||
/**
|
||||
* The x coordinate of the point.
|
||||
*
|
||||
* Note that for some [tileShapes](#plotOptions.tilemap.tileShape) the grid
|
||||
* coordinates are offset.
|
||||
*
|
||||
* @sample maps/series/tilemap-gridoffset
|
||||
* Offset grid coordinates
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.tilemap.data.x
|
||||
*/
|
||||
/**
|
||||
* The y coordinate of the point.
|
||||
*
|
||||
* Note that for some [tileShapes](#plotOptions.tilemap.tileShape) the grid
|
||||
* coordinates are offset.
|
||||
*
|
||||
* @sample maps/series/tilemap-gridoffset
|
||||
* Offset grid coordinates
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highmaps
|
||||
* @apioption series.tilemap.data.y
|
||||
*/
|
||||
''; // adds doclets above to the transpiled file
|
||||
@@ -0,0 +1,595 @@
|
||||
/* *
|
||||
*
|
||||
* Timeline Series.
|
||||
*
|
||||
* (c) 2010-2020 Highsoft AS
|
||||
*
|
||||
* Author: Daniel Studencki
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import LegendSymbolMixin from '../Mixins/LegendSymbol.js';
|
||||
import Point from '../Core/Series/Point.js';
|
||||
import SVGElement from '../Core/Renderer/SVG/SVGElement.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, defined = U.defined, isNumber = U.isNumber, merge = U.merge, objectEach = U.objectEach, pick = U.pick, seriesType = U.seriesType;
|
||||
/**
|
||||
* Callback JavaScript function to format the data label as a string. Note that
|
||||
* if a `format` is defined, the format takes precedence and the formatter is
|
||||
* ignored.
|
||||
*
|
||||
* @callback Highcharts.TimelineDataLabelsFormatterCallbackFunction
|
||||
*
|
||||
* @param {Highcharts.PointLabelObject|Highcharts.TimelineDataLabelsFormatterContextObject} this
|
||||
* Data label context to format
|
||||
*
|
||||
* @return {number|string|null|undefined}
|
||||
* Formatted data label text
|
||||
*/
|
||||
/**
|
||||
* @interface Highcharts.TimelineDataLabelsFormatterContextObject
|
||||
* @extends Highcharts.PointLabelObject
|
||||
*/ /**
|
||||
* @name Highcharts.TimelineDataLabelsFormatterContextObject#key
|
||||
* @type {string|undefined}
|
||||
*/ /**
|
||||
* @name Highcharts.TimelineDataLabelsFormatterContextObject#point
|
||||
* @type {Highcharts.Point}
|
||||
*/ /**
|
||||
* @name Highcharts.TimelineDataLabelsFormatterContextObject#series
|
||||
* @type {Highcharts.Series}
|
||||
*/
|
||||
''; // dettach doclets above
|
||||
import '../Core/Series/Series.js';
|
||||
var TrackerMixin = H.TrackerMixin, Series = H.Series, seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* The timeline series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.timeline
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('timeline', 'line',
|
||||
/**
|
||||
* The timeline series presents given events along a drawn line.
|
||||
*
|
||||
* @sample highcharts/series-timeline/alternate-labels
|
||||
* Timeline series
|
||||
* @sample highcharts/series-timeline/inverted
|
||||
* Inverted timeline
|
||||
* @sample highcharts/series-timeline/datetime-axis
|
||||
* With true datetime axis
|
||||
*
|
||||
* @extends plotOptions.line
|
||||
* @since 7.0.0
|
||||
* @product highcharts
|
||||
* @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
|
||||
* cropThreshold, dashStyle, findNearestPointBy,
|
||||
* getExtremesFromAll, lineWidth, negativeColor,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement,
|
||||
* pointStart, softThreshold, stacking, step, threshold,
|
||||
* turboThreshold, zoneAxis, zones, dataSorting,
|
||||
* boostBlending
|
||||
* @requires modules/timeline
|
||||
* @optionparent plotOptions.timeline
|
||||
*/
|
||||
{
|
||||
colorByPoint: true,
|
||||
stickyTracking: false,
|
||||
ignoreHiddenPoint: true,
|
||||
legendType: 'point',
|
||||
lineWidth: 4,
|
||||
tooltip: {
|
||||
headerFormat: '<span style="color:{point.color}">\u25CF</span> ' +
|
||||
'<span style="font-size: 10px"> {point.key}</span><br/>',
|
||||
pointFormat: '{point.description}'
|
||||
},
|
||||
states: {
|
||||
hover: {
|
||||
lineWidthPlus: 0
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @declare Highcharts.TimelineDataLabelsOptionsObject
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
allowOverlap: true,
|
||||
/**
|
||||
* Whether to position data labels alternately. For example, if
|
||||
* [distance](#plotOptions.timeline.dataLabels.distance)
|
||||
* is set equal to `100`, then data labels will be positioned
|
||||
* alternately (on both sides of the point) at a distance of 100px.
|
||||
*
|
||||
* @sample {highcharts} highcharts/series-timeline/alternate-disabled
|
||||
* Alternate disabled
|
||||
*/
|
||||
alternate: true,
|
||||
backgroundColor: '#ffffff',
|
||||
borderWidth: 1,
|
||||
borderColor: '#999999',
|
||||
borderRadius: 3,
|
||||
color: '#333333',
|
||||
/**
|
||||
* The color of the line connecting the data label to the point.
|
||||
* The default color is the same as the point's color.
|
||||
*
|
||||
* In styled mode, the connector stroke is given in the
|
||||
* `.highcharts-data-label-connector` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/series-timeline/connector-styles
|
||||
* Custom connector width and color
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @apioption plotOptions.timeline.dataLabels.connectorColor
|
||||
*/
|
||||
/**
|
||||
* The width of the line connecting the data label to the point.
|
||||
*
|
||||
* In styled mode, the connector stroke width is given in the
|
||||
* `.highcharts-data-label-connector` class.
|
||||
*
|
||||
* @sample {highcharts} highcharts/series-timeline/connector-styles
|
||||
* Custom connector width and color
|
||||
*/
|
||||
connectorWidth: 1,
|
||||
/**
|
||||
* A pixel value defining the distance between the data label and
|
||||
* the point. Negative numbers puts the label on top of the point.
|
||||
*/
|
||||
distance: 100,
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* @type {Highcharts.TimelineDataLabelsFormatterCallbackFunction}
|
||||
* @default function () {
|
||||
* var format;
|
||||
*
|
||||
* if (!this.series.chart.styledMode) {
|
||||
* format = '<span style="color:' + this.point.color +
|
||||
* '">● </span>';
|
||||
* } else {
|
||||
* format = '<span>● </span>';
|
||||
* }
|
||||
* format += '<span>' + (this.key || '') + '</span><br/>' +
|
||||
* (this.point.label || '');
|
||||
* return format;
|
||||
* }
|
||||
*/
|
||||
formatter: function () {
|
||||
var format;
|
||||
if (!this.series.chart.styledMode) {
|
||||
format = '<span style="color:' + this.point.color +
|
||||
'">● </span>';
|
||||
}
|
||||
else {
|
||||
format = '<span>● </span>';
|
||||
}
|
||||
format += '<span class="highcharts-strong">' +
|
||||
(this.key || '') + '</span><br/>' +
|
||||
(this.point.label || '');
|
||||
return format;
|
||||
},
|
||||
style: {
|
||||
/** @internal */
|
||||
textOutline: 'none',
|
||||
/** @internal */
|
||||
fontWeight: 'normal',
|
||||
/** @internal */
|
||||
fontSize: '12px'
|
||||
},
|
||||
/**
|
||||
* Shadow options for the data label.
|
||||
*
|
||||
* @type {boolean|Highcharts.CSSObject}
|
||||
*/
|
||||
shadow: false,
|
||||
/**
|
||||
* @type {number}
|
||||
* @apioption plotOptions.timeline.dataLabels.width
|
||||
*/
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
marker: {
|
||||
enabledThreshold: 0,
|
||||
symbol: 'square',
|
||||
radius: 6,
|
||||
lineWidth: 2,
|
||||
height: 15
|
||||
},
|
||||
showInLegend: false,
|
||||
colorKey: 'x'
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Series#
|
||||
*/
|
||||
{
|
||||
trackerGroups: ['markerGroup', 'dataLabelsGroup'],
|
||||
// Use a simple symbol from LegendSymbolMixin
|
||||
drawLegendSymbol: LegendSymbolMixin.drawRectangle,
|
||||
// Use a group of trackers from TrackerMixin
|
||||
drawTracker: TrackerMixin.drawTrackerPoint,
|
||||
init: function () {
|
||||
var series = this;
|
||||
Series.prototype.init.apply(series, arguments);
|
||||
addEvent(series, 'afterTranslate', function () {
|
||||
var lastPlotX, closestPointRangePx = Number.MAX_VALUE;
|
||||
series.points.forEach(function (point) {
|
||||
// Set the isInside parameter basing also on the real point
|
||||
// visibility, in order to avoid showing hidden points
|
||||
// in drawPoints method.
|
||||
point.isInside = point.isInside && point.visible;
|
||||
// New way of calculating closestPointRangePx value, which
|
||||
// respects the real point visibility is needed.
|
||||
if (point.visible && !point.isNull) {
|
||||
if (defined(lastPlotX)) {
|
||||
closestPointRangePx = Math.min(closestPointRangePx, Math.abs(point.plotX - lastPlotX));
|
||||
}
|
||||
lastPlotX = point.plotX;
|
||||
}
|
||||
});
|
||||
series.closestPointRangePx = closestPointRangePx;
|
||||
});
|
||||
// Distribute data labels before rendering them. Distribution is
|
||||
// based on the 'dataLabels.distance' and 'dataLabels.alternate'
|
||||
// property.
|
||||
addEvent(series, 'drawDataLabels', function () {
|
||||
// Distribute data labels basing on defined algorithm.
|
||||
series.distributeDL(); // @todo use this scope for series
|
||||
});
|
||||
addEvent(series, 'afterDrawDataLabels', function () {
|
||||
var dataLabel; // @todo use this scope for series
|
||||
// Draw or align connector for each point.
|
||||
series.points.forEach(function (point) {
|
||||
dataLabel = point.dataLabel;
|
||||
if (dataLabel) {
|
||||
// Within this wrap method is necessary to save the
|
||||
// current animation params, because the data label
|
||||
// target position (after animation) is needed to align
|
||||
// connectors.
|
||||
dataLabel.animate = function (params) {
|
||||
if (this.targetPosition) {
|
||||
this.targetPosition = params;
|
||||
}
|
||||
return SVGElement.prototype.animate.apply(this, arguments);
|
||||
};
|
||||
// Initialize the targetPosition field within data label
|
||||
// object. It's necessary because there is need to know
|
||||
// expected position of specific data label, when
|
||||
// aligning connectors. This field is overrided inside
|
||||
// of SVGElement.animate() wrapped method.
|
||||
if (!dataLabel.targetPosition) {
|
||||
dataLabel.targetPosition = {};
|
||||
}
|
||||
return point.drawConnector();
|
||||
}
|
||||
});
|
||||
});
|
||||
addEvent(series.chart, 'afterHideOverlappingLabel', function () {
|
||||
series.points.forEach(function (p) {
|
||||
if (p.connector &&
|
||||
p.dataLabel &&
|
||||
p.dataLabel.oldOpacity !== p.dataLabel.newOpacity) {
|
||||
p.alignConnector();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
alignDataLabel: function (point, dataLabel, options, alignTo) {
|
||||
var series = this, isInverted = series.chart.inverted, visiblePoints = series.visibilityMap.filter(function (point) {
|
||||
return point;
|
||||
}), visiblePointsCount = series.visiblePointsCount, pointIndex = visiblePoints.indexOf(point), isFirstOrLast = (!pointIndex || pointIndex === visiblePointsCount - 1), dataLabelsOptions = series.options.dataLabels, userDLOptions = point.userDLOptions || {},
|
||||
// Define multiplier which is used to calculate data label
|
||||
// width. If data labels are alternate, they have two times more
|
||||
// space to adapt (excepting first and last ones, which has only
|
||||
// one and half), than in case of placing all data labels side
|
||||
// by side.
|
||||
multiplier = dataLabelsOptions.alternate ?
|
||||
(isFirstOrLast ? 1.5 : 2) :
|
||||
1, distance, availableSpace = Math.floor(series.xAxis.len / visiblePointsCount), pad = dataLabel.padding, targetDLWidth, styles;
|
||||
// Adjust data label width to the currently available space.
|
||||
if (point.visible) {
|
||||
distance = Math.abs(userDLOptions.x || point.options.dataLabels.x);
|
||||
if (isInverted) {
|
||||
targetDLWidth = ((distance - pad) * 2 - (point.itemHeight / 2));
|
||||
styles = {
|
||||
width: targetDLWidth + 'px',
|
||||
// Apply ellipsis when data label height is exceeded.
|
||||
textOverflow: dataLabel.width / targetDLWidth *
|
||||
dataLabel.height / 2 > availableSpace * multiplier ?
|
||||
'ellipsis' : 'none'
|
||||
};
|
||||
}
|
||||
else {
|
||||
styles = {
|
||||
width: (userDLOptions.width ||
|
||||
dataLabelsOptions.width ||
|
||||
availableSpace * multiplier - (pad * 2)) + 'px'
|
||||
};
|
||||
}
|
||||
dataLabel.css(styles);
|
||||
if (!series.chart.styledMode) {
|
||||
dataLabel.shadow(dataLabelsOptions.shadow);
|
||||
}
|
||||
}
|
||||
Series.prototype.alignDataLabel.apply(series, arguments);
|
||||
},
|
||||
processData: function () {
|
||||
var series = this, visiblePoints = 0, i;
|
||||
series.visibilityMap = series.getVisibilityMap();
|
||||
// Calculate currently visible points.
|
||||
series.visibilityMap.forEach(function (point) {
|
||||
if (point) {
|
||||
visiblePoints++;
|
||||
}
|
||||
});
|
||||
series.visiblePointsCount = visiblePoints;
|
||||
for (i = 0; i < series.xData.length; i++) {
|
||||
series.yData[i] = 1;
|
||||
}
|
||||
Series.prototype.processData.call(this, arguments);
|
||||
return;
|
||||
},
|
||||
getXExtremes: function (xData) {
|
||||
var series = this, filteredData = xData.filter(function (x, i) {
|
||||
return series.points[i].isValid() &&
|
||||
series.points[i].visible;
|
||||
});
|
||||
return {
|
||||
min: arrayMin(filteredData),
|
||||
max: arrayMax(filteredData)
|
||||
};
|
||||
},
|
||||
generatePoints: function () {
|
||||
var series = this;
|
||||
Series.prototype.generatePoints.apply(series);
|
||||
series.points.forEach(function (point, i) {
|
||||
point.applyOptions({
|
||||
x: series.xData[i]
|
||||
}, series.xData[i]);
|
||||
});
|
||||
},
|
||||
getVisibilityMap: function () {
|
||||
var series = this, map = (series.data.length ?
|
||||
series.data : series.userOptions.data).map(function (point) {
|
||||
return (point &&
|
||||
point.visible !== false &&
|
||||
!point.isNull) ? point : false;
|
||||
});
|
||||
return map;
|
||||
},
|
||||
distributeDL: function () {
|
||||
var series = this, dataLabelsOptions = series.options.dataLabels, options, pointDLOptions, newOptions = {}, visibilityIndex = 1, distance = dataLabelsOptions.distance;
|
||||
series.points.forEach(function (point) {
|
||||
if (point.visible && !point.isNull) {
|
||||
options = point.options;
|
||||
pointDLOptions = point.options.dataLabels;
|
||||
if (!series.hasRendered) {
|
||||
point.userDLOptions =
|
||||
merge({}, pointDLOptions);
|
||||
}
|
||||
newOptions[series.chart.inverted ? 'x' : 'y'] =
|
||||
dataLabelsOptions.alternate && visibilityIndex % 2 ?
|
||||
-distance : distance;
|
||||
options.dataLabels = merge(newOptions, point.userDLOptions);
|
||||
visibilityIndex++;
|
||||
}
|
||||
});
|
||||
},
|
||||
markerAttribs: function (point, state) {
|
||||
var series = this, seriesMarkerOptions = series.options.marker, seriesStateOptions, pointMarkerOptions = point.marker || {}, symbol = (pointMarkerOptions.symbol || seriesMarkerOptions.symbol), pointStateOptions, width = pick(pointMarkerOptions.width, seriesMarkerOptions.width, series.closestPointRangePx), height = pick(pointMarkerOptions.height, seriesMarkerOptions.height), radius = 0, attribs;
|
||||
// Call default markerAttribs method, when the xAxis type
|
||||
// is set to datetime.
|
||||
if (series.xAxis.dateTime) {
|
||||
return seriesTypes.line.prototype.markerAttribs
|
||||
.call(this, point, state);
|
||||
}
|
||||
// Handle hover and select states
|
||||
if (state) {
|
||||
seriesStateOptions =
|
||||
seriesMarkerOptions.states[state] || {};
|
||||
pointStateOptions = pointMarkerOptions.states &&
|
||||
pointMarkerOptions.states[state] || {};
|
||||
radius = pick(pointStateOptions.radius, seriesStateOptions.radius, radius + (seriesStateOptions.radiusPlus || 0));
|
||||
}
|
||||
point.hasImage = (symbol && symbol.indexOf('url') === 0);
|
||||
attribs = {
|
||||
x: Math.floor(point.plotX) - (width / 2) - (radius / 2),
|
||||
y: point.plotY - (height / 2) - (radius / 2),
|
||||
width: width + radius,
|
||||
height: height + radius
|
||||
};
|
||||
return attribs;
|
||||
},
|
||||
bindAxes: function () {
|
||||
var series = this;
|
||||
Series.prototype.bindAxes.call(series);
|
||||
['xAxis', 'yAxis'].forEach(function (axis) {
|
||||
// Initially set the linked xAxis type to category.
|
||||
if (axis === 'xAxis' && !series[axis].userOptions.type) {
|
||||
series[axis].categories = series[axis].hasNames = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @lends Highcharts.Point#
|
||||
*/
|
||||
{
|
||||
init: function () {
|
||||
var point = Point.prototype.init.apply(this, arguments);
|
||||
point.name = pick(point.name, 'Event');
|
||||
point.y = 1;
|
||||
return point;
|
||||
},
|
||||
isValid: function () {
|
||||
return this.options.y !== null;
|
||||
},
|
||||
setVisible: function (vis, redraw) {
|
||||
var point = this, series = point.series;
|
||||
redraw = pick(redraw, series.options.ignoreHiddenPoint);
|
||||
seriesTypes.pie.prototype.pointClass.prototype
|
||||
.setVisible.call(point, vis, false);
|
||||
// Process new data
|
||||
series.processData();
|
||||
if (redraw) {
|
||||
series.chart.redraw();
|
||||
}
|
||||
},
|
||||
setState: function () {
|
||||
var proceed = Series.prototype.pointClass.prototype.setState;
|
||||
// Prevent triggering the setState method on null points.
|
||||
if (!this.isNull) {
|
||||
proceed.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
getConnectorPath: function () {
|
||||
var point = this, chart = point.series.chart, xAxisLen = point.series.xAxis.len, inverted = chart.inverted, direction = inverted ? 'x2' : 'y2', dl = point.dataLabel, targetDLPos = dl.targetPosition, coords = {
|
||||
x1: point.plotX,
|
||||
y1: point.plotY,
|
||||
x2: point.plotX,
|
||||
y2: isNumber(targetDLPos.y) ? targetDLPos.y : dl.y
|
||||
}, negativeDistance = ((dl.alignAttr || dl)[direction[0]] <
|
||||
point.series.yAxis.len / 2), path;
|
||||
// Recalculate coords when the chart is inverted.
|
||||
if (inverted) {
|
||||
coords = {
|
||||
x1: point.plotY,
|
||||
y1: xAxisLen - point.plotX,
|
||||
x2: targetDLPos.x || dl.x,
|
||||
y2: xAxisLen - point.plotX
|
||||
};
|
||||
}
|
||||
// Subtract data label width or height from expected coordinate so
|
||||
// that the connector would start from the appropriate edge.
|
||||
if (negativeDistance) {
|
||||
coords[direction] += dl[inverted ? 'width' : 'height'];
|
||||
}
|
||||
// Change coordinates so that they will be relative to data label.
|
||||
objectEach(coords, function (_coord, i) {
|
||||
coords[i] -= (dl.alignAttr || dl)[i[0]];
|
||||
});
|
||||
path = chart.renderer.crispLine([
|
||||
['M', coords.x1, coords.y1],
|
||||
['L', coords.x2, coords.y2]
|
||||
], dl.options.connectorWidth);
|
||||
return path;
|
||||
},
|
||||
drawConnector: function () {
|
||||
var point = this, series = point.series;
|
||||
if (!point.connector) {
|
||||
point.connector = series.chart.renderer
|
||||
.path(point.getConnectorPath())
|
||||
.attr({
|
||||
zIndex: -1
|
||||
})
|
||||
.add(point.dataLabel);
|
||||
}
|
||||
if (point.series.chart.isInsidePlot(// #10507
|
||||
point.dataLabel.x, point.dataLabel.y)) {
|
||||
point.alignConnector();
|
||||
}
|
||||
},
|
||||
alignConnector: function () {
|
||||
var point = this, series = point.series, connector = point.connector, dl = point.dataLabel, dlOptions = point.dataLabel.options = merge(series.options.dataLabels, point.options.dataLabels), chart = point.series.chart, bBox = connector.getBBox(), plotPos = {
|
||||
x: bBox.x + dl.translateX,
|
||||
y: bBox.y + dl.translateY
|
||||
}, isVisible;
|
||||
// Include a half of connector width in order to run animation,
|
||||
// when connectors are aligned to the plot area edge.
|
||||
if (chart.inverted) {
|
||||
plotPos.y -= dl.options.connectorWidth / 2;
|
||||
}
|
||||
else {
|
||||
plotPos.x += dl.options.connectorWidth / 2;
|
||||
}
|
||||
isVisible = chart.isInsidePlot(plotPos.x, plotPos.y);
|
||||
connector[isVisible ? 'animate' : 'attr']({
|
||||
d: point.getConnectorPath()
|
||||
});
|
||||
if (!series.chart.styledMode) {
|
||||
connector.attr({
|
||||
stroke: dlOptions.connectorColor || point.color,
|
||||
'stroke-width': dlOptions.connectorWidth,
|
||||
opacity: dl[defined(dl.newOpacity) ? 'newOpacity' : 'opacity']
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* The `timeline` series. If the [type](#series.timeline.type) option is
|
||||
* not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.timeline
|
||||
* @excluding animationLimit, boostThreshold, connectEnds, connectNulls,
|
||||
* cropThreshold, dashStyle, dataParser, dataURL, findNearestPointBy,
|
||||
* getExtremesFromAll, lineWidth, negativeColor,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement, pointStart,
|
||||
* softThreshold, stacking, stack, step, threshold, turboThreshold,
|
||||
* zoneAxis, zones, dataSorting, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/timeline
|
||||
* @apioption series.timeline
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `timeline` series type,
|
||||
* points can be given with three general parameters, `name`, `label`,
|
||||
* and `description`:
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```js
|
||||
* series: [{
|
||||
* type: 'timeline',
|
||||
* data: [{
|
||||
* name: 'Jan 2018',
|
||||
* label: 'Some event label',
|
||||
* description: 'Description to show in tooltip'
|
||||
* }]
|
||||
* }]
|
||||
* ```
|
||||
* If all points additionally have the `x` values, and xAxis type is set to
|
||||
* `datetime`, then events are laid out on a true time axis, where their
|
||||
* placement reflects the actual time between them.
|
||||
*
|
||||
* @sample {highcharts} highcharts/series-timeline/alternate-labels
|
||||
* Alternate labels
|
||||
* @sample {highcharts} highcharts/series-timeline/datetime-axis
|
||||
* Real time intervals
|
||||
*
|
||||
* @type {Array<*>}
|
||||
* @extends series.line.data
|
||||
* @excluding marker, y
|
||||
* @product highcharts
|
||||
* @apioption series.timeline.data
|
||||
*/
|
||||
/**
|
||||
* The name of event.
|
||||
*
|
||||
* @type {string}
|
||||
* @product highcharts
|
||||
* @apioption series.timeline.data.name
|
||||
*/
|
||||
/**
|
||||
* The label of event.
|
||||
*
|
||||
* @type {string}
|
||||
* @product highcharts
|
||||
* @apioption series.timeline.data.label
|
||||
*/
|
||||
/**
|
||||
* The description of event. This description will be shown in tooltip.
|
||||
*
|
||||
* @type {string}
|
||||
* @product highcharts
|
||||
* @apioption series.timeline.data.description
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,374 @@
|
||||
/* *
|
||||
*
|
||||
* Variable Pie module for Highcharts
|
||||
*
|
||||
* (c) 2010-2017 Grzegorz Blachliński
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
/**
|
||||
* @typedef {"area"|"radius"} Highcharts.VariablePieSizeByValue
|
||||
*/
|
||||
import U from '../Core/Utilities.js';
|
||||
var arrayMax = U.arrayMax, arrayMin = U.arrayMin, clamp = U.clamp, fireEvent = U.fireEvent, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
var pieProto = H.seriesTypes.pie.prototype;
|
||||
/**
|
||||
* The variablepie series type.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.variablepie
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('variablepie', 'pie',
|
||||
/**
|
||||
* A variable pie series is a two dimensional series type, where each point
|
||||
* renders an Y and Z value. Each point is drawn as a pie slice where the
|
||||
* size (arc) of the slice relates to the Y value and the radius of pie
|
||||
* slice relates to the Z value.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/variable-radius-pie/
|
||||
* Variable-radius pie chart
|
||||
*
|
||||
* @extends plotOptions.pie
|
||||
* @excluding dragDrop
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @requires modules/variable-pie.js
|
||||
* @optionparent plotOptions.variablepie
|
||||
*/
|
||||
{
|
||||
/**
|
||||
* The minimum size of the points' radius related to chart's `plotArea`.
|
||||
* If a number is set, it applies in pixels.
|
||||
*
|
||||
* @sample {highcharts} highcharts/variable-radius-pie/min-max-point-size/
|
||||
* Example of minPointSize and maxPointSize
|
||||
* @sample {highcharts} highcharts/variable-radius-pie/min-point-size-100/
|
||||
* minPointSize set to 100
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
minPointSize: '10%',
|
||||
/**
|
||||
* The maximum size of the points' radius related to chart's `plotArea`.
|
||||
* If a number is set, it applies in pixels.
|
||||
*
|
||||
* @sample {highcharts} highcharts/variable-radius-pie/min-max-point-size/
|
||||
* Example of minPointSize and maxPointSize
|
||||
*
|
||||
* @type {number|string}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
maxPointSize: '100%',
|
||||
/**
|
||||
* The minimum possible z value for the point's radius calculation. If
|
||||
* the point's Z value is smaller than zMin, the slice will be drawn
|
||||
* according to the zMin value.
|
||||
*
|
||||
* @sample {highcharts} highcharts/variable-radius-pie/zmin-5/
|
||||
* zMin set to 5, smaller z values are treated as 5
|
||||
* @sample {highcharts} highcharts/variable-radius-pie/zmin-zmax/
|
||||
* Series limited by both zMin and zMax
|
||||
*
|
||||
* @type {number}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
zMin: void 0,
|
||||
/**
|
||||
* The maximum possible z value for the point's radius calculation. If
|
||||
* the point's Z value is bigger than zMax, the slice will be drawn
|
||||
* according to the zMax value
|
||||
*
|
||||
* @sample {highcharts} highcharts/variable-radius-pie/zmin-zmax/
|
||||
* Series limited by both zMin and zMax
|
||||
*
|
||||
* @type {number}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
zMax: void 0,
|
||||
/**
|
||||
* Whether the pie slice's value should be represented by the area or
|
||||
* the radius of the slice. Can be either `area` or `radius`. The
|
||||
* default, `area`, corresponds best to the human perception of the size
|
||||
* of each pie slice.
|
||||
*
|
||||
* @sample {highcharts} highcharts/variable-radius-pie/sizeby/
|
||||
* Difference between area and radius sizeBy
|
||||
*
|
||||
* @type {Highcharts.VariablePieSizeByValue}
|
||||
* @since 6.0.0
|
||||
*/
|
||||
sizeBy: 'area',
|
||||
tooltip: {
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}<br/>Value: {point.y}<br/>Size: {point.z}<br/>'
|
||||
}
|
||||
}, {
|
||||
pointArrayMap: ['y', 'z'],
|
||||
parallelArrays: ['x', 'y', 'z'],
|
||||
// It is needed to null series.center on chart redraw. Probably good
|
||||
// idea will be to add this option in directly in pie series.
|
||||
redraw: function () {
|
||||
this.center = null;
|
||||
pieProto.redraw.call(this, arguments);
|
||||
},
|
||||
// For arrayMin and arrayMax calculations array shouldn't have
|
||||
// null/undefined/string values. In this case it is needed to check if
|
||||
// points Z value is a Number.
|
||||
zValEval: function (zVal) {
|
||||
if (typeof zVal === 'number' && !isNaN(zVal)) {
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
// Before standard translate method for pie chart it is needed to
|
||||
// calculate min/max radius of each pie slice based on its Z value.
|
||||
calculateExtremes: function () {
|
||||
var series = this, chart = series.chart, plotWidth = chart.plotWidth, plotHeight = chart.plotHeight, seriesOptions = series.options, slicingRoom = 2 * (seriesOptions.slicedOffset || 0), zMin, zMax, zData = series.zData, smallestSize = Math.min(plotWidth, plotHeight) - slicingRoom,
|
||||
// Min and max size of pie slice:
|
||||
extremes = {},
|
||||
// In pie charts size of a pie is changed to make space for
|
||||
// dataLabels, then series.center is changing.
|
||||
positions = series.center || series.getCenter();
|
||||
['minPointSize', 'maxPointSize'].forEach(function (prop) {
|
||||
var length = seriesOptions[prop], isPercent = /%$/.test(length);
|
||||
length = parseInt(length, 10);
|
||||
extremes[prop] = isPercent ?
|
||||
smallestSize * length / 100 :
|
||||
length * 2; // Because it should be radius, not diameter.
|
||||
});
|
||||
series.minPxSize = positions[3] + extremes.minPointSize;
|
||||
series.maxPxSize = clamp(positions[2], positions[3] + extremes.minPointSize, extremes.maxPointSize);
|
||||
if (zData.length) {
|
||||
zMin = pick(seriesOptions.zMin, arrayMin(zData.filter(series.zValEval)));
|
||||
zMax = pick(seriesOptions.zMax, arrayMax(zData.filter(series.zValEval)));
|
||||
this.getRadii(zMin, zMax, series.minPxSize, series.maxPxSize);
|
||||
}
|
||||
},
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Finding radius of series points based on their Z value and min/max Z
|
||||
* value for all series.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#getRadii
|
||||
*
|
||||
* @param {number} zMin
|
||||
* Min threshold for Z value. If point's Z value is smaller that
|
||||
* zMin, point will have the smallest possible radius.
|
||||
*
|
||||
* @param {number} zMax
|
||||
* Max threshold for Z value. If point's Z value is bigger that
|
||||
* zMax, point will have the biggest possible radius.
|
||||
*
|
||||
* @param {number} minSize
|
||||
* Minimal pixel size possible for radius.
|
||||
*
|
||||
* @param {numbner} maxSize
|
||||
* Minimal pixel size possible for radius.
|
||||
*
|
||||
* @return {void}
|
||||
*/
|
||||
getRadii: function (zMin, zMax, minSize, maxSize) {
|
||||
var i = 0, pos, zData = this.zData, len = zData.length, radii = [], options = this.options, sizeByArea = options.sizeBy !== 'radius', zRange = zMax - zMin, value, radius;
|
||||
// Calculate radius for all pie slice's based on their Z values
|
||||
for (i; i < len; i++) {
|
||||
// if zData[i] is null/undefined/string we need to take zMin for
|
||||
// smallest radius.
|
||||
value = this.zValEval(zData[i]) ? zData[i] : zMin;
|
||||
if (value <= zMin) {
|
||||
radius = minSize / 2;
|
||||
}
|
||||
else if (value >= zMax) {
|
||||
radius = maxSize / 2;
|
||||
}
|
||||
else {
|
||||
// Relative size, a number between 0 and 1
|
||||
pos = zRange > 0 ? (value - zMin) / zRange : 0.5;
|
||||
if (sizeByArea) {
|
||||
pos = Math.sqrt(pos);
|
||||
}
|
||||
radius = Math.ceil(minSize + pos * (maxSize - minSize)) / 2;
|
||||
}
|
||||
radii.push(radius);
|
||||
}
|
||||
this.radii = radii;
|
||||
},
|
||||
/* eslint-enable valid-jsdoc */
|
||||
// Extend translate by updating radius for each pie slice instead of
|
||||
// using one global radius.
|
||||
translate: function (positions) {
|
||||
this.generatePoints();
|
||||
var series = this, cumulative = 0, precision = 1000, // issue #172
|
||||
options = series.options, slicedOffset = options.slicedOffset, connectorOffset = slicedOffset + (options.borderWidth || 0), finalConnectorOffset, start, end, angle, startAngle = options.startAngle || 0, startAngleRad = Math.PI / 180 * (startAngle - 90), endAngleRad = Math.PI / 180 * (pick(options.endAngle, startAngle + 360) - 90), circ = endAngleRad - startAngleRad, // 2 * Math.PI,
|
||||
points = series.points,
|
||||
// the x component of the radius vector for a given point
|
||||
radiusX, radiusY, labelDistance = options.dataLabels.distance, ignoreHiddenPoint = options.ignoreHiddenPoint, i, len = points.length, point, pointRadii, pointRadiusX, pointRadiusY;
|
||||
series.startAngleRad = startAngleRad;
|
||||
series.endAngleRad = endAngleRad;
|
||||
// Use calculateExtremes to get series.radii array.
|
||||
series.calculateExtremes();
|
||||
// Get positions - either an integer or a percentage string must be
|
||||
// given. If positions are passed as a parameter, we're in a
|
||||
// recursive loop for adjusting space for data labels.
|
||||
if (!positions) {
|
||||
series.center = positions = series.getCenter();
|
||||
}
|
||||
// Calculate the geometry for each point
|
||||
for (i = 0; i < len; i++) {
|
||||
point = points[i];
|
||||
pointRadii = series.radii[i];
|
||||
// Used for distance calculation for specific point.
|
||||
point.labelDistance = pick(point.options.dataLabels &&
|
||||
point.options.dataLabels.distance, labelDistance);
|
||||
// Saved for later dataLabels distance calculation.
|
||||
series.maxLabelDistance = Math.max(series.maxLabelDistance || 0, point.labelDistance);
|
||||
// set start and end angle
|
||||
start = startAngleRad + (cumulative * circ);
|
||||
if (!ignoreHiddenPoint || point.visible) {
|
||||
cumulative += point.percentage / 100;
|
||||
}
|
||||
end = startAngleRad + (cumulative * circ);
|
||||
// set the shape
|
||||
point.shapeType = 'arc';
|
||||
point.shapeArgs = {
|
||||
x: positions[0],
|
||||
y: positions[1],
|
||||
r: pointRadii,
|
||||
innerR: positions[3] / 2,
|
||||
start: Math.round(start * precision) / precision,
|
||||
end: Math.round(end * precision) / precision
|
||||
};
|
||||
// The angle must stay within -90 and 270 (#2645)
|
||||
angle = (end + start) / 2;
|
||||
if (angle > 1.5 * Math.PI) {
|
||||
angle -= 2 * Math.PI;
|
||||
}
|
||||
else if (angle < -Math.PI / 2) {
|
||||
angle += 2 * Math.PI;
|
||||
}
|
||||
// Center for the sliced out slice
|
||||
point.slicedTranslation = {
|
||||
translateX: Math.round(Math.cos(angle) * slicedOffset),
|
||||
translateY: Math.round(Math.sin(angle) * slicedOffset)
|
||||
};
|
||||
// set the anchor point for tooltips
|
||||
radiusX = Math.cos(angle) * positions[2] / 2;
|
||||
radiusY = Math.sin(angle) * positions[2] / 2;
|
||||
pointRadiusX = Math.cos(angle) * pointRadii;
|
||||
pointRadiusY = Math.sin(angle) * pointRadii;
|
||||
point.tooltipPos = [
|
||||
positions[0] + radiusX * 0.7,
|
||||
positions[1] + radiusY * 0.7
|
||||
];
|
||||
point.half = angle < -Math.PI / 2 || angle > Math.PI / 2 ?
|
||||
1 :
|
||||
0;
|
||||
point.angle = angle;
|
||||
// Set the anchor point for data labels. Use point.labelDistance
|
||||
// instead of labelDistance // #1174
|
||||
// finalConnectorOffset - not override connectorOffset value.
|
||||
finalConnectorOffset = Math.min(connectorOffset, point.labelDistance / 5); // #1678
|
||||
point.labelPosition = {
|
||||
natural: {
|
||||
// initial position of the data label - it's utilized
|
||||
// for finding the final position for the label
|
||||
x: positions[0] + pointRadiusX +
|
||||
Math.cos(angle) * point.labelDistance,
|
||||
y: positions[1] + pointRadiusY +
|
||||
Math.sin(angle) * point.labelDistance
|
||||
},
|
||||
'final': {
|
||||
// used for generating connector path -
|
||||
// initialized later in drawDataLabels function
|
||||
// x: undefined,
|
||||
// y: undefined
|
||||
},
|
||||
// left - pie on the left side of the data label
|
||||
// right - pie on the right side of the data label
|
||||
alignment: point.half ? 'right' : 'left',
|
||||
connectorPosition: {
|
||||
breakAt: {
|
||||
x: positions[0] + pointRadiusX +
|
||||
Math.cos(angle) * finalConnectorOffset,
|
||||
y: positions[1] + pointRadiusY +
|
||||
Math.sin(angle) * finalConnectorOffset
|
||||
},
|
||||
touchingSliceAt: {
|
||||
x: positions[0] + pointRadiusX,
|
||||
y: positions[1] + pointRadiusY
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
fireEvent(series, 'afterTranslate');
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `variablepie` series. If the [type](#series.variablepie.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.variablepie
|
||||
* @excluding dataParser, dataURL, stack, xAxis, yAxis, dataSorting,
|
||||
* boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/variable-pie.js
|
||||
* @apioption series.variablepie
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `variablepie` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 2 values. In this case, the numerical values will
|
||||
* be interpreted as `y, z` options. Example:
|
||||
* ```js
|
||||
* data: [
|
||||
* [40, 75],
|
||||
* [50, 50],
|
||||
* [60, 40]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.variablepie.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* y: 1,
|
||||
* z: 4,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* y: 7,
|
||||
* z: 10,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|*>}
|
||||
* @extends series.pie.data
|
||||
* @excluding marker, x
|
||||
* @product highcharts
|
||||
* @apioption series.variablepie.data
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,312 @@
|
||||
/* *
|
||||
*
|
||||
* Highcharts variwide module
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, isNumber = U.isNumber, pick = U.pick, seriesType = U.seriesType, wrap = U.wrap;
|
||||
import '../Series/AreaSeries.js';
|
||||
var seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.variwide
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('variwide', 'column'
|
||||
/**
|
||||
* A variwide chart (related to marimekko chart) is a column chart with a
|
||||
* variable width expressing a third dimension.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/variwide/
|
||||
* Variwide chart
|
||||
* @sample {highcharts} highcharts/series-variwide/inverted/
|
||||
* Inverted variwide chart
|
||||
* @sample {highcharts} highcharts/series-variwide/datetime/
|
||||
* Variwide columns on a datetime axis
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @excluding boostThreshold, crisp, depth, edgeColor, edgeWidth,
|
||||
* groupZPadding, boostBlending
|
||||
* @requires modules/variwide
|
||||
* @optionparent plotOptions.variwide
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* In a variwide chart, the point padding is 0 in order to express the
|
||||
* horizontal stacking of items.
|
||||
*/
|
||||
pointPadding: 0,
|
||||
/**
|
||||
* In a variwide chart, the group padding is 0 in order to express the
|
||||
* horizontal stacking of items.
|
||||
*/
|
||||
groupPadding: 0
|
||||
}, {
|
||||
irregularWidths: true,
|
||||
pointArrayMap: ['y', 'z'],
|
||||
parallelArrays: ['x', 'y', 'z'],
|
||||
processData: function (force) {
|
||||
this.totalZ = 0;
|
||||
this.relZ = [];
|
||||
seriesTypes.column.prototype.processData.call(this, force);
|
||||
(this.xAxis.reversed ?
|
||||
this.zData.slice().reverse() :
|
||||
this.zData).forEach(function (z, i) {
|
||||
this.relZ[i] = this.totalZ;
|
||||
this.totalZ += z;
|
||||
}, this);
|
||||
if (this.xAxis.categories) {
|
||||
this.xAxis.variwide = true;
|
||||
this.xAxis.zData = this.zData; // Used for label rank
|
||||
}
|
||||
return;
|
||||
},
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Translate an x value inside a given category index into the distorted
|
||||
* axis translation.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#postTranslate
|
||||
*
|
||||
* @param {number} index
|
||||
* The category index
|
||||
*
|
||||
* @param {number} x
|
||||
* The X pixel position in undistorted axis pixels
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
* For crosshairWidth for every point
|
||||
*
|
||||
* @return {number}
|
||||
* Distorted X position
|
||||
*/
|
||||
postTranslate: function (index, x, point) {
|
||||
var axis = this.xAxis, relZ = this.relZ, i = axis.reversed ? relZ.length - index : index, goRight = axis.reversed ? -1 : 1, len = axis.len, totalZ = this.totalZ, linearSlotLeft = i / relZ.length * len, linearSlotRight = (i + goRight) / relZ.length * len, slotLeft = (pick(relZ[i], totalZ) / totalZ) * len, slotRight = (pick(relZ[i + goRight], totalZ) / totalZ) * len, xInsideLinearSlot = x - linearSlotLeft, ret;
|
||||
// Set crosshairWidth for every point (#8173)
|
||||
if (point) {
|
||||
point.crosshairWidth = slotRight - slotLeft;
|
||||
}
|
||||
ret = slotLeft +
|
||||
xInsideLinearSlot * (slotRight - slotLeft) /
|
||||
(linearSlotRight - linearSlotLeft);
|
||||
return ret;
|
||||
},
|
||||
/* eslint-enable valid-jsdoc */
|
||||
// Extend translation by distoring X position based on Z.
|
||||
translate: function () {
|
||||
// Temporarily disable crisping when computing original shapeArgs
|
||||
var crispOption = this.options.crisp, xAxis = this.xAxis;
|
||||
this.options.crisp = false;
|
||||
seriesTypes.column.prototype.translate.call(this);
|
||||
// Reset option
|
||||
this.options.crisp = crispOption;
|
||||
var inverted = this.chart.inverted, crisp = this.borderWidth % 2 / 2;
|
||||
// Distort the points to reflect z dimension
|
||||
this.points.forEach(function (point, i) {
|
||||
var left, right;
|
||||
if (xAxis.variwide) {
|
||||
left = this.postTranslate(i, point.shapeArgs.x, point);
|
||||
right = this.postTranslate(i, point.shapeArgs.x +
|
||||
point.shapeArgs.width);
|
||||
// For linear or datetime axes, the variwide column should
|
||||
// start with X and extend Z units, without modifying the
|
||||
// axis.
|
||||
}
|
||||
else {
|
||||
left = point.plotX;
|
||||
right = xAxis.translate(point.x + point.z, 0, 0, 0, 1);
|
||||
}
|
||||
if (this.options.crisp) {
|
||||
left = Math.round(left) - crisp;
|
||||
right = Math.round(right) - crisp;
|
||||
}
|
||||
point.shapeArgs.x = left;
|
||||
point.shapeArgs.width = Math.max(right - left, 1);
|
||||
// Crosshair position (#8083)
|
||||
point.plotX = (left + right) / 2;
|
||||
// Adjust the tooltip position
|
||||
if (!inverted) {
|
||||
point.tooltipPos[0] =
|
||||
point.shapeArgs.x +
|
||||
point.shapeArgs.width / 2;
|
||||
}
|
||||
else {
|
||||
point.tooltipPos[1] =
|
||||
xAxis.len - point.shapeArgs.x -
|
||||
point.shapeArgs.width / 2;
|
||||
}
|
||||
}, this);
|
||||
if (this.options.stacking) {
|
||||
this.correctStackLabels();
|
||||
}
|
||||
},
|
||||
// Function that corrects stack labels positions
|
||||
correctStackLabels: function () {
|
||||
var series = this, options = series.options, yAxis = series.yAxis, pointStack, pointWidth, stack, xValue;
|
||||
series.points.forEach(function (point) {
|
||||
xValue = point.x;
|
||||
pointWidth = point.shapeArgs.width;
|
||||
stack = yAxis.stacking.stacks[(series.negStacks &&
|
||||
point.y < (options.startFromThreshold ?
|
||||
0 :
|
||||
options.threshold) ?
|
||||
'-' :
|
||||
'') + series.stackKey];
|
||||
if (stack) {
|
||||
pointStack = stack[xValue];
|
||||
if (pointStack && !point.isNull) {
|
||||
pointStack.setOffset(-(pointWidth / 2) || 0, pointWidth || 0, void 0, void 0, point.plotX);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Point functions
|
||||
}, {
|
||||
isValid: function () {
|
||||
return isNumber(this.y) && isNumber(this.z);
|
||||
}
|
||||
});
|
||||
H.Tick.prototype.postTranslate = function (xy, xOrY, index) {
|
||||
var axis = this.axis, pos = xy[xOrY] - axis.pos;
|
||||
if (!axis.horiz) {
|
||||
pos = axis.len - pos;
|
||||
}
|
||||
pos = axis.series[0].postTranslate(index, pos);
|
||||
if (!axis.horiz) {
|
||||
pos = axis.len - pos;
|
||||
}
|
||||
xy[xOrY] = axis.pos + pos;
|
||||
};
|
||||
/* eslint-disable no-invalid-this */
|
||||
// Same width as the category (#8083)
|
||||
addEvent(H.Axis, 'afterDrawCrosshair', function (e) {
|
||||
if (this.variwide && this.cross) {
|
||||
this.cross.attr('stroke-width', (e.point && e.point.crosshairWidth));
|
||||
}
|
||||
});
|
||||
// On a vertical axis, apply anti-collision logic to the labels.
|
||||
addEvent(H.Axis, 'afterRender', function () {
|
||||
var axis = this;
|
||||
if (!this.horiz && this.variwide) {
|
||||
this.chart.labelCollectors.push(function () {
|
||||
return axis.tickPositions
|
||||
.filter(function (pos) {
|
||||
return axis.ticks[pos].label;
|
||||
})
|
||||
.map(function (pos, i) {
|
||||
var label = axis.ticks[pos].label;
|
||||
label.labelrank = axis.zData[i];
|
||||
return label;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
addEvent(H.Tick, 'afterGetPosition', function (e) {
|
||||
var axis = this.axis, xOrY = axis.horiz ? 'x' : 'y';
|
||||
if (axis.variwide) {
|
||||
this[xOrY + 'Orig'] = e.pos[xOrY];
|
||||
this.postTranslate(e.pos, xOrY, this.pos);
|
||||
}
|
||||
});
|
||||
wrap(H.Tick.prototype, 'getLabelPosition', function (proceed, x, y, label, horiz, labelOptions, tickmarkOffset, index) {
|
||||
var args = Array.prototype.slice.call(arguments, 1), xy, xOrY = horiz ? 'x' : 'y';
|
||||
// Replace the x with the original x
|
||||
if (this.axis.variwide &&
|
||||
typeof this[xOrY + 'Orig'] === 'number') {
|
||||
args[horiz ? 0 : 1] = this[xOrY + 'Orig'];
|
||||
}
|
||||
xy = proceed.apply(this, args);
|
||||
// Post-translate
|
||||
if (this.axis.variwide && this.axis.categories) {
|
||||
this.postTranslate(xy, xOrY, index);
|
||||
}
|
||||
return xy;
|
||||
});
|
||||
/**
|
||||
* A `variwide` series. If the [type](#series.variwide.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.variwide
|
||||
* @excluding boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/variwide
|
||||
* @apioption series.variwide
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `variwide` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 3 or 2 values. In this case, the values correspond
|
||||
* to `x,y,z`. If the first value is a string, it is applied as the name of
|
||||
* the point, and the `x` value is inferred. The `x` value can also be
|
||||
* omitted, in which case the inner arrays should be of length 2. Then the
|
||||
* `x` value is automatically calculated, either starting at 0 and
|
||||
* incremented by 1, or from `pointStart` and `pointInterval` given in the
|
||||
* series options.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 1, 2],
|
||||
* [1, 5, 5],
|
||||
* [2, 0, 2]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.variwide.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 1,
|
||||
* z: 1,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 5,
|
||||
* z: 4,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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>|Array<(number|string),number,number>|*>}
|
||||
* @extends series.line.data
|
||||
* @excluding marker
|
||||
* @product highcharts
|
||||
* @apioption series.variwide.data
|
||||
*/
|
||||
/**
|
||||
* The relative width for each column. On a category axis, the widths are
|
||||
* distributed so they sum up to the X axis length. On linear and datetime axes,
|
||||
* the columns will be laid out from the X value and Z units along the axis.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts
|
||||
* @apioption series.variwide.data.z
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,327 @@
|
||||
/* *
|
||||
*
|
||||
* Vector plot series module
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var animObject = U.animObject, arrayMax = U.arrayMax, pick = U.pick, seriesType = U.seriesType;
|
||||
import './ScatterSeries.js';
|
||||
/**
|
||||
* The vector series class.
|
||||
*
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.vector
|
||||
*
|
||||
* @augments Highcharts.seriesTypes.scatter
|
||||
*/
|
||||
seriesType('vector', 'scatter'
|
||||
/**
|
||||
* A vector plot is a type of cartesian chart where each point has an X and
|
||||
* Y position, a length and a direction. Vectors are drawn as arrows.
|
||||
*
|
||||
* @sample {highcharts|highstock} highcharts/demo/vector-plot/
|
||||
* Vector pot
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @extends plotOptions.scatter
|
||||
* @excluding boostThreshold, marker, connectEnds, connectNulls,
|
||||
* cropThreshold, dashStyle, dragDrop, gapSize, gapUnit,
|
||||
* dataGrouping, linecap, shadow, stacking, step, jitter,
|
||||
* boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires modules/vector
|
||||
* @optionparent plotOptions.vector
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* The line width for each vector arrow.
|
||||
*/
|
||||
lineWidth: 2,
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
marker: null,
|
||||
/**
|
||||
* What part of the vector it should be rotated around. Can be one of
|
||||
* `start`, `center` and `end`. When `start`, the vectors will start
|
||||
* from the given [x, y] position, and when `end` the vectors will end
|
||||
* in the [x, y] position.
|
||||
*
|
||||
* @sample highcharts/plotoptions/vector-rotationorigin-start/
|
||||
* Rotate from start
|
||||
*
|
||||
* @validvalue ["start", "center", "end"]
|
||||
*/
|
||||
rotationOrigin: 'center',
|
||||
states: {
|
||||
hover: {
|
||||
/**
|
||||
* Additonal line width for the vector errors when they are
|
||||
* hovered.
|
||||
*/
|
||||
lineWidthPlus: 1
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
/**
|
||||
* @default [{point.x}, {point.y}] Length: {point.length} Direction: {point.direction}°
|
||||
*/
|
||||
pointFormat: '<b>[{point.x}, {point.y}]</b><br/>Length: <b>{point.length}</b><br/>Direction: <b>{point.direction}\u00B0</b><br/>'
|
||||
},
|
||||
/**
|
||||
* Maximum length of the arrows in the vector plot. The individual arrow
|
||||
* length is computed between 0 and this value.
|
||||
*/
|
||||
vectorLength: 20
|
||||
}, {
|
||||
pointArrayMap: ['y', 'length', 'direction'],
|
||||
parallelArrays: ['x', 'y', 'length', 'direction'],
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* Get presentational attributes.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.vector#pointAttribs
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
*
|
||||
* @param {string} [state]
|
||||
*
|
||||
* @return {Highcharts.SVGAttributes}
|
||||
*/
|
||||
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
|
||||
};
|
||||
},
|
||||
/**
|
||||
* @ignore
|
||||
* @deprecated
|
||||
* @function Highcharts.seriesTypes.vector#markerAttribs
|
||||
*/
|
||||
markerAttribs: H.noop,
|
||||
/**
|
||||
* @ignore
|
||||
* @deprecated
|
||||
* @function Highcharts.seriesTypes.vector#getSymbol
|
||||
*/
|
||||
getSymbol: H.noop,
|
||||
/**
|
||||
* Create a single arrow. It is later rotated around the zero
|
||||
* centerpoint.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.vector#arrow
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
*
|
||||
* @return {Highcharts.SVGPathArray}
|
||||
*/
|
||||
arrow: function (point) {
|
||||
var path, fraction = point.length / this.lengthMax, u = fraction * this.options.vectorLength / 20, o = {
|
||||
start: 10 * u,
|
||||
center: 0,
|
||||
end: -10 * u
|
||||
}[this.options.rotationOrigin] || 0;
|
||||
// The stem and the arrow head. Draw the arrow first with rotation
|
||||
// 0, which is the arrow pointing down (vector from north to south).
|
||||
path = [
|
||||
['M', 0, 7 * u + o],
|
||||
['L', -1.5 * u, 7 * u + o],
|
||||
['L', 0, 10 * u + o],
|
||||
['L', 1.5 * u, 7 * u + o],
|
||||
['L', 0, 7 * u + o],
|
||||
['L', 0, -10 * u + o] // top
|
||||
];
|
||||
return path;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.vector#translate
|
||||
*/
|
||||
translate: function () {
|
||||
H.Series.prototype.translate.call(this);
|
||||
this.lengthMax = arrayMax(this.lengthData);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.vector#drawPoints
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var chart = this.chart;
|
||||
this.points.forEach(function (point) {
|
||||
var plotX = point.plotX, plotY = point.plotY;
|
||||
if (this.options.clip === false ||
|
||||
chart.isInsidePlot(plotX, plotY, chart.inverted)) {
|
||||
if (!point.graphic) {
|
||||
point.graphic = this.chart.renderer
|
||||
.path()
|
||||
.add(this.markerGroup)
|
||||
.addClass('highcharts-point ' +
|
||||
'highcharts-color-' +
|
||||
pick(point.colorIndex, point.series.colorIndex));
|
||||
}
|
||||
point.graphic
|
||||
.attr({
|
||||
d: this.arrow(point),
|
||||
translateX: plotX,
|
||||
translateY: plotY,
|
||||
rotation: point.direction
|
||||
});
|
||||
if (!this.chart.styledMode) {
|
||||
point.graphic
|
||||
.attr(this.pointAttribs(point));
|
||||
}
|
||||
}
|
||||
else if (point.graphic) {
|
||||
point.graphic = point.graphic.destroy();
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
/**
|
||||
* @ignore
|
||||
* @deprecated
|
||||
* @function Highcharts.seriesTypes.vector#drawGraph
|
||||
*/
|
||||
drawGraph: H.noop,
|
||||
/*
|
||||
drawLegendSymbol: function (legend, item) {
|
||||
var options = legend.options,
|
||||
symbolHeight = legend.symbolHeight,
|
||||
square = options.squareSymbol,
|
||||
symbolWidth = square ? symbolHeight : legend.symbolWidth,
|
||||
path = this.arrow.call({
|
||||
lengthMax: 1,
|
||||
options: {
|
||||
vectorLength: symbolWidth
|
||||
}
|
||||
}, {
|
||||
length: 1
|
||||
});
|
||||
|
||||
item.legendLine = this.chart.renderer.path(path)
|
||||
.addClass('highcharts-point')
|
||||
.attr({
|
||||
zIndex: 3,
|
||||
translateY: symbolWidth / 2,
|
||||
rotation: 270,
|
||||
'stroke-width': 1,
|
||||
'stroke': 'black'
|
||||
}).add(item.legendGroup);
|
||||
|
||||
},
|
||||
*/
|
||||
/**
|
||||
* Fade in the arrows on initializing series.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.seriesTypes.vector#animate
|
||||
*
|
||||
* @param {boolean} [init]
|
||||
*/
|
||||
animate: function (init) {
|
||||
if (init) {
|
||||
this.markerGroup.attr({
|
||||
opacity: 0.01
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.markerGroup.animate({
|
||||
opacity: 1
|
||||
}, animObject(this.options.animation));
|
||||
}
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* A `vector` series. If the [type](#series.vector.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.vector
|
||||
* @excluding dataParser, dataURL, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires modules/vector
|
||||
* @apioption series.vector
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `vector` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 4 values. In this case, the values correspond to
|
||||
* to `x,y,length,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: [
|
||||
* [0, 0, 10, 90],
|
||||
* [0, 1, 5, 180],
|
||||
* [1, 1, 2, 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: 0,
|
||||
* y: 0,
|
||||
* name: "Point2",
|
||||
* length: 10,
|
||||
* direction: 90
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 1,
|
||||
* name: "Point1",
|
||||
* direction: 270
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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,number>|*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock
|
||||
* @apioption series.vector.data
|
||||
*/
|
||||
/**
|
||||
* The length of the vector. The rendered length will relate to the
|
||||
* `vectorLength` setting.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock
|
||||
* @apioption series.vector.data.length
|
||||
*/
|
||||
/**
|
||||
* The vector direction in degrees, where 0 is north (pointing towards south).
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock
|
||||
* @apioption series.vector.data.direction
|
||||
*/
|
||||
''; // adds doclets above to the transpiled file
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,811 @@
|
||||
/* *
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Axis from '../Core/Axis/Axis.js';
|
||||
import Chart from '../Core/Chart/Chart.js';
|
||||
import H from '../Core/Globals.js';
|
||||
import Point from '../Core/Series/Point.js';
|
||||
import StackItem from '../Extensions/Stacking.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, arrayMax = U.arrayMax, arrayMin = U.arrayMin, correctFloat = U.correctFloat, isNumber = U.isNumber, objectEach = U.objectEach, pick = U.pick, seriesType = U.seriesType;
|
||||
import '../Core/Options.js';
|
||||
import '../Core/Series/Series.js';
|
||||
var Series = H.Series, seriesTypes = H.seriesTypes;
|
||||
/**
|
||||
* Returns true if the key is a direct property of the object.
|
||||
* @private
|
||||
* @param {*} obj - Object with property to test
|
||||
* @param {string} key - Property key to test
|
||||
* @return {boolean} - Whether it is a direct property
|
||||
*/
|
||||
function ownProp(obj, key) {
|
||||
return Object.hasOwnProperty.call(obj, key);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var WaterfallAxis;
|
||||
(function (WaterfallAxis) {
|
||||
/* *
|
||||
*
|
||||
* Interfaces
|
||||
*
|
||||
* */
|
||||
/* *
|
||||
*
|
||||
* Classes
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
var Composition = /** @class */ (function () {
|
||||
/* *
|
||||
*
|
||||
* Constructors
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function Composition(axis) {
|
||||
this.axis = axis;
|
||||
this.stacks = {
|
||||
changed: false
|
||||
};
|
||||
}
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* Calls StackItem.prototype.render function that creates and renders
|
||||
* stack total label for each waterfall stack item.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Axis#renderWaterfallStackTotals
|
||||
*/
|
||||
Composition.prototype.renderStackTotals = function () {
|
||||
var yAxis = this.axis, waterfallStacks = yAxis.waterfall.stacks, stackTotalGroup = yAxis.stacking && yAxis.stacking.stackTotalGroup, dummyStackItem = new StackItem(yAxis, yAxis.options.stackLabels, false, 0, void 0);
|
||||
this.dummyStackItem = dummyStackItem;
|
||||
// Render each waterfall stack total
|
||||
objectEach(waterfallStacks, function (type) {
|
||||
objectEach(type, function (stackItem) {
|
||||
dummyStackItem.total = stackItem.stackTotal;
|
||||
if (stackItem.label) {
|
||||
dummyStackItem.label = stackItem.label;
|
||||
}
|
||||
StackItem.prototype.render.call(dummyStackItem, stackTotalGroup);
|
||||
stackItem.label = dummyStackItem.label;
|
||||
delete dummyStackItem.label;
|
||||
});
|
||||
});
|
||||
dummyStackItem.total = null;
|
||||
};
|
||||
return Composition;
|
||||
}());
|
||||
WaterfallAxis.Composition = Composition;
|
||||
/* *
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
* */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function compose(AxisClass, ChartClass) {
|
||||
addEvent(AxisClass, 'init', onInit);
|
||||
addEvent(AxisClass, 'afterBuildStacks', onAfterBuildStacks);
|
||||
addEvent(AxisClass, 'afterRender', onAfterRender);
|
||||
addEvent(ChartClass, 'beforeRedraw', onBeforeRedraw);
|
||||
}
|
||||
WaterfallAxis.compose = compose;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function onAfterBuildStacks() {
|
||||
var axis = this;
|
||||
var stacks = axis.waterfall.stacks;
|
||||
if (stacks) {
|
||||
stacks.changed = false;
|
||||
delete stacks.alreadyChanged;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function onAfterRender() {
|
||||
var axis = this;
|
||||
var stackLabelOptions = axis.options.stackLabels;
|
||||
if (stackLabelOptions && stackLabelOptions.enabled &&
|
||||
axis.waterfall.stacks) {
|
||||
axis.waterfall.renderStackTotals();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function onBeforeRedraw() {
|
||||
var axes = this.axes, series = this.series, i = series.length;
|
||||
while (i--) {
|
||||
if (series[i].options.stacking) {
|
||||
axes.forEach(function (axis) {
|
||||
if (!axis.isXAxis) {
|
||||
axis.waterfall.stacks.changed = true;
|
||||
}
|
||||
});
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function onInit() {
|
||||
var axis = this;
|
||||
if (!axis.waterfall) {
|
||||
axis.waterfall = new Composition(axis);
|
||||
}
|
||||
}
|
||||
})(WaterfallAxis || (WaterfallAxis = {}));
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* A waterfall chart displays sequentially introduced positive or negative
|
||||
* values in cumulative columns.
|
||||
*
|
||||
* @sample highcharts/demo/waterfall/
|
||||
* Waterfall chart
|
||||
* @sample highcharts/plotoptions/waterfall-inverted/
|
||||
* Horizontal (inverted) waterfall
|
||||
* @sample highcharts/plotoptions/waterfall-stacked/
|
||||
* Stacked waterfall chart
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @excluding boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-more
|
||||
* @optionparent plotOptions.waterfall
|
||||
*/
|
||||
seriesType('waterfall', 'column', {
|
||||
/**
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @apioption plotOptions.waterfall.color
|
||||
*/
|
||||
/**
|
||||
* The color used specifically for positive point columns. When not
|
||||
* specified, the general series color is used.
|
||||
*
|
||||
* In styled mode, the waterfall colors can be set with the
|
||||
* `.highcharts-point-negative`, `.highcharts-sum` and
|
||||
* `.highcharts-intermediate-sum` classes.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/waterfall/
|
||||
* Waterfall
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highcharts
|
||||
* @apioption plotOptions.waterfall.upColor
|
||||
*/
|
||||
dataLabels: {
|
||||
inside: true
|
||||
},
|
||||
/**
|
||||
* The width of the line connecting waterfall columns.
|
||||
*
|
||||
* @product highcharts
|
||||
*/
|
||||
lineWidth: 1,
|
||||
/**
|
||||
* The color of the line that connects columns in a waterfall series.
|
||||
*
|
||||
* In styled mode, the stroke can be set with the `.highcharts-graph` class.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
lineColor: '#333333',
|
||||
/**
|
||||
* A name for the dash style to use for the line connecting the columns
|
||||
* of the waterfall series. Possible values: Dash, DashDot, Dot, LongDash,
|
||||
* LongDashDot, LongDashDotDot, ShortDash, ShortDashDot, ShortDashDotDot,
|
||||
* ShortDot, Solid
|
||||
*
|
||||
* In styled mode, the stroke dash-array can be set with the
|
||||
* `.highcharts-graph` class.
|
||||
*
|
||||
* @type {Highcharts.DashStyleValue}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
dashStyle: 'Dot',
|
||||
/**
|
||||
* The color of the border of each waterfall column.
|
||||
*
|
||||
* In styled mode, the border stroke can be set with the
|
||||
* `.highcharts-point` class.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @since 3.0
|
||||
* @product highcharts
|
||||
*/
|
||||
borderColor: '#333333',
|
||||
states: {
|
||||
hover: {
|
||||
lineWidthPlus: 0 // #3126
|
||||
}
|
||||
}
|
||||
// Prototype members
|
||||
}, {
|
||||
pointValKey: 'y',
|
||||
// Property needed to prevent lines between the columns from disappearing
|
||||
// when negativeColor is used.
|
||||
showLine: true,
|
||||
// After generating points, set y-values for all sums.
|
||||
generatePoints: function () {
|
||||
var point, len, i, y;
|
||||
// Parent call:
|
||||
seriesTypes.column.prototype.generatePoints.apply(this);
|
||||
for (i = 0, len = this.points.length; i < len; i++) {
|
||||
point = this.points[i];
|
||||
y = this.processedYData[i];
|
||||
// override point value for sums
|
||||
// #3710 Update point does not propagate to sum
|
||||
if (point.isIntermediateSum || point.isSum) {
|
||||
point.y = correctFloat(y);
|
||||
}
|
||||
}
|
||||
},
|
||||
// Translate data points from raw values
|
||||
translate: function () {
|
||||
var series = this, options = series.options, yAxis = series.yAxis, len, i, points, point, shapeArgs, y, yValue, previousY, previousIntermediate, range, minPointLength = pick(options.minPointLength, 5), halfMinPointLength = minPointLength / 2, threshold = options.threshold, stacking = options.stacking, tooltipY, actualStack = yAxis.waterfall.stacks[series.stackKey], actualStackX, dummyStackItem, total, pointY, yPos, hPos;
|
||||
// run column series translate
|
||||
seriesTypes.column.prototype.translate.apply(series);
|
||||
previousY = previousIntermediate = threshold;
|
||||
points = series.points;
|
||||
for (i = 0, len = points.length; i < len; i++) {
|
||||
// cache current point object
|
||||
point = points[i];
|
||||
yValue = series.processedYData[i];
|
||||
shapeArgs = point.shapeArgs;
|
||||
range = [0, yValue];
|
||||
pointY = point.y;
|
||||
// code responsible for correct positions of stacked points
|
||||
// starts here
|
||||
if (stacking) {
|
||||
if (actualStack) {
|
||||
actualStackX = actualStack[i];
|
||||
if (stacking === 'overlap') {
|
||||
total =
|
||||
actualStackX.stackState[actualStackX.stateIndex--];
|
||||
y = pointY >= 0 ? total : total - pointY;
|
||||
if (ownProp(actualStackX, 'absolutePos')) {
|
||||
delete actualStackX.absolutePos;
|
||||
}
|
||||
if (ownProp(actualStackX, 'absoluteNeg')) {
|
||||
delete actualStackX.absoluteNeg;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pointY >= 0) {
|
||||
total = actualStackX.threshold +
|
||||
actualStackX.posTotal;
|
||||
actualStackX.posTotal -= pointY;
|
||||
y = total;
|
||||
}
|
||||
else {
|
||||
total = actualStackX.threshold +
|
||||
actualStackX.negTotal;
|
||||
actualStackX.negTotal -= pointY;
|
||||
y = total - pointY;
|
||||
}
|
||||
if (!actualStackX.posTotal) {
|
||||
if (ownProp(actualStackX, 'absolutePos')) {
|
||||
actualStackX.posTotal =
|
||||
actualStackX.absolutePos;
|
||||
delete actualStackX.absolutePos;
|
||||
}
|
||||
}
|
||||
if (!actualStackX.negTotal) {
|
||||
if (ownProp(actualStackX, 'absoluteNeg')) {
|
||||
actualStackX.negTotal =
|
||||
actualStackX.absoluteNeg;
|
||||
delete actualStackX.absoluteNeg;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!point.isSum) {
|
||||
// the connectorThreshold property is later used in
|
||||
// getCrispPath function to draw a connector line in a
|
||||
// correct place
|
||||
actualStackX.connectorThreshold =
|
||||
actualStackX.threshold + actualStackX.stackTotal;
|
||||
}
|
||||
if (yAxis.reversed) {
|
||||
yPos = (pointY >= 0) ? (y - pointY) : (y + pointY);
|
||||
hPos = y;
|
||||
}
|
||||
else {
|
||||
yPos = y;
|
||||
hPos = y - pointY;
|
||||
}
|
||||
point.below = yPos <= pick(threshold, 0);
|
||||
shapeArgs.y = yAxis.translate(yPos, 0, 1, 0, 1);
|
||||
shapeArgs.height = Math.abs(shapeArgs.y -
|
||||
yAxis.translate(hPos, 0, 1, 0, 1));
|
||||
}
|
||||
dummyStackItem = yAxis.waterfall.dummyStackItem;
|
||||
if (dummyStackItem) {
|
||||
dummyStackItem.x = i;
|
||||
dummyStackItem.label = actualStack[i].label;
|
||||
dummyStackItem.setOffset(series.pointXOffset || 0, series.barW || 0, series.stackedYNeg[i], series.stackedYPos[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// up points
|
||||
y =
|
||||
Math.max(previousY, previousY + pointY) + range[0];
|
||||
shapeArgs.y =
|
||||
yAxis.translate(y, 0, 1, 0, 1);
|
||||
// sum points
|
||||
if (point.isSum) {
|
||||
shapeArgs.y = yAxis.translate(range[1], 0, 1, 0, 1);
|
||||
shapeArgs.height = Math.min(yAxis.translate(range[0], 0, 1, 0, 1), yAxis.len) - shapeArgs.y; // #4256
|
||||
}
|
||||
else if (point.isIntermediateSum) {
|
||||
if (pointY >= 0) {
|
||||
yPos = range[1] + previousIntermediate;
|
||||
hPos = previousIntermediate;
|
||||
}
|
||||
else {
|
||||
yPos = previousIntermediate;
|
||||
hPos = range[1] + previousIntermediate;
|
||||
}
|
||||
if (yAxis.reversed) {
|
||||
// swapping values
|
||||
yPos ^= hPos;
|
||||
hPos ^= yPos;
|
||||
yPos ^= hPos;
|
||||
}
|
||||
shapeArgs.y = yAxis.translate(yPos, 0, 1, 0, 1);
|
||||
shapeArgs.height = Math.abs(shapeArgs.y -
|
||||
Math.min(yAxis.translate(hPos, 0, 1, 0, 1), yAxis.len));
|
||||
previousIntermediate += range[1];
|
||||
// If it's not the sum point, update previous stack end position
|
||||
// and get shape height (#3886)
|
||||
}
|
||||
else {
|
||||
shapeArgs.height = yValue > 0 ?
|
||||
yAxis.translate(previousY, 0, 1, 0, 1) - shapeArgs.y :
|
||||
yAxis.translate(previousY, 0, 1, 0, 1) - yAxis.translate(previousY - yValue, 0, 1, 0, 1);
|
||||
previousY += yValue;
|
||||
point.below = previousY < pick(threshold, 0);
|
||||
}
|
||||
// #3952 Negative sum or intermediate sum not rendered correctly
|
||||
if (shapeArgs.height < 0) {
|
||||
shapeArgs.y += shapeArgs.height;
|
||||
shapeArgs.height *= -1;
|
||||
}
|
||||
}
|
||||
point.plotY = shapeArgs.y =
|
||||
Math.round(shapeArgs.y) - (series.borderWidth % 2) / 2;
|
||||
// #3151
|
||||
shapeArgs.height =
|
||||
Math.max(Math.round(shapeArgs.height), 0.001);
|
||||
point.yBottom = shapeArgs.y + shapeArgs.height;
|
||||
if (shapeArgs.height <= minPointLength && !point.isNull) {
|
||||
shapeArgs.height = minPointLength;
|
||||
shapeArgs.y -= halfMinPointLength;
|
||||
point.plotY = shapeArgs.y;
|
||||
if (point.y < 0) {
|
||||
point.minPointLengthOffset = -halfMinPointLength;
|
||||
}
|
||||
else {
|
||||
point.minPointLengthOffset = halfMinPointLength;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (point.isNull) {
|
||||
shapeArgs.width = 0;
|
||||
}
|
||||
point.minPointLengthOffset = 0;
|
||||
}
|
||||
// Correct tooltip placement (#3014)
|
||||
tooltipY =
|
||||
point.plotY + (point.negative ? shapeArgs.height : 0);
|
||||
if (series.chart.inverted) {
|
||||
point.tooltipPos[0] = yAxis.len - tooltipY;
|
||||
}
|
||||
else {
|
||||
point.tooltipPos[1] = tooltipY;
|
||||
}
|
||||
}
|
||||
},
|
||||
// Call default processData then override yData to reflect waterfall's
|
||||
// extremes on yAxis
|
||||
processData: function (force) {
|
||||
var series = this, options = series.options, yData = series.yData,
|
||||
// #3710 Update point does not propagate to sum
|
||||
points = options.data, point, dataLength = yData.length, threshold = options.threshold || 0, subSum, sum, dataMin, dataMax, y, i;
|
||||
sum = subSum = dataMin = dataMax = 0;
|
||||
for (i = 0; i < dataLength; i++) {
|
||||
y = yData[i];
|
||||
point = points && points[i] ? points[i] : {};
|
||||
if (y === 'sum' || point.isSum) {
|
||||
yData[i] = correctFloat(sum);
|
||||
}
|
||||
else if (y === 'intermediateSum' ||
|
||||
point.isIntermediateSum) {
|
||||
yData[i] = correctFloat(subSum);
|
||||
subSum = 0;
|
||||
}
|
||||
else {
|
||||
sum += y;
|
||||
subSum += y;
|
||||
}
|
||||
dataMin = Math.min(sum, dataMin);
|
||||
dataMax = Math.max(sum, dataMax);
|
||||
}
|
||||
Series.prototype.processData.call(this, force);
|
||||
// Record extremes only if stacking was not set:
|
||||
if (!options.stacking) {
|
||||
series.dataMin = dataMin + threshold;
|
||||
series.dataMax = dataMax;
|
||||
}
|
||||
return;
|
||||
},
|
||||
// Return y value or string if point is sum
|
||||
toYData: function (pt) {
|
||||
if (pt.isSum) {
|
||||
return 'sum';
|
||||
}
|
||||
if (pt.isIntermediateSum) {
|
||||
return 'intermediateSum';
|
||||
}
|
||||
return pt.y;
|
||||
},
|
||||
updateParallelArrays: function (point, i) {
|
||||
Series.prototype.updateParallelArrays.call(this, point, i);
|
||||
// Prevent initial sums from triggering an error (#3245, #7559)
|
||||
if (this.yData[0] === 'sum' || this.yData[0] === 'intermediateSum') {
|
||||
this.yData[0] = null;
|
||||
}
|
||||
},
|
||||
// Postprocess mapping between options and SVG attributes
|
||||
pointAttribs: function (point, state) {
|
||||
var upColor = this.options.upColor, attr;
|
||||
// Set or reset up color (#3710, update to negative)
|
||||
if (upColor && !point.options.color) {
|
||||
point.color = point.y > 0 ? upColor : null;
|
||||
}
|
||||
attr = seriesTypes.column.prototype.pointAttribs.call(this, point, state);
|
||||
// The dashStyle option in waterfall applies to the graph, not
|
||||
// the points
|
||||
delete attr.dashstyle;
|
||||
return attr;
|
||||
},
|
||||
// Return an empty path initially, because we need to know the stroke-width
|
||||
// in order to set the final path.
|
||||
getGraphPath: function () {
|
||||
return [['M', 0, 0]];
|
||||
},
|
||||
// Draw columns' connector lines
|
||||
getCrispPath: function () {
|
||||
var data = this.data, yAxis = this.yAxis, length = data.length, graphNormalizer = Math.round(this.graph.strokeWidth()) % 2 / 2, borderNormalizer = Math.round(this.borderWidth) % 2 / 2, reversedXAxis = this.xAxis.reversed, reversedYAxis = this.yAxis.reversed, stacking = this.options.stacking, path = [], connectorThreshold, prevStack, prevStackX, prevPoint, yPos, isPos, prevArgs, pointArgs, i;
|
||||
for (i = 1; i < length; i++) {
|
||||
pointArgs = data[i].shapeArgs;
|
||||
prevPoint = data[i - 1];
|
||||
prevArgs = data[i - 1].shapeArgs;
|
||||
prevStack = yAxis.waterfall.stacks[this.stackKey];
|
||||
isPos = prevPoint.y > 0 ? -prevArgs.height : 0;
|
||||
if (prevStack && prevArgs && pointArgs) {
|
||||
prevStackX = prevStack[i - 1];
|
||||
// y position of the connector is different when series are
|
||||
// stacked, yAxis is reversed and it also depends on point's
|
||||
// value
|
||||
if (stacking) {
|
||||
connectorThreshold = prevStackX.connectorThreshold;
|
||||
yPos = Math.round((yAxis.translate(connectorThreshold, 0, 1, 0, 1) +
|
||||
(reversedYAxis ? isPos : 0))) - graphNormalizer;
|
||||
}
|
||||
else {
|
||||
yPos =
|
||||
prevArgs.y + prevPoint.minPointLengthOffset +
|
||||
borderNormalizer - graphNormalizer;
|
||||
}
|
||||
path.push([
|
||||
'M',
|
||||
(prevArgs.x || 0) + (reversedXAxis ?
|
||||
0 :
|
||||
(prevArgs.width || 0)),
|
||||
yPos
|
||||
], [
|
||||
'L',
|
||||
(pointArgs.x || 0) + (reversedXAxis ?
|
||||
(pointArgs.width || 0) :
|
||||
0),
|
||||
yPos
|
||||
]);
|
||||
}
|
||||
if (!stacking &&
|
||||
path.length &&
|
||||
prevArgs &&
|
||||
((prevPoint.y < 0 && !reversedYAxis) ||
|
||||
(prevPoint.y > 0 && reversedYAxis))) {
|
||||
path[path.length - 2][2] += prevArgs.height;
|
||||
path[path.length - 1][2] += prevArgs.height;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
},
|
||||
// The graph is initially drawn with an empty definition, then updated with
|
||||
// crisp rendering.
|
||||
drawGraph: function () {
|
||||
Series.prototype.drawGraph.call(this);
|
||||
this.graph.attr({
|
||||
d: this.getCrispPath()
|
||||
});
|
||||
},
|
||||
// Waterfall has stacking along the x-values too.
|
||||
setStackedPoints: function () {
|
||||
var series = this, options = series.options, waterfallStacks = series.yAxis.waterfall.stacks, seriesThreshold = options.threshold, stackThreshold = seriesThreshold || 0, interSum = stackThreshold, stackKey = series.stackKey, xData = series.xData, xLength = xData.length, actualStack, actualStackX, totalYVal, actualSum, prevSum, statesLen, posTotal, negTotal, xPoint, yVal, x, alreadyChanged, changed;
|
||||
// function responsible for calculating correct values for stackState
|
||||
// array of each stack item. The arguments are: firstS - the value for
|
||||
// the first state, nextS - the difference between the previous and the
|
||||
// newest state, sInx - counter used in the for that updates each state
|
||||
// when necessary, sOff - offset that must be added to each state when
|
||||
// they need to be updated (if point isn't a total sum)
|
||||
// eslint-disable-next-line require-jsdoc
|
||||
function calculateStackState(firstS, nextS, sInx, sOff) {
|
||||
if (!statesLen) {
|
||||
actualStackX.stackState[0] = firstS;
|
||||
statesLen = actualStackX.stackState.length;
|
||||
}
|
||||
else {
|
||||
for (sInx; sInx < statesLen; sInx++) {
|
||||
actualStackX.stackState[sInx] += sOff;
|
||||
}
|
||||
}
|
||||
actualStackX.stackState.push(actualStackX.stackState[statesLen - 1] + nextS);
|
||||
}
|
||||
series.yAxis.stacking.usePercentage = false;
|
||||
totalYVal = actualSum = prevSum = stackThreshold;
|
||||
// code responsible for creating stacks for waterfall series
|
||||
if (series.visible ||
|
||||
!series.chart.options.chart.ignoreHiddenSeries) {
|
||||
changed = waterfallStacks.changed;
|
||||
alreadyChanged = waterfallStacks.alreadyChanged;
|
||||
// in case of a redraw, stack for each x value must be
|
||||
// emptied (only for the first series in a specific stack)
|
||||
// and recalculated once more
|
||||
if (alreadyChanged &&
|
||||
alreadyChanged.indexOf(stackKey) < 0) {
|
||||
changed = true;
|
||||
}
|
||||
if (!waterfallStacks[stackKey]) {
|
||||
waterfallStacks[stackKey] = {};
|
||||
}
|
||||
actualStack = waterfallStacks[stackKey];
|
||||
for (var i = 0; i < xLength; i++) {
|
||||
x = xData[i];
|
||||
if (!actualStack[x] || changed) {
|
||||
actualStack[x] = {
|
||||
negTotal: 0,
|
||||
posTotal: 0,
|
||||
stackTotal: 0,
|
||||
threshold: 0,
|
||||
stateIndex: 0,
|
||||
stackState: [],
|
||||
label: ((changed &&
|
||||
actualStack[x]) ?
|
||||
actualStack[x].label :
|
||||
void 0)
|
||||
};
|
||||
}
|
||||
actualStackX = actualStack[x];
|
||||
yVal = series.yData[i];
|
||||
if (yVal >= 0) {
|
||||
actualStackX.posTotal += yVal;
|
||||
}
|
||||
else {
|
||||
actualStackX.negTotal += yVal;
|
||||
}
|
||||
// points do not exist yet, so raw data is used
|
||||
xPoint = options.data[i];
|
||||
posTotal = actualStackX.absolutePos =
|
||||
actualStackX.posTotal;
|
||||
negTotal = actualStackX.absoluteNeg =
|
||||
actualStackX.negTotal;
|
||||
actualStackX.stackTotal = posTotal + negTotal;
|
||||
statesLen = actualStackX.stackState.length;
|
||||
if (xPoint && xPoint.isIntermediateSum) {
|
||||
calculateStackState(prevSum, actualSum, 0, prevSum);
|
||||
prevSum = actualSum;
|
||||
actualSum = seriesThreshold;
|
||||
// swapping values
|
||||
stackThreshold ^= interSum;
|
||||
interSum ^= stackThreshold;
|
||||
stackThreshold ^= interSum;
|
||||
}
|
||||
else if (xPoint && xPoint.isSum) {
|
||||
calculateStackState(seriesThreshold, totalYVal, statesLen);
|
||||
stackThreshold = seriesThreshold;
|
||||
}
|
||||
else {
|
||||
calculateStackState(stackThreshold, yVal, 0, totalYVal);
|
||||
if (xPoint) {
|
||||
totalYVal += yVal;
|
||||
actualSum += yVal;
|
||||
}
|
||||
}
|
||||
actualStackX.stateIndex++;
|
||||
actualStackX.threshold = stackThreshold;
|
||||
stackThreshold += actualStackX.stackTotal;
|
||||
}
|
||||
waterfallStacks.changed = false;
|
||||
if (!waterfallStacks.alreadyChanged) {
|
||||
waterfallStacks.alreadyChanged = [];
|
||||
}
|
||||
waterfallStacks.alreadyChanged.push(stackKey);
|
||||
}
|
||||
},
|
||||
// Extremes for a non-stacked series are recorded in processData.
|
||||
// In case of stacking, use Series.stackedYData to calculate extremes.
|
||||
getExtremes: function () {
|
||||
var stacking = this.options.stacking, yAxis, waterfallStacks, stackedYNeg, stackedYPos;
|
||||
if (stacking) {
|
||||
yAxis = this.yAxis;
|
||||
waterfallStacks = yAxis.waterfall.stacks;
|
||||
stackedYNeg = this.stackedYNeg = [];
|
||||
stackedYPos = this.stackedYPos = [];
|
||||
// the visible y range can be different when stacking is set to
|
||||
// overlap and different when it's set to normal
|
||||
if (stacking === 'overlap') {
|
||||
objectEach(waterfallStacks[this.stackKey], function (stackX) {
|
||||
stackedYNeg.push(arrayMin(stackX.stackState));
|
||||
stackedYPos.push(arrayMax(stackX.stackState));
|
||||
});
|
||||
}
|
||||
else {
|
||||
objectEach(waterfallStacks[this.stackKey], function (stackX) {
|
||||
stackedYNeg.push(stackX.negTotal + stackX.threshold);
|
||||
stackedYPos.push(stackX.posTotal + stackX.threshold);
|
||||
});
|
||||
}
|
||||
return {
|
||||
dataMin: arrayMin(stackedYNeg),
|
||||
dataMax: arrayMax(stackedYPos)
|
||||
};
|
||||
}
|
||||
// When not stacking, data extremes have already been computed in the
|
||||
// processData function.
|
||||
return {
|
||||
dataMin: this.dataMin,
|
||||
dataMax: this.dataMax
|
||||
};
|
||||
}
|
||||
// Point members
|
||||
}, {
|
||||
getClassName: function () {
|
||||
var className = Point.prototype.getClassName.call(this);
|
||||
if (this.isSum) {
|
||||
className += ' highcharts-sum';
|
||||
}
|
||||
else if (this.isIntermediateSum) {
|
||||
className += ' highcharts-intermediate-sum';
|
||||
}
|
||||
return className;
|
||||
},
|
||||
// Pass the null test in ColumnSeries.translate.
|
||||
isValid: function () {
|
||||
return (isNumber(this.y) ||
|
||||
this.isSum ||
|
||||
Boolean(this.isIntermediateSum));
|
||||
}
|
||||
});
|
||||
/**
|
||||
* A `waterfall` series. If the [type](#series.waterfall.type) option
|
||||
* is not specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.waterfall
|
||||
* @excluding dataParser, dataURL, boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires highcharts-more
|
||||
* @apioption series.waterfall
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `waterfall` series
|
||||
* type, points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of numerical values. In this case, the numerical values will be
|
||||
* interpreted as `y` options. The `x` values will be automatically
|
||||
* calculated, either starting at 0 and incremented by 1, or from
|
||||
* `pointStart` and `pointInterval` given in the series options. If the axis
|
||||
* has categories, these will be used. Example:
|
||||
* ```js
|
||||
* data: [0, 5, 3, 5]
|
||||
* ```
|
||||
*
|
||||
* 2. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `x,y`. If the first value is a string, it is applied as the name of the
|
||||
* point, and the `x` value is inferred.
|
||||
* ```js
|
||||
* data: [
|
||||
* [0, 7],
|
||||
* [1, 8],
|
||||
* [2, 3]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 3. 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.waterfall.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: 1,
|
||||
* y: 8,
|
||||
* name: "Point2",
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: 1,
|
||||
* y: 8,
|
||||
* name: "Point1",
|
||||
* color: "#FF00FF"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @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<number|Array<(number|string),(number|null)>|null|*>}
|
||||
* @extends series.line.data
|
||||
* @excluding marker
|
||||
* @product highcharts
|
||||
* @apioption series.waterfall.data
|
||||
*/
|
||||
/**
|
||||
* When this property is true, the points acts as a summary column for
|
||||
* the values added or substracted since the last intermediate sum,
|
||||
* or since the start of the series. The `y` value is ignored.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/waterfall/
|
||||
* Waterfall
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @product highcharts
|
||||
* @apioption series.waterfall.data.isIntermediateSum
|
||||
*/
|
||||
/**
|
||||
* When this property is true, the point display the total sum across
|
||||
* the entire series. The `y` value is ignored.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/waterfall/
|
||||
* Waterfall
|
||||
*
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
* @product highcharts
|
||||
* @apioption series.waterfall.data.isSum
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
WaterfallAxis.compose(Axis, Chart);
|
||||
export default WaterfallAxis;
|
||||
@@ -0,0 +1,394 @@
|
||||
/* *
|
||||
*
|
||||
* Wind barb series module
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var animObject = U.animObject, isNumber = U.isNumber, pick = U.pick, seriesType = U.seriesType;
|
||||
import onSeriesMixin from '../Mixins/OnSeries.js';
|
||||
import './ColumnSeries.js';
|
||||
var noop = H.noop;
|
||||
// eslint-disable-next-line valid-jsdoc
|
||||
/**
|
||||
* Once off, register the windbarb approximation for data grouping. This can be
|
||||
* called anywhere (not necessarily in the translate function), but must happen
|
||||
* after the data grouping module is loaded and before the wind barb series uses
|
||||
* it.
|
||||
* @private
|
||||
*/
|
||||
function registerApproximation() {
|
||||
if (H.approximations && !H.approximations.windbarb) {
|
||||
H.approximations.windbarb = function (values, directions) {
|
||||
var vectorX = 0, vectorY = 0, i, len = values.length;
|
||||
for (i = 0; i < len; i++) {
|
||||
vectorX += values[i] * Math.cos(directions[i] * H.deg2rad);
|
||||
vectorY += values[i] * Math.sin(directions[i] * H.deg2rad);
|
||||
}
|
||||
return [
|
||||
// Wind speed
|
||||
values.reduce(function (sum, value) {
|
||||
return sum + value;
|
||||
}, 0) / values.length,
|
||||
// Wind direction
|
||||
Math.atan2(vectorY, vectorX) / H.deg2rad
|
||||
];
|
||||
};
|
||||
}
|
||||
}
|
||||
registerApproximation();
|
||||
/**
|
||||
* @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, dragDrop, gapSize, gapUnit,
|
||||
* linecap, shadow, stacking, step, boostBlending
|
||||
* @since 6.0.0
|
||||
* @product highcharts highstock
|
||||
* @requires modules/windbarb
|
||||
* @optionparent plotOptions.windbarb
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* Data grouping options for the wind barbs. In Highcharts, this
|
||||
* requires the `modules/datagrouping.js` module to be loaded. In
|
||||
* Highstock, data grouping is included.
|
||||
*
|
||||
* @sample highcharts/plotoptions/windbarb-datagrouping
|
||||
* Wind barb with data grouping
|
||||
*
|
||||
* @since 7.1.0
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
dataGrouping: {
|
||||
/**
|
||||
* Whether to enable data grouping.
|
||||
*
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
enabled: true,
|
||||
/**
|
||||
* Approximation function for the data grouping. The default
|
||||
* returns an average of wind speed and a vector average direction
|
||||
* weighted by wind speed.
|
||||
*
|
||||
* @product highcharts highstock
|
||||
*
|
||||
* @type {string|Function}
|
||||
*/
|
||||
approximation: 'windbarb',
|
||||
/**
|
||||
* The approximate data group width.
|
||||
*
|
||||
* @product highcharts highstock
|
||||
*/
|
||||
groupPixelWidth: 30
|
||||
},
|
||||
/**
|
||||
* 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,
|
||||
/**
|
||||
* @default value
|
||||
*/
|
||||
colorKey: 'value',
|
||||
/**
|
||||
* 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'],
|
||||
init: function (chart, options) {
|
||||
registerApproximation();
|
||||
H.Series.prototype.init.call(this, chart, options);
|
||||
},
|
||||
// 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;
|
||||
},
|
||||
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],
|
||||
['L', -1.5 * u, 7 * u],
|
||||
['L', 0, 10 * u],
|
||||
['L', 1.5 * u, 7 * u],
|
||||
['L', 0, 7 * u],
|
||||
['L', 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', 0, pos * u] : ['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', 0, pos * u] : ['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', 0, pos * u] : ['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 (this.options.clip === false ||
|
||||
chart.isInsidePlot(plotX, 0, false)) {
|
||||
// Create the graphic the first time
|
||||
if (!point.graphic) {
|
||||
point.graphic = this.chart.renderer
|
||||
.path()
|
||||
.add(this.markerGroup)
|
||||
.addClass('highcharts-point ' +
|
||||
'highcharts-color-' +
|
||||
pick(point.colorIndex, point.series.colorIndex));
|
||||
}
|
||||
// Position the graphic
|
||||
point.graphic
|
||||
.attr({
|
||||
d: this.windArrow(point),
|
||||
translateX: plotX + this.options.xOffset,
|
||||
translateY: plotY + this.options.yOffset,
|
||||
rotation: point.direction
|
||||
});
|
||||
if (!this.chart.styledMode) {
|
||||
point.graphic
|
||||
.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 initializing series.
|
||||
animate: function (init) {
|
||||
if (init) {
|
||||
this.markerGroup.attr({
|
||||
opacity: 0.01
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.markerGroup.animate({
|
||||
opacity: 1
|
||||
}, animObject(this.options.animation));
|
||||
}
|
||||
},
|
||||
// Don't invert the marker group (#4960)
|
||||
invertGroups: noop,
|
||||
// No data extremes for the Y axis
|
||||
getExtremes: function () { return ({}); }
|
||||
}, {
|
||||
isValid: function () {
|
||||
return 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, boostThreshold, boostBlending
|
||||
* @product highcharts highstock
|
||||
* @requires modules/windbarb
|
||||
* @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|null}
|
||||
* @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
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
@@ -0,0 +1,934 @@
|
||||
/* *
|
||||
*
|
||||
* Experimental Highcharts module which enables visualization of a word cloud.
|
||||
*
|
||||
* (c) 2016-2020 Highsoft AS
|
||||
* Authors: Jon Arild Nygard
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
* */
|
||||
'use strict';
|
||||
import H from '../Core/Globals.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var extend = U.extend, find = U.find, isArray = U.isArray, isNumber = U.isNumber, isObject = U.isObject, merge = U.merge, seriesType = U.seriesType;
|
||||
import drawPointModule from '../Mixins/DrawPoint.js';
|
||||
var drawPoint = drawPointModule.drawPoint;
|
||||
import polygonMixin from '../Mixins/Polygon.js';
|
||||
var getBoundingBoxFromPolygon = polygonMixin.getBoundingBoxFromPolygon, getPolygon = polygonMixin.getPolygon, isPolygonsColliding = polygonMixin.isPolygonsColliding, movePolygon = polygonMixin.movePolygon, rotate2DToOrigin = polygonMixin.rotate2DToOrigin, rotate2DToPoint = polygonMixin.rotate2DToPoint;
|
||||
import '../Core/Series/Series.js';
|
||||
var noop = H.noop, Series = H.Series;
|
||||
/**
|
||||
* Detects if there is a collision between two rectangles.
|
||||
*
|
||||
* @private
|
||||
* @function isRectanglesIntersecting
|
||||
*
|
||||
* @param {Highcharts.PolygonBoxObject} r1
|
||||
* First rectangle.
|
||||
*
|
||||
* @param {Highcharts.PolygonBoxObject} r2
|
||||
* Second rectangle.
|
||||
*
|
||||
* @return {boolean}
|
||||
* Returns true if the rectangles overlap.
|
||||
*/
|
||||
function isRectanglesIntersecting(r1, r2) {
|
||||
return !(r2.left > r1.right ||
|
||||
r2.right < r1.left ||
|
||||
r2.top > r1.bottom ||
|
||||
r2.bottom < r1.top);
|
||||
}
|
||||
/**
|
||||
* Detects if a word collides with any previously placed words.
|
||||
*
|
||||
* @private
|
||||
* @function intersectsAnyWord
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
* Point which the word is connected to.
|
||||
*
|
||||
* @param {Array<Highcharts.Point>} points
|
||||
* Previously placed points to check against.
|
||||
*
|
||||
* @return {boolean}
|
||||
* Returns true if there is collision.
|
||||
*/
|
||||
function intersectsAnyWord(point, points) {
|
||||
var intersects = false, rect = point.rect, polygon = point.polygon, lastCollidedWith = point.lastCollidedWith, isIntersecting = function (p) {
|
||||
var result = isRectanglesIntersecting(rect, p.rect);
|
||||
if (result &&
|
||||
(point.rotation % 90 || p.rotation % 90)) {
|
||||
result = isPolygonsColliding(polygon, p.polygon);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
// If the point has already intersected a different point, chances are they
|
||||
// are still intersecting. So as an enhancement we check this first.
|
||||
if (lastCollidedWith) {
|
||||
intersects = isIntersecting(lastCollidedWith);
|
||||
// If they no longer intersects, remove the cache from the point.
|
||||
if (!intersects) {
|
||||
delete point.lastCollidedWith;
|
||||
}
|
||||
}
|
||||
// If not already found, then check if we can find a point that is
|
||||
// intersecting.
|
||||
if (!intersects) {
|
||||
intersects = !!find(points, function (p) {
|
||||
var result = isIntersecting(p);
|
||||
if (result) {
|
||||
point.lastCollidedWith = p;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
return intersects;
|
||||
}
|
||||
/**
|
||||
* Gives a set of cordinates for an Archimedian Spiral.
|
||||
*
|
||||
* @private
|
||||
* @function archimedeanSpiral
|
||||
*
|
||||
* @param {number} attempt
|
||||
* How far along the spiral we have traversed.
|
||||
*
|
||||
* @param {Highcharts.WordcloudSpiralParamsObject} [params]
|
||||
* Additional parameters.
|
||||
*
|
||||
* @return {boolean|Highcharts.PositionObject}
|
||||
* Resulting coordinates, x and y. False if the word should be dropped from the
|
||||
* visualization.
|
||||
*/
|
||||
function archimedeanSpiral(attempt, params) {
|
||||
var field = params.field, result = false, maxDelta = (field.width * field.width) + (field.height * field.height), t = attempt * 0.8; // 0.2 * 4 = 0.8. Enlarging the spiral.
|
||||
// Emergency brake. TODO make spiralling logic more foolproof.
|
||||
if (attempt <= 10000) {
|
||||
result = {
|
||||
x: t * Math.cos(t),
|
||||
y: t * Math.sin(t)
|
||||
};
|
||||
if (!(Math.min(Math.abs(result.x), Math.abs(result.y)) < maxDelta)) {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Gives a set of cordinates for an rectangular spiral.
|
||||
*
|
||||
* @private
|
||||
* @function squareSpiral
|
||||
*
|
||||
* @param {number} attempt
|
||||
* How far along the spiral we have traversed.
|
||||
*
|
||||
* @param {Highcharts.WordcloudSpiralParamsObject} [params]
|
||||
* Additional parameters.
|
||||
*
|
||||
* @return {boolean|Highcharts.PositionObject}
|
||||
* Resulting coordinates, x and y. False if the word should be dropped from the
|
||||
* visualization.
|
||||
*/
|
||||
function squareSpiral(attempt, params) {
|
||||
var a = attempt * 4, k = Math.ceil((Math.sqrt(a) - 1) / 2), t = 2 * k + 1, m = Math.pow(t, 2), isBoolean = function (x) {
|
||||
return typeof x === 'boolean';
|
||||
}, result = false;
|
||||
t -= 1;
|
||||
if (attempt <= 10000) {
|
||||
if (isBoolean(result) && a >= m - t) {
|
||||
result = {
|
||||
x: k - (m - a),
|
||||
y: -k
|
||||
};
|
||||
}
|
||||
m -= t;
|
||||
if (isBoolean(result) && a >= m - t) {
|
||||
result = {
|
||||
x: -k,
|
||||
y: -k + (m - a)
|
||||
};
|
||||
}
|
||||
m -= t;
|
||||
if (isBoolean(result)) {
|
||||
if (a >= m - t) {
|
||||
result = {
|
||||
x: -k + (m - a),
|
||||
y: k
|
||||
};
|
||||
}
|
||||
else {
|
||||
result = {
|
||||
x: k,
|
||||
y: k - (m - a - t)
|
||||
};
|
||||
}
|
||||
}
|
||||
result.x *= 5;
|
||||
result.y *= 5;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Gives a set of cordinates for an rectangular spiral.
|
||||
*
|
||||
* @private
|
||||
* @function rectangularSpiral
|
||||
*
|
||||
* @param {number} attempt
|
||||
* How far along the spiral we have traversed.
|
||||
*
|
||||
* @param {Highcharts.WordcloudSpiralParamsObject} [params]
|
||||
* Additional parameters.
|
||||
*
|
||||
* @return {boolean|Higcharts.PositionObject}
|
||||
* Resulting coordinates, x and y. False if the word should be dropped from the
|
||||
* visualization.
|
||||
*/
|
||||
function rectangularSpiral(attempt, params) {
|
||||
var result = squareSpiral(attempt, params), field = params.field;
|
||||
if (result) {
|
||||
result.x *= field.ratioX;
|
||||
result.y *= field.ratioY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @function getRandomPosition
|
||||
*
|
||||
* @param {number} size
|
||||
* Random factor.
|
||||
*
|
||||
* @return {number}
|
||||
* Random position.
|
||||
*/
|
||||
function getRandomPosition(size) {
|
||||
return Math.round((size * (Math.random() + 0.5)) / 2);
|
||||
}
|
||||
/**
|
||||
* Calculates the proper scale to fit the cloud inside the plotting area.
|
||||
*
|
||||
* @private
|
||||
* @function getScale
|
||||
*
|
||||
* @param {number} targetWidth
|
||||
* Width of target area.
|
||||
*
|
||||
* @param {number} targetHeight
|
||||
* Height of target area.
|
||||
*
|
||||
* @param {object} field
|
||||
* The playing field.
|
||||
*
|
||||
* @param {Highcharts.Series} series
|
||||
* Series object.
|
||||
*
|
||||
* @return {number}
|
||||
* Returns the value to scale the playing field up to the size of the target
|
||||
* area.
|
||||
*/
|
||||
function getScale(targetWidth, targetHeight, field) {
|
||||
var height = Math.max(Math.abs(field.top), Math.abs(field.bottom)) * 2, width = Math.max(Math.abs(field.left), Math.abs(field.right)) * 2, scaleX = width > 0 ? 1 / width * targetWidth : 1, scaleY = height > 0 ? 1 / height * targetHeight : 1;
|
||||
return Math.min(scaleX, scaleY);
|
||||
}
|
||||
/**
|
||||
* Calculates what is called the playing field. The field is the area which all
|
||||
* the words are allowed to be positioned within. The area is proportioned to
|
||||
* match the target aspect ratio.
|
||||
*
|
||||
* @private
|
||||
* @function getPlayingField
|
||||
*
|
||||
* @param {number} targetWidth
|
||||
* Width of the target area.
|
||||
*
|
||||
* @param {number} targetHeight
|
||||
* Height of the target area.
|
||||
*
|
||||
* @param {Array<Highcharts.Point>} data
|
||||
* Array of points.
|
||||
*
|
||||
* @param {object} data.dimensions
|
||||
* The height and width of the word.
|
||||
*
|
||||
* @return {object}
|
||||
* The width and height of the playing field.
|
||||
*/
|
||||
function getPlayingField(targetWidth, targetHeight, data) {
|
||||
var info = data.reduce(function (obj, point) {
|
||||
var dimensions = point.dimensions, x = Math.max(dimensions.width, dimensions.height);
|
||||
// Find largest height.
|
||||
obj.maxHeight = Math.max(obj.maxHeight, dimensions.height);
|
||||
// Find largest width.
|
||||
obj.maxWidth = Math.max(obj.maxWidth, dimensions.width);
|
||||
// Sum up the total maximum area of all the words.
|
||||
obj.area += x * x;
|
||||
return obj;
|
||||
}, {
|
||||
maxHeight: 0,
|
||||
maxWidth: 0,
|
||||
area: 0
|
||||
}),
|
||||
/**
|
||||
* Use largest width, largest height, or root of total area to give size
|
||||
* to the playing field.
|
||||
*/
|
||||
x = Math.max(info.maxHeight, // Have enough space for the tallest word
|
||||
info.maxWidth, // Have enough space for the broadest word
|
||||
// Adjust 15% to account for close packing of words
|
||||
Math.sqrt(info.area) * 0.85), ratioX = targetWidth > targetHeight ? targetWidth / targetHeight : 1, ratioY = targetHeight > targetWidth ? targetHeight / targetWidth : 1;
|
||||
return {
|
||||
width: x * ratioX,
|
||||
height: x * ratioY,
|
||||
ratioX: ratioX,
|
||||
ratioY: ratioY
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Calculates a number of degrees to rotate, based upon a number of orientations
|
||||
* within a range from-to.
|
||||
*
|
||||
* @private
|
||||
* @function getRotation
|
||||
*
|
||||
* @param {number} [orientations]
|
||||
* Number of orientations.
|
||||
*
|
||||
* @param {number} [index]
|
||||
* Index of point, used to decide orientation.
|
||||
*
|
||||
* @param {number} [from]
|
||||
* The smallest degree of rotation.
|
||||
*
|
||||
* @param {number} [to]
|
||||
* The largest degree of rotation.
|
||||
*
|
||||
* @return {boolean|number}
|
||||
* Returns the resulting rotation for the word. Returns false if invalid input
|
||||
* parameters.
|
||||
*/
|
||||
function getRotation(orientations, index, from, to) {
|
||||
var result = false, // Default to false
|
||||
range, intervals, orientation;
|
||||
// Check if we have valid input parameters.
|
||||
if (isNumber(orientations) &&
|
||||
isNumber(index) &&
|
||||
isNumber(from) &&
|
||||
isNumber(to) &&
|
||||
orientations > 0 &&
|
||||
index > -1 &&
|
||||
to > from) {
|
||||
range = to - from;
|
||||
intervals = range / (orientations - 1 || 1);
|
||||
orientation = index % orientations;
|
||||
result = from + (orientation * intervals);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Calculates the spiral positions and store them in scope for quick access.
|
||||
*
|
||||
* @private
|
||||
* @function getSpiral
|
||||
*
|
||||
* @param {Function} fn
|
||||
* The spiral function.
|
||||
*
|
||||
* @param {object} params
|
||||
* Additional parameters for the spiral.
|
||||
*
|
||||
* @return {Function}
|
||||
* Function with access to spiral positions.
|
||||
*/
|
||||
function getSpiral(fn, params) {
|
||||
var length = 10000, i, arr = [];
|
||||
for (i = 1; i < length; i++) {
|
||||
arr.push(fn(i, params)); // @todo unnecessary amount of precaclulation
|
||||
}
|
||||
return function (attempt) {
|
||||
return attempt <= length ? arr[attempt - 1] : false;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Detects if a word is placed outside the playing field.
|
||||
*
|
||||
* @private
|
||||
* @function outsidePlayingField
|
||||
*
|
||||
* @param {Highcharts.PolygonBoxObject} rect
|
||||
* The word box.
|
||||
*
|
||||
* @param {Highcharts.WordcloudFieldObject} field
|
||||
* The width and height of the playing field.
|
||||
*
|
||||
* @return {boolean}
|
||||
* Returns true if the word is placed outside the field.
|
||||
*/
|
||||
function outsidePlayingField(rect, field) {
|
||||
var playingField = {
|
||||
left: -(field.width / 2),
|
||||
right: field.width / 2,
|
||||
top: -(field.height / 2),
|
||||
bottom: field.height / 2
|
||||
};
|
||||
return !(playingField.left < rect.left &&
|
||||
playingField.right > rect.right &&
|
||||
playingField.top < rect.top &&
|
||||
playingField.bottom > rect.bottom);
|
||||
}
|
||||
/**
|
||||
* Check if a point intersects with previously placed words, or if it goes
|
||||
* outside the field boundaries. If a collision, then try to adjusts the
|
||||
* position.
|
||||
*
|
||||
* @private
|
||||
* @function intersectionTesting
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
* Point to test for intersections.
|
||||
*
|
||||
* @param {Highcharts.WordcloudTestOptionsObject} options
|
||||
* Options object.
|
||||
*
|
||||
* @return {boolean|Highcharts.PositionObject}
|
||||
* Returns an object with how much to correct the positions. Returns false if
|
||||
* the word should not be placed at all.
|
||||
*/
|
||||
function intersectionTesting(point, options) {
|
||||
var placed = options.placed, field = options.field, rectangle = options.rectangle, polygon = options.polygon, spiral = options.spiral, attempt = 1, delta = {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
// Make a copy to update values during intersection testing.
|
||||
rect = point.rect = extend({}, rectangle);
|
||||
point.polygon = polygon;
|
||||
point.rotation = options.rotation;
|
||||
/* while w intersects any previously placed words:
|
||||
do {
|
||||
move w a little bit along a spiral path
|
||||
} while any part of w is outside the playing field and
|
||||
the spiral radius is still smallish */
|
||||
while (delta !== false &&
|
||||
(intersectsAnyWord(point, placed) ||
|
||||
outsidePlayingField(rect, field))) {
|
||||
delta = spiral(attempt);
|
||||
if (isObject(delta)) {
|
||||
// Update the DOMRect with new positions.
|
||||
rect.left = rectangle.left + delta.x;
|
||||
rect.right = rectangle.right + delta.x;
|
||||
rect.top = rectangle.top + delta.y;
|
||||
rect.bottom = rectangle.bottom + delta.y;
|
||||
point.polygon = movePolygon(delta.x, delta.y, polygon);
|
||||
}
|
||||
attempt++;
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
/**
|
||||
* Extends the playing field to have enough space to fit a given word.
|
||||
*
|
||||
* @private
|
||||
* @function extendPlayingField
|
||||
*
|
||||
* @param {Highcharts.WordcloudFieldObject} field
|
||||
* The width, height and ratios of a playing field.
|
||||
*
|
||||
* @param {Highcharts.PolygonBoxObject} rectangle
|
||||
* The bounding box of the word to add space for.
|
||||
*
|
||||
* @return {Highcharts.WordcloudFieldObject}
|
||||
* Returns the extended playing field with updated height and width.
|
||||
*/
|
||||
function extendPlayingField(field, rectangle) {
|
||||
var height, width, ratioX, ratioY, x, extendWidth, extendHeight, result;
|
||||
if (isObject(field) && isObject(rectangle)) {
|
||||
height = (rectangle.bottom - rectangle.top);
|
||||
width = (rectangle.right - rectangle.left);
|
||||
ratioX = field.ratioX;
|
||||
ratioY = field.ratioY;
|
||||
// Use the same variable to extend both the height and width.
|
||||
x = ((width * ratioX) > (height * ratioY)) ? width : height;
|
||||
// Multiply variable with ratios to preserve aspect ratio.
|
||||
extendWidth = x * ratioX;
|
||||
extendHeight = x * ratioY;
|
||||
// Calculate the size of the new field after adding space for the word.
|
||||
result = merge(field, {
|
||||
// Add space on the left and right.
|
||||
width: field.width + (extendWidth * 2),
|
||||
// Add space on the top and bottom.
|
||||
height: field.height + (extendHeight * 2)
|
||||
});
|
||||
}
|
||||
else {
|
||||
result = field;
|
||||
}
|
||||
// Return the new extended field.
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* If a rectangle is outside a give field, then the boundaries of the field is
|
||||
* adjusted accordingly. Modifies the field object which is passed as the first
|
||||
* parameter.
|
||||
*
|
||||
* @private
|
||||
* @function updateFieldBoundaries
|
||||
*
|
||||
* @param {Highcharts.WordcloudFieldObject} field
|
||||
* The bounding box of a playing field.
|
||||
*
|
||||
* @param {Highcharts.PolygonBoxObject} rectangle
|
||||
* The bounding box for a placed point.
|
||||
*
|
||||
* @return {Highcharts.WordcloudFieldObject}
|
||||
* Returns a modified field object.
|
||||
*/
|
||||
function updateFieldBoundaries(field, rectangle) {
|
||||
// @todo improve type checking.
|
||||
if (!isNumber(field.left) || field.left > rectangle.left) {
|
||||
field.left = rectangle.left;
|
||||
}
|
||||
if (!isNumber(field.right) || field.right < rectangle.right) {
|
||||
field.right = rectangle.right;
|
||||
}
|
||||
if (!isNumber(field.top) || field.top > rectangle.top) {
|
||||
field.top = rectangle.top;
|
||||
}
|
||||
if (!isNumber(field.bottom) || field.bottom < rectangle.bottom) {
|
||||
field.bottom = rectangle.bottom;
|
||||
}
|
||||
return field;
|
||||
}
|
||||
/**
|
||||
* A word cloud is a visualization of a set of words, where the size and
|
||||
* placement of a word is determined by how it is weighted.
|
||||
*
|
||||
* @sample highcharts/demo/wordcloud
|
||||
* Word Cloud chart
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @excluding allAreas, boostThreshold, clip, colorAxis, compare,
|
||||
* compareBase, crisp, cropTreshold, dataGrouping, dataLabels,
|
||||
* depth, dragDrop, edgeColor, findNearestPointBy,
|
||||
* getExtremesFromAll, grouping, groupPadding, groupZPadding,
|
||||
* joinBy, maxPointWidth, minPointLength, navigatorOptions,
|
||||
* negativeColor, pointInterval, pointIntervalUnit, pointPadding,
|
||||
* pointPlacement, pointRange, pointStart, pointWidth, pointStart,
|
||||
* pointWidth, shadow, showCheckbox, showInNavigator,
|
||||
* softThreshold, stacking, threshold, zoneAxis, zones,
|
||||
* dataSorting, boostBlending
|
||||
* @product highcharts
|
||||
* @since 6.0.0
|
||||
* @requires modules/wordcloud
|
||||
* @optionparent plotOptions.wordcloud
|
||||
*/
|
||||
var wordCloudOptions = {
|
||||
/**
|
||||
* If there is no space for a word on the playing field, then this option
|
||||
* will allow the playing field to be extended to fit the word. If false
|
||||
* then the word will be dropped from the visualization.
|
||||
*
|
||||
* NB! This option is currently not decided to be published in the API, and
|
||||
* is therefore marked as private.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
allowExtendPlayingField: true,
|
||||
animation: {
|
||||
/** @internal */
|
||||
duration: 500
|
||||
},
|
||||
borderWidth: 0,
|
||||
clip: false,
|
||||
colorByPoint: true,
|
||||
/**
|
||||
* A threshold determining the minimum font size that can be applied to a
|
||||
* word.
|
||||
*/
|
||||
minFontSize: 1,
|
||||
/**
|
||||
* The word with the largest weight will have a font size equal to this
|
||||
* value. The font size of a word is the ratio between its weight and the
|
||||
* largest occuring weight, multiplied with the value of maxFontSize.
|
||||
*/
|
||||
maxFontSize: 25,
|
||||
/**
|
||||
* This option decides which algorithm is used for placement, and rotation
|
||||
* of a word. The choice of algorith is therefore a crucial part of the
|
||||
* resulting layout of the wordcloud. It is possible for users to add their
|
||||
* own custom placement strategies for use in word cloud. Read more about it
|
||||
* in our
|
||||
* [documentation](https://www.highcharts.com/docs/chart-and-series-types/word-cloud-series#custom-placement-strategies)
|
||||
*
|
||||
* @validvalue: ["center", "random"]
|
||||
*/
|
||||
placementStrategy: 'center',
|
||||
/**
|
||||
* Rotation options for the words in the wordcloud.
|
||||
*
|
||||
* @sample highcharts/plotoptions/wordcloud-rotation
|
||||
* Word cloud with rotation
|
||||
*/
|
||||
rotation: {
|
||||
/**
|
||||
* The smallest degree of rotation for a word.
|
||||
*/
|
||||
from: 0,
|
||||
/**
|
||||
* The number of possible orientations for a word, within the range of
|
||||
* `rotation.from` and `rotation.to`. Must be a number larger than 0.
|
||||
*/
|
||||
orientations: 2,
|
||||
/**
|
||||
* The largest degree of rotation for a word.
|
||||
*/
|
||||
to: 90
|
||||
},
|
||||
showInLegend: false,
|
||||
/**
|
||||
* Spiral used for placing a word after the initial position experienced a
|
||||
* collision with either another word or the borders.
|
||||
* It is possible for users to add their own custom spiralling algorithms
|
||||
* for use in word cloud. Read more about it in our
|
||||
* [documentation](https://www.highcharts.com/docs/chart-and-series-types/word-cloud-series#custom-spiralling-algorithm)
|
||||
*
|
||||
* @validvalue: ["archimedean", "rectangular", "square"]
|
||||
*/
|
||||
spiral: 'rectangular',
|
||||
/**
|
||||
* CSS styles for the words.
|
||||
*
|
||||
* @type {Highcharts.CSSObject}
|
||||
* @default {"fontFamily":"sans-serif", "fontWeight": "900"}
|
||||
*/
|
||||
style: {
|
||||
/** @ignore-option */
|
||||
fontFamily: 'sans-serif',
|
||||
/** @ignore-option */
|
||||
fontWeight: '900',
|
||||
/** @ignore-option */
|
||||
whiteSpace: 'nowrap'
|
||||
},
|
||||
tooltip: {
|
||||
followPointer: true,
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.weight}</b><br/>'
|
||||
}
|
||||
};
|
||||
// Properties of the WordCloud series.
|
||||
var wordCloudSeries = {
|
||||
animate: Series.prototype.animate,
|
||||
animateDrilldown: noop,
|
||||
animateDrillupFrom: noop,
|
||||
setClip: noop,
|
||||
bindAxes: function () {
|
||||
var wordcloudAxis = {
|
||||
endOnTick: false,
|
||||
gridLineWidth: 0,
|
||||
lineWidth: 0,
|
||||
maxPadding: 0,
|
||||
startOnTick: false,
|
||||
title: null,
|
||||
tickPositions: []
|
||||
};
|
||||
Series.prototype.bindAxes.call(this);
|
||||
extend(this.yAxis.options, wordcloudAxis);
|
||||
extend(this.xAxis.options, wordcloudAxis);
|
||||
},
|
||||
pointAttribs: function (point, state) {
|
||||
var attribs = H.seriesTypes.column.prototype
|
||||
.pointAttribs.call(this, point, state);
|
||||
delete attribs.stroke;
|
||||
delete attribs['stroke-width'];
|
||||
return attribs;
|
||||
},
|
||||
/**
|
||||
* Calculates the fontSize of a word based on its weight.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#deriveFontSize
|
||||
*
|
||||
* @param {number} [relativeWeight=0]
|
||||
* The weight of the word, on a scale 0-1.
|
||||
*
|
||||
* @param {number} [maxFontSize=1]
|
||||
* The maximum font size of a word.
|
||||
*
|
||||
* @param {number} [minFontSize=1]
|
||||
* The minimum font size of a word.
|
||||
*
|
||||
* @return {number}
|
||||
* Returns the resulting fontSize of a word. If minFontSize is larger then
|
||||
* maxFontSize the result will equal minFontSize.
|
||||
*/
|
||||
deriveFontSize: function deriveFontSize(relativeWeight, maxFontSize, minFontSize) {
|
||||
var weight = isNumber(relativeWeight) ? relativeWeight : 0, max = isNumber(maxFontSize) ? maxFontSize : 1, min = isNumber(minFontSize) ? minFontSize : 1;
|
||||
return Math.floor(Math.max(min, weight * max));
|
||||
},
|
||||
drawPoints: function () {
|
||||
var series = this, hasRendered = series.hasRendered, xAxis = series.xAxis, yAxis = series.yAxis, chart = series.chart, group = series.group, options = series.options, animation = options.animation, allowExtendPlayingField = options.allowExtendPlayingField, renderer = chart.renderer, testElement = renderer.text().add(group), placed = [], placementStrategy = series.placementStrategy[options.placementStrategy], spiral, rotation = options.rotation, scale, weights = series.points.map(function (p) {
|
||||
return p.weight;
|
||||
}), maxWeight = Math.max.apply(null, weights),
|
||||
// concat() prevents from sorting the original array.
|
||||
data = series.points.concat().sort(function (a, b) {
|
||||
return b.weight - a.weight; // Sort descending
|
||||
}), field;
|
||||
// Reset the scale before finding the dimensions (#11993).
|
||||
// SVGGRaphicsElement.getBBox() (used in SVGElement.getBBox(boolean))
|
||||
// returns slightly different values for the same element depending on
|
||||
// whether it is rendered in a group which has already defined scale
|
||||
// (e.g. 6) or in the group without a scale (scale = 1).
|
||||
series.group.attr({
|
||||
scaleX: 1,
|
||||
scaleY: 1
|
||||
});
|
||||
// Get the dimensions for each word.
|
||||
// Used in calculating the playing field.
|
||||
data.forEach(function (point) {
|
||||
var relativeWeight = 1 / maxWeight * point.weight, fontSize = series.deriveFontSize(relativeWeight, options.maxFontSize, options.minFontSize), css = extend({
|
||||
fontSize: fontSize + 'px'
|
||||
}, options.style), bBox;
|
||||
testElement.css(css).attr({
|
||||
x: 0,
|
||||
y: 0,
|
||||
text: point.name
|
||||
});
|
||||
bBox = testElement.getBBox(true);
|
||||
point.dimensions = {
|
||||
height: bBox.height,
|
||||
width: bBox.width
|
||||
};
|
||||
});
|
||||
// Calculate the playing field.
|
||||
field = getPlayingField(xAxis.len, yAxis.len, data);
|
||||
spiral = getSpiral(series.spirals[options.spiral], {
|
||||
field: field
|
||||
});
|
||||
// Draw all the points.
|
||||
data.forEach(function (point) {
|
||||
var relativeWeight = 1 / maxWeight * point.weight, fontSize = series.deriveFontSize(relativeWeight, options.maxFontSize, options.minFontSize), css = extend({
|
||||
fontSize: fontSize + 'px'
|
||||
}, options.style), placement = placementStrategy(point, {
|
||||
data: data,
|
||||
field: field,
|
||||
placed: placed,
|
||||
rotation: rotation
|
||||
}), attr = extend(series.pointAttribs(point, (point.selected && 'select')), {
|
||||
align: 'center',
|
||||
'alignment-baseline': 'middle',
|
||||
x: placement.x,
|
||||
y: placement.y,
|
||||
text: point.name,
|
||||
rotation: placement.rotation
|
||||
}), polygon = getPolygon(placement.x, placement.y, point.dimensions.width, point.dimensions.height, placement.rotation), rectangle = getBoundingBoxFromPolygon(polygon), delta = intersectionTesting(point, {
|
||||
rectangle: rectangle,
|
||||
polygon: polygon,
|
||||
field: field,
|
||||
placed: placed,
|
||||
spiral: spiral,
|
||||
rotation: placement.rotation
|
||||
}), animate;
|
||||
// If there is no space for the word, extend the playing field.
|
||||
if (!delta && allowExtendPlayingField) {
|
||||
// Extend the playing field to fit the word.
|
||||
field = extendPlayingField(field, rectangle);
|
||||
// Run intersection testing one more time to place the word.
|
||||
delta = intersectionTesting(point, {
|
||||
rectangle: rectangle,
|
||||
polygon: polygon,
|
||||
field: field,
|
||||
placed: placed,
|
||||
spiral: spiral,
|
||||
rotation: placement.rotation
|
||||
});
|
||||
}
|
||||
// Check if point was placed, if so delete it, otherwise place it on
|
||||
// the correct positions.
|
||||
if (isObject(delta)) {
|
||||
attr.x += delta.x;
|
||||
attr.y += delta.y;
|
||||
rectangle.left += delta.x;
|
||||
rectangle.right += delta.x;
|
||||
rectangle.top += delta.y;
|
||||
rectangle.bottom += delta.y;
|
||||
field = updateFieldBoundaries(field, rectangle);
|
||||
placed.push(point);
|
||||
point.isNull = false;
|
||||
}
|
||||
else {
|
||||
point.isNull = true;
|
||||
}
|
||||
if (animation) {
|
||||
// Animate to new positions
|
||||
animate = {
|
||||
x: attr.x,
|
||||
y: attr.y
|
||||
};
|
||||
// Animate from center of chart
|
||||
if (!hasRendered) {
|
||||
attr.x = 0;
|
||||
attr.y = 0;
|
||||
// or animate from previous position
|
||||
}
|
||||
else {
|
||||
delete attr.x;
|
||||
delete attr.y;
|
||||
}
|
||||
}
|
||||
point.draw({
|
||||
animatableAttribs: animate,
|
||||
attribs: attr,
|
||||
css: css,
|
||||
group: group,
|
||||
renderer: renderer,
|
||||
shapeArgs: void 0,
|
||||
shapeType: 'text'
|
||||
});
|
||||
});
|
||||
// Destroy the element after use.
|
||||
testElement = testElement.destroy();
|
||||
// Scale the series group to fit within the plotArea.
|
||||
scale = getScale(xAxis.len, yAxis.len, field);
|
||||
series.group.attr({
|
||||
scaleX: scale,
|
||||
scaleY: scale
|
||||
});
|
||||
},
|
||||
hasData: function () {
|
||||
var series = this;
|
||||
return (isObject(series) &&
|
||||
series.visible === true &&
|
||||
isArray(series.points) &&
|
||||
series.points.length > 0);
|
||||
},
|
||||
// Strategies used for deciding rotation and initial position of a word. To
|
||||
// implement a custom strategy, have a look at the function random for
|
||||
// example.
|
||||
placementStrategy: {
|
||||
random: function (point, options) {
|
||||
var field = options.field, r = options.rotation;
|
||||
return {
|
||||
x: getRandomPosition(field.width) - (field.width / 2),
|
||||
y: getRandomPosition(field.height) - (field.height / 2),
|
||||
rotation: getRotation(r.orientations, point.index, r.from, r.to)
|
||||
};
|
||||
},
|
||||
center: function (point, options) {
|
||||
var r = options.rotation;
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotation: getRotation(r.orientations, point.index, r.from, r.to)
|
||||
};
|
||||
}
|
||||
},
|
||||
pointArrayMap: ['weight'],
|
||||
// Spirals used for placing a word after the initial position experienced a
|
||||
// collision with either another word or the borders. To implement a custom
|
||||
// spiral, look at the function archimedeanSpiral for example.
|
||||
spirals: {
|
||||
'archimedean': archimedeanSpiral,
|
||||
'rectangular': rectangularSpiral,
|
||||
'square': squareSpiral
|
||||
},
|
||||
utils: {
|
||||
extendPlayingField: extendPlayingField,
|
||||
getRotation: getRotation,
|
||||
isPolygonsColliding: isPolygonsColliding,
|
||||
rotate2DToOrigin: rotate2DToOrigin,
|
||||
rotate2DToPoint: rotate2DToPoint
|
||||
},
|
||||
getPlotBox: function () {
|
||||
var series = this, chart = series.chart, inverted = chart.inverted,
|
||||
// Swap axes for inverted (#2339)
|
||||
xAxis = series[(inverted ? 'yAxis' : 'xAxis')], yAxis = series[(inverted ? 'xAxis' : 'yAxis')], width = xAxis ? xAxis.len : chart.plotWidth, height = yAxis ? yAxis.len : chart.plotHeight, x = xAxis ? xAxis.left : chart.plotLeft, y = yAxis ? yAxis.top : chart.plotTop;
|
||||
return {
|
||||
translateX: x + (width / 2),
|
||||
translateY: y + (height / 2),
|
||||
scaleX: 1,
|
||||
scaleY: 1
|
||||
};
|
||||
}
|
||||
};
|
||||
// Properties of the Sunburst series.
|
||||
var wordCloudPoint = {
|
||||
draw: drawPoint,
|
||||
shouldDraw: function shouldDraw() {
|
||||
var point = this;
|
||||
return !point.isNull;
|
||||
},
|
||||
isValid: function isValid() {
|
||||
return true;
|
||||
},
|
||||
weight: 1
|
||||
};
|
||||
/**
|
||||
* A `wordcloud` series. If the [type](#series.wordcloud.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.wordcloud
|
||||
* @exclude dataSorting, boostThreshold, boostBlending
|
||||
* @product highcharts
|
||||
* @requires modules/wordcloud
|
||||
* @apioption series.wordcloud
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `wordcloud` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of arrays with 2 values. In this case, the values correspond to
|
||||
* `name,weight`.
|
||||
* ```js
|
||||
* data: [
|
||||
* ['Lorem', 4],
|
||||
* ['Ipsum', 1]
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* 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.arearange.turboThreshold), this option is not
|
||||
* available.
|
||||
* ```js
|
||||
* data: [{
|
||||
* name: "Lorem",
|
||||
* weight: 4
|
||||
* }, {
|
||||
* name: "Ipsum",
|
||||
* weight: 1
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @type {Array<Array<string,number>|*>}
|
||||
* @extends series.line.data
|
||||
* @excluding drilldown, marker, x, y
|
||||
* @product highcharts
|
||||
* @apioption series.wordcloud.data
|
||||
*/
|
||||
/**
|
||||
* The name decides the text for a word.
|
||||
*
|
||||
* @type {string}
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption series.sunburst.data.name
|
||||
*/
|
||||
/**
|
||||
* The weighting of a word. The weight decides the relative size of a word
|
||||
* compared to the rest of the collection.
|
||||
*
|
||||
* @type {number}
|
||||
* @since 6.0.0
|
||||
* @product highcharts
|
||||
* @apioption series.sunburst.data.weight
|
||||
*/
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.wordcloud
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('wordcloud', 'column', wordCloudOptions, wordCloudSeries, wordCloudPoint);
|
||||
@@ -0,0 +1,673 @@
|
||||
/* *
|
||||
*
|
||||
* X-range series module
|
||||
*
|
||||
* (c) 2010-2020 Torstein Honsi, Lars A. V. Cabrera
|
||||
*
|
||||
* License: www.highcharts.com/license
|
||||
*
|
||||
* !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
|
||||
*
|
||||
* */
|
||||
'use strict';
|
||||
import Axis from '../Core/Axis/Axis.js';
|
||||
import H from '../Core/Globals.js';
|
||||
import Color from '../Core/Color.js';
|
||||
var color = Color.parse;
|
||||
import Point from '../Core/Series/Point.js';
|
||||
import U from '../Core/Utilities.js';
|
||||
var addEvent = U.addEvent, clamp = U.clamp, correctFloat = U.correctFloat, defined = U.defined, find = U.find, isNumber = U.isNumber, isObject = U.isObject, merge = U.merge, pick = U.pick, seriesType = U.seriesType;
|
||||
/* *
|
||||
* @interface Highcharts.PointOptionsObject in parts/Point.ts
|
||||
*/ /**
|
||||
* The ending X value of the range point.
|
||||
* @name Highcharts.PointOptionsObject#x2
|
||||
* @type {number|undefined}
|
||||
* @requires modules/xrange
|
||||
*/
|
||||
import '../Core/Series/Series.js';
|
||||
var columnType = H.seriesTypes.column, seriesTypes = H.seriesTypes, Series = H.Series;
|
||||
/**
|
||||
* Return color of a point based on its category.
|
||||
*
|
||||
* @private
|
||||
* @function getColorByCategory
|
||||
*
|
||||
* @param {object} series
|
||||
* The series which the point belongs to.
|
||||
*
|
||||
* @param {object} point
|
||||
* The point to calculate its color for.
|
||||
*
|
||||
* @return {object}
|
||||
* Returns an object containing the properties color and colorIndex.
|
||||
*/
|
||||
function getColorByCategory(series, point) {
|
||||
var colors = series.options.colors || series.chart.options.colors, colorCount = colors ?
|
||||
colors.length :
|
||||
series.chart.options.chart.colorCount, colorIndex = point.y % colorCount, color = colors && colors[colorIndex];
|
||||
return {
|
||||
colorIndex: colorIndex,
|
||||
color: color
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @class
|
||||
* @name Highcharts.seriesTypes.xrange
|
||||
*
|
||||
* @augments Highcharts.Series
|
||||
*/
|
||||
seriesType('xrange', 'column'
|
||||
/**
|
||||
* The X-range series displays ranges on the X axis, typically time
|
||||
* intervals with a start and end date.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/x-range/
|
||||
* X-range
|
||||
* @sample {highcharts} highcharts/css/x-range/
|
||||
* Styled mode X-range
|
||||
* @sample {highcharts} highcharts/chart/inverted-xrange/
|
||||
* Inverted X-range
|
||||
*
|
||||
* @extends plotOptions.column
|
||||
* @since 6.0.0
|
||||
* @product highcharts highstock gantt
|
||||
* @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor,
|
||||
* edgeWidth, findNearestPointBy, getExtremesFromAll,
|
||||
* negativeColor, pointInterval, pointIntervalUnit,
|
||||
* pointPlacement, pointRange, pointStart, softThreshold,
|
||||
* stacking, threshold, data, dataSorting, boostBlending
|
||||
* @requires modules/xrange
|
||||
* @optionparent plotOptions.xrange
|
||||
*/
|
||||
, {
|
||||
/**
|
||||
* A partial fill for each point, typically used to visualize how much
|
||||
* of a task is performed. The partial fill object can be set either on
|
||||
* series or point level.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/x-range
|
||||
* X-range with partial fill
|
||||
*
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption plotOptions.xrange.partialFill
|
||||
*/
|
||||
/**
|
||||
* The fill color to be used for partial fills. Defaults to a darker
|
||||
* shade of the point color.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption plotOptions.xrange.partialFill.fill
|
||||
*/
|
||||
/**
|
||||
* A partial fill for each point, typically used to visualize how much
|
||||
* of a task is performed. See [completed](series.gantt.data.completed).
|
||||
*
|
||||
* @sample gantt/demo/progress-indicator
|
||||
* Gantt with progress indicator
|
||||
*
|
||||
* @product gantt
|
||||
* @apioption plotOptions.gantt.partialFill
|
||||
*/
|
||||
/**
|
||||
* In an X-range series, this option makes all points of the same Y-axis
|
||||
* category the same color.
|
||||
*/
|
||||
colorByPoint: true,
|
||||
dataLabels: {
|
||||
formatter: function () {
|
||||
var point = this.point, amount = point.partialFill;
|
||||
if (isObject(amount)) {
|
||||
amount = amount.amount;
|
||||
}
|
||||
if (isNumber(amount) && amount > 0) {
|
||||
return correctFloat(amount * 100) + '%';
|
||||
}
|
||||
},
|
||||
inside: true,
|
||||
verticalAlign: 'middle'
|
||||
},
|
||||
tooltip: {
|
||||
headerFormat: '<span style="font-size: 10px">{point.x} - {point.x2}</span><br/>',
|
||||
pointFormat: '<span style="color:{point.color}">\u25CF</span> {series.name}: <b>{point.yCategory}</b><br/>'
|
||||
},
|
||||
borderRadius: 3,
|
||||
pointRange: 0
|
||||
}, {
|
||||
type: 'xrange',
|
||||
parallelArrays: ['x', 'x2', 'y'],
|
||||
requireSorting: false,
|
||||
animate: seriesTypes.line.prototype.animate,
|
||||
cropShoulder: 1,
|
||||
getExtremesFromAll: true,
|
||||
autoIncrement: H.noop,
|
||||
buildKDTree: H.noop,
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
* @function Highcarts.seriesTypes.xrange#init
|
||||
* @return {void}
|
||||
*/
|
||||
init: function () {
|
||||
seriesTypes.column.prototype.init.apply(this, arguments);
|
||||
this.options.stacking = void 0; // #13161
|
||||
},
|
||||
/**
|
||||
* Borrow the column series metrics, but with swapped axes. This gives
|
||||
* free access to features like groupPadding, grouping, pointWidth etc.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#getColumnMetrics
|
||||
*
|
||||
* @return {Highcharts.ColumnMetricsObject}
|
||||
*/
|
||||
getColumnMetrics: function () {
|
||||
var metrics, chart = this.chart;
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
function swapAxes() {
|
||||
chart.series.forEach(function (s) {
|
||||
var xAxis = s.xAxis;
|
||||
s.xAxis = s.yAxis;
|
||||
s.yAxis = xAxis;
|
||||
});
|
||||
}
|
||||
swapAxes();
|
||||
metrics = columnType.prototype.getColumnMetrics.call(this);
|
||||
swapAxes();
|
||||
return metrics;
|
||||
},
|
||||
/**
|
||||
* Override cropData to show a point where x or x2 is outside visible
|
||||
* range, but one of them is inside.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#cropData
|
||||
*
|
||||
* @param {Array<number>} xData
|
||||
*
|
||||
* @param {Array<number>} yData
|
||||
*
|
||||
* @param {number} min
|
||||
*
|
||||
* @param {number} max
|
||||
*
|
||||
* @param {number} [cropShoulder]
|
||||
*
|
||||
* @return {*}
|
||||
*/
|
||||
cropData: function (xData, yData, min, max) {
|
||||
// Replace xData with x2Data to find the appropriate cropStart
|
||||
var cropData = Series.prototype.cropData, crop = cropData.call(this, this.x2Data, yData, min, max);
|
||||
// Re-insert the cropped xData
|
||||
crop.xData = xData.slice(crop.start, crop.end);
|
||||
return crop;
|
||||
},
|
||||
/**
|
||||
* Finds the index of an existing point that matches the given point
|
||||
* options.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#findPointIndex
|
||||
* @param {object} options The options of the point.
|
||||
* @returns {number|undefined} Returns index of a matching point,
|
||||
* returns undefined if no match is found.
|
||||
*/
|
||||
findPointIndex: function (options) {
|
||||
var _a = this, cropped = _a.cropped, cropStart = _a.cropStart, points = _a.points;
|
||||
var id = options.id;
|
||||
var pointIndex;
|
||||
if (id) {
|
||||
var point = find(points, function (point) {
|
||||
return point.id === id;
|
||||
});
|
||||
pointIndex = point ? point.index : void 0;
|
||||
}
|
||||
if (typeof pointIndex === 'undefined') {
|
||||
var point = find(points, function (point) {
|
||||
return (point.x === options.x &&
|
||||
point.x2 === options.x2 &&
|
||||
!point.touched);
|
||||
});
|
||||
pointIndex = point ? point.index : void 0;
|
||||
}
|
||||
// Reduce pointIndex if data is cropped
|
||||
if (cropped &&
|
||||
isNumber(pointIndex) &&
|
||||
isNumber(cropStart) &&
|
||||
pointIndex >= cropStart) {
|
||||
pointIndex -= cropStart;
|
||||
}
|
||||
return pointIndex;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Series#translatePoint
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
*/
|
||||
translatePoint: function (point) {
|
||||
var series = this, xAxis = series.xAxis, yAxis = series.yAxis, metrics = series.columnMetrics, options = series.options, minPointLength = options.minPointLength || 0, plotX = point.plotX, posX = pick(point.x2, point.x + (point.len || 0)), plotX2 = xAxis.translate(posX, 0, 0, 0, 1), length = Math.abs(plotX2 - plotX), widthDifference, shapeArgs, partialFill, inverted = this.chart.inverted, borderWidth = pick(options.borderWidth, 1), crisper = borderWidth % 2 / 2, yOffset = metrics.offset, pointHeight = Math.round(metrics.width), dlLeft, dlRight, dlWidth, clipRectWidth, tooltipYOffset;
|
||||
if (minPointLength) {
|
||||
widthDifference = minPointLength - length;
|
||||
if (widthDifference < 0) {
|
||||
widthDifference = 0;
|
||||
}
|
||||
plotX -= widthDifference / 2;
|
||||
plotX2 += widthDifference / 2;
|
||||
}
|
||||
plotX = Math.max(plotX, -10);
|
||||
plotX2 = clamp(plotX2, -10, xAxis.len + 10);
|
||||
// Handle individual pointWidth
|
||||
if (defined(point.options.pointWidth)) {
|
||||
yOffset -= ((Math.ceil(point.options.pointWidth) - pointHeight) / 2);
|
||||
pointHeight = Math.ceil(point.options.pointWidth);
|
||||
}
|
||||
// Apply pointPlacement to the Y axis
|
||||
if (options.pointPlacement &&
|
||||
isNumber(point.plotY) &&
|
||||
yAxis.categories) {
|
||||
point.plotY = yAxis.translate(point.y, 0, 1, 0, 1, options.pointPlacement);
|
||||
}
|
||||
point.shapeArgs = {
|
||||
x: Math.floor(Math.min(plotX, plotX2)) + crisper,
|
||||
y: Math.floor(point.plotY + yOffset) + crisper,
|
||||
width: Math.round(Math.abs(plotX2 - plotX)),
|
||||
height: pointHeight,
|
||||
r: series.options.borderRadius
|
||||
};
|
||||
// Align data labels inside the shape and inside the plot area
|
||||
dlLeft = point.shapeArgs.x;
|
||||
dlRight = dlLeft + point.shapeArgs.width;
|
||||
if (dlLeft < 0 || dlRight > xAxis.len) {
|
||||
dlLeft = clamp(dlLeft, 0, xAxis.len);
|
||||
dlRight = clamp(dlRight, 0, xAxis.len);
|
||||
dlWidth = dlRight - dlLeft;
|
||||
point.dlBox = merge(point.shapeArgs, {
|
||||
x: dlLeft,
|
||||
width: dlRight - dlLeft,
|
||||
centerX: dlWidth ? dlWidth / 2 : null
|
||||
});
|
||||
}
|
||||
else {
|
||||
point.dlBox = null;
|
||||
}
|
||||
// Tooltip position
|
||||
var tooltipPos = point.tooltipPos;
|
||||
var xIndex = !inverted ? 0 : 1;
|
||||
var yIndex = !inverted ? 1 : 0;
|
||||
tooltipYOffset = series.columnMetrics ?
|
||||
series.columnMetrics.offset : -metrics.width / 2;
|
||||
// Limit position by the correct axis size (#9727)
|
||||
tooltipPos[xIndex] = clamp(tooltipPos[xIndex] + ((!inverted ? 1 : -1) * (xAxis.reversed ? -1 : 1) *
|
||||
(length / 2)), 0, xAxis.len - 1);
|
||||
tooltipPos[yIndex] = clamp(tooltipPos[yIndex] + ((inverted ? -1 : 1) * tooltipYOffset), 0, yAxis.len - 1);
|
||||
// Add a partShapeArgs to the point, based on the shapeArgs property
|
||||
partialFill = point.partialFill;
|
||||
if (partialFill) {
|
||||
// Get the partial fill amount
|
||||
if (isObject(partialFill)) {
|
||||
partialFill = partialFill.amount;
|
||||
}
|
||||
// If it was not a number, assume 0
|
||||
if (!isNumber(partialFill)) {
|
||||
partialFill = 0;
|
||||
}
|
||||
shapeArgs = point.shapeArgs;
|
||||
point.partShapeArgs = {
|
||||
x: shapeArgs.x,
|
||||
y: shapeArgs.y,
|
||||
width: shapeArgs.width,
|
||||
height: shapeArgs.height,
|
||||
r: series.options.borderRadius
|
||||
};
|
||||
clipRectWidth = Math.max(Math.round(length * partialFill + point.plotX -
|
||||
plotX), 0);
|
||||
point.clipRectArgs = {
|
||||
x: xAxis.reversed ? // #10717
|
||||
shapeArgs.x + length - clipRectWidth :
|
||||
shapeArgs.x,
|
||||
y: shapeArgs.y,
|
||||
width: clipRectWidth,
|
||||
height: shapeArgs.height
|
||||
};
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Series#translate
|
||||
*/
|
||||
translate: function () {
|
||||
columnType.prototype.translate.apply(this, arguments);
|
||||
this.points.forEach(function (point) {
|
||||
this.translatePoint(point);
|
||||
}, this);
|
||||
},
|
||||
/**
|
||||
* Draws a single point in the series. Needed for partial fill.
|
||||
*
|
||||
* This override turns point.graphic into a group containing the
|
||||
* original graphic and an overlay displaying the partial fill.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#drawPoint
|
||||
*
|
||||
* @param {Highcharts.Point} point
|
||||
* An instance of Point in the series.
|
||||
*
|
||||
* @param {"animate"|"attr"} verb
|
||||
* 'animate' (animates changes) or 'attr' (sets options)
|
||||
*/
|
||||
drawPoint: function (point, verb) {
|
||||
var series = this, seriesOpts = series.options, renderer = series.chart.renderer, graphic = point.graphic, type = point.shapeType, shapeArgs = point.shapeArgs, partShapeArgs = point.partShapeArgs, clipRectArgs = point.clipRectArgs, pfOptions = point.partialFill, cutOff = seriesOpts.stacking && !seriesOpts.borderRadius, pointState = point.state, stateOpts = (seriesOpts.states[pointState || 'normal'] ||
|
||||
{}), pointStateVerb = typeof pointState === 'undefined' ?
|
||||
'attr' : verb, pointAttr = series.pointAttribs(point, pointState), animation = pick(series.chart.options.chart.animation, stateOpts.animation), fill;
|
||||
if (!point.isNull && point.visible !== false) {
|
||||
// Original graphic
|
||||
if (graphic) { // update
|
||||
graphic.rect[verb](shapeArgs);
|
||||
}
|
||||
else {
|
||||
point.graphic = graphic = renderer.g('point')
|
||||
.addClass(point.getClassName())
|
||||
.add(point.group || series.group);
|
||||
graphic.rect = renderer[type](merge(shapeArgs))
|
||||
.addClass(point.getClassName())
|
||||
.addClass('highcharts-partfill-original')
|
||||
.add(graphic);
|
||||
}
|
||||
// Partial fill graphic
|
||||
if (partShapeArgs) {
|
||||
if (graphic.partRect) {
|
||||
graphic.partRect[verb](merge(partShapeArgs));
|
||||
graphic.partialClipRect[verb](merge(clipRectArgs));
|
||||
}
|
||||
else {
|
||||
graphic.partialClipRect = renderer.clipRect(clipRectArgs.x, clipRectArgs.y, clipRectArgs.width, clipRectArgs.height);
|
||||
graphic.partRect =
|
||||
renderer[type](partShapeArgs)
|
||||
.addClass('highcharts-partfill-overlay')
|
||||
.add(graphic)
|
||||
.clip(graphic.partialClipRect);
|
||||
}
|
||||
}
|
||||
// Presentational
|
||||
if (!series.chart.styledMode) {
|
||||
graphic
|
||||
.rect[verb](pointAttr, animation)
|
||||
.shadow(seriesOpts.shadow, null, cutOff);
|
||||
if (partShapeArgs) {
|
||||
// Ensure pfOptions is an object
|
||||
if (!isObject(pfOptions)) {
|
||||
pfOptions = {};
|
||||
}
|
||||
if (isObject(seriesOpts.partialFill)) {
|
||||
pfOptions = merge(pfOptions, seriesOpts.partialFill);
|
||||
}
|
||||
fill = (pfOptions.fill ||
|
||||
color(pointAttr.fill).brighten(-0.3).get() ||
|
||||
color(point.color || series.color)
|
||||
.brighten(-0.3).get());
|
||||
pointAttr.fill = fill;
|
||||
graphic
|
||||
.partRect[pointStateVerb](pointAttr, animation)
|
||||
.shadow(seriesOpts.shadow, null, cutOff);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (graphic) {
|
||||
point.graphic = graphic.destroy(); // #1269
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Series#drawPoints
|
||||
*/
|
||||
drawPoints: function () {
|
||||
var series = this, verb = series.getAnimationVerb();
|
||||
// Draw the columns
|
||||
series.points.forEach(function (point) {
|
||||
series.drawPoint(point, verb);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Returns "animate", or "attr" if the number of points is above the
|
||||
* animation limit.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Series#getAnimationVerb
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
getAnimationVerb: function () {
|
||||
return (this.chart.pointCount < (this.options.animationLimit || 250) ?
|
||||
'animate' :
|
||||
'attr');
|
||||
}
|
||||
/*
|
||||
// Override to remove stroke from points. For partial fill.
|
||||
pointAttribs: function () {
|
||||
var series = this,
|
||||
retVal = columnType.prototype.pointAttribs
|
||||
.apply(series, arguments);
|
||||
|
||||
//retVal['stroke-width'] = 0;
|
||||
return retVal;
|
||||
}
|
||||
//*/
|
||||
/* eslint-enable valid-jsdoc */
|
||||
}, {
|
||||
/**
|
||||
* The ending X value of the range point.
|
||||
* @name Highcharts.Point#x2
|
||||
* @type {number|undefined}
|
||||
* @requires modules/xrange
|
||||
*/
|
||||
/**
|
||||
* Extend applyOptions so that `colorByPoint` for x-range means that one
|
||||
* color is applied per Y axis category.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Point#applyOptions
|
||||
*
|
||||
* @return {Highcharts.Series}
|
||||
*/
|
||||
/* eslint-disable valid-jsdoc */
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
resolveColor: function () {
|
||||
var series = this.series, colorByPoint;
|
||||
if (series.options.colorByPoint && !this.options.color) {
|
||||
colorByPoint = getColorByCategory(series, this);
|
||||
if (!series.chart.styledMode) {
|
||||
this.color = colorByPoint.color;
|
||||
}
|
||||
if (!this.options.colorIndex) {
|
||||
this.colorIndex = colorByPoint.colorIndex;
|
||||
}
|
||||
}
|
||||
else if (!this.color) {
|
||||
this.color = series.color;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Extend init to have y default to 0.
|
||||
*
|
||||
* @private
|
||||
* @function Highcharts.Point#init
|
||||
*
|
||||
* @return {Highcharts.Point}
|
||||
*/
|
||||
init: function () {
|
||||
Point.prototype.init.apply(this, arguments);
|
||||
if (!this.y) {
|
||||
this.y = 0;
|
||||
}
|
||||
return this;
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Point#setState
|
||||
*/
|
||||
setState: function () {
|
||||
Point.prototype.setState.apply(this, arguments);
|
||||
this.series.drawPoint(this, this.series.getAnimationVerb());
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Point#getLabelConfig
|
||||
*
|
||||
* @return {Highcharts.PointLabelObject}
|
||||
*/
|
||||
// Add x2 and yCategory to the available properties for tooltip formats
|
||||
getLabelConfig: function () {
|
||||
var point = this, cfg = Point.prototype.getLabelConfig.call(point), yCats = point.series.yAxis.categories;
|
||||
cfg.x2 = point.x2;
|
||||
cfg.yCategory = point.yCategory = yCats && yCats[point.y];
|
||||
return cfg;
|
||||
},
|
||||
tooltipDateKeys: ['x', 'x2'],
|
||||
/**
|
||||
* @private
|
||||
* @function Highcharts.Point#isValid
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
isValid: function () {
|
||||
return typeof this.x === 'number' &&
|
||||
typeof this.x2 === 'number';
|
||||
}
|
||||
/* eslint-enable valid-jsdoc */
|
||||
});
|
||||
/**
|
||||
* Max x2 should be considered in xAxis extremes
|
||||
*/
|
||||
addEvent(Axis, 'afterGetSeriesExtremes', function () {
|
||||
var axis = this, // eslint-disable-line no-invalid-this
|
||||
axisSeries = axis.series, dataMax, modMax;
|
||||
if (axis.isXAxis) {
|
||||
dataMax = pick(axis.dataMax, -Number.MAX_VALUE);
|
||||
axisSeries.forEach(function (series) {
|
||||
if (series.x2Data) {
|
||||
series.x2Data
|
||||
.forEach(function (val) {
|
||||
if (val > dataMax) {
|
||||
dataMax = val;
|
||||
modMax = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (modMax) {
|
||||
axis.dataMax = dataMax;
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* An `xrange` series. If the [type](#series.xrange.type) option is not
|
||||
* specified, it is inherited from [chart.type](#chart.type).
|
||||
*
|
||||
* @extends series,plotOptions.xrange
|
||||
* @excluding boostThreshold, crisp, cropThreshold, depth, edgeColor, edgeWidth,
|
||||
* findNearestPointBy, getExtremesFromAll, negativeColor,
|
||||
* pointInterval, pointIntervalUnit, pointPlacement, pointRange,
|
||||
* pointStart, softThreshold, stacking, threshold, dataSorting,
|
||||
* boostBlending
|
||||
* @product highcharts highstock gantt
|
||||
* @requires modules/xrange
|
||||
* @apioption series.xrange
|
||||
*/
|
||||
/**
|
||||
* An array of data points for the series. For the `xrange` series type,
|
||||
* points can be given in the following ways:
|
||||
*
|
||||
* 1. An array of objects with named values. The objects are point configuration
|
||||
* objects as seen below.
|
||||
* ```js
|
||||
* data: [{
|
||||
* x: Date.UTC(2017, 0, 1),
|
||||
* x2: Date.UTC(2017, 0, 3),
|
||||
* name: "Test",
|
||||
* y: 0,
|
||||
* color: "#00FF00"
|
||||
* }, {
|
||||
* x: Date.UTC(2017, 0, 4),
|
||||
* x2: Date.UTC(2017, 0, 5),
|
||||
* name: "Deploy",
|
||||
* y: 1,
|
||||
* color: "#FF0000"
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* @sample {highcharts} highcharts/series/data-array-of-objects/
|
||||
* Config objects
|
||||
*
|
||||
* @declare Highcharts.XrangePointOptionsObject
|
||||
* @type {Array<*>}
|
||||
* @extends series.line.data
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption series.xrange.data
|
||||
*/
|
||||
/**
|
||||
* The starting X value of the range point.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/x-range
|
||||
* X-range
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption series.xrange.data.x
|
||||
*/
|
||||
/**
|
||||
* The ending X value of the range point.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/x-range
|
||||
* X-range
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption series.xrange.data.x2
|
||||
*/
|
||||
/**
|
||||
* The Y value of the range point.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/x-range
|
||||
* X-range
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption series.xrange.data.y
|
||||
*/
|
||||
/**
|
||||
* A partial fill for each point, typically used to visualize how much of
|
||||
* a task is performed. The partial fill object can be set either on series
|
||||
* or point level.
|
||||
*
|
||||
* @sample {highcharts} highcharts/demo/x-range
|
||||
* X-range with partial fill
|
||||
*
|
||||
* @declare Highcharts.XrangePointPartialFillOptionsObject
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption series.xrange.data.partialFill
|
||||
*/
|
||||
/**
|
||||
* The amount of the X-range point to be filled. Values can be 0-1 and are
|
||||
* converted to percentages in the default data label formatter.
|
||||
*
|
||||
* @type {number}
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption series.xrange.data.partialFill.amount
|
||||
*/
|
||||
/**
|
||||
* The fill color to be used for partial fills. Defaults to a darker shade
|
||||
* of the point color.
|
||||
*
|
||||
* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject}
|
||||
* @product highcharts highstock gantt
|
||||
* @apioption series.xrange.data.partialFill.fill
|
||||
*/
|
||||
''; // adds doclets above to transpiled file
|
||||
Reference in New Issue
Block a user