/**
* (c) 2010-2017 Torstein Honsi
*
* License: www.highcharts.com/license
*/
'use strict';
import H from '../parts/Globals.js';
import '../parts/Utilities.js';
import '../parts/Options.js';
var each = H.each,
noop = H.noop,
pick = H.pick,
seriesType = H.seriesType,
seriesTypes = H.seriesTypes;
/**
* The boxplot series type.
*
* @constructor seriesTypes.boxplot
* @augments seriesTypes.column
*/
/**
* 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}
* @product highcharts
* @excluding borderColor,borderRadius,borderWidth,groupZPadding,states
* @optionparent plotOptions.boxplot
*/
seriesType('boxplot', 'column', {
threshold: null,
tooltip: {
pointFormat: '' +
'\u25CF {series.name} ' +
'Maximum: {point.high} ' +
'Upper quartile: {point.q3} ' +
'Median: {point.median} ' +
'Lower quartile: {point.q1} ' +
'Minimum: {point.low} '
},
/**
* 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.
*
* @type {Number|String}
* @sample {highcharts} highcharts/plotoptions/box-plot-styling/
* True by default
* @since 3.0
* @product highcharts
*/
whiskerLength: '50%'
}, /** @lends seriesTypes.boxplot */ {
// array point configs are mapped to this
pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'],
toYData: function (point) { // return a plain array for speedy calculation
return [point.low, point.q1, point.median, point.q3, point.high];
},
// defines the top of the tracker
pointValKey: 'high',
/**
* 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
each(series.points, function (point) {
each(pointArrayMap, function (key) {
if (point[key] !== null) {
point[key + 'Plot'] = yAxis.translate(
point[key], 0, 1, 0, 1
);
}
});
});
},
/**
* Draw the data points
*/
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;
each(points, function (point) {
var graphic = point.graphic,
verb = graphic ? 'animate' : 'attr',
shapeArgs = point.shapeArgs; // the box
if (point.plotY !== undefined) {
// crisp vector coordinates
width = 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);
}
// The stem
crispCorr = (point.stem.strokeWidth() % 2) / 2;
crispX = left + halfWidth + crispCorr;
point.stem[verb]({ d: [
// stem up
'M',
crispX, q3Plot,
'L',
crispX, highPlot,
// stem down
'M',
crispX, q1Plot,
'L',
crispX, lowPlot
] });
// The box
if (doQuartiles) {
crispCorr = (point.box.strokeWidth() % 2) / 2;
q1Plot = Math.floor(q1Plot) + crispCorr;
q3Plot = Math.floor(q3Plot) + crispCorr;
left += crispCorr;
right += crispCorr;
point.box[verb]({ d: [
'M',
left, q3Plot,
'L',
left, q1Plot,
'L',
right, q1Plot,
'L',
right, q3Plot,
'L',
left, q3Plot,
'z'
] });
}
// 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;
point.whiskers[verb]({ d: [
// High whisker
'M',
crispX - pointWiskerLength,
highPlot,
'L',
crispX + pointWiskerLength,
highPlot,
// Low whisker
'M',
crispX - pointWiskerLength,
lowPlot,
'L',
crispX + pointWiskerLength,
lowPlot
] });
}
// The median
medianPlot = Math.round(point.medianPlot);
crispCorr = (point.medianShape.strokeWidth() % 2) / 2;
medianPlot = medianPlot + crispCorr;
point.medianShape[verb]({ d: [
'M',
left,
medianPlot,
'L',
right,
medianPlot
] });
}
});
},
setStackedPoints: noop // #3890
});
/**
* A `boxplot` series. If the [type](#series.boxplot.type) option is
* not specified, it is inherited from [chart.type](#chart.type).
*
* @type {Object}
* @extends series,plotOptions.boxplot
* @excluding dataParser,dataURL,marker,stack,stacking,states
* @product highcharts
* @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 objects are point
* configuration objects as seen 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"
* }]
* ```
*
* @type {Array