UNPKG

highcharts

Version:
238 lines (237 loc) 9.59 kB
/* * * * (c) 2010-2025 Torstein Honsi * * License: www.highcharts.com/license * * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!! * * */ 'use strict'; import BoxPlotSeriesDefaults from './BoxPlotSeriesDefaults.js'; import ColumnSeries from '../Column/ColumnSeries.js'; import H from '../../Core/Globals.js'; const { noop } = H; import SeriesRegistry from '../../Core/Series/SeriesRegistry.js'; import U from '../../Core/Utilities.js'; const { crisp, extend, merge, pick, relativeLength } = U; /* * * * Class * * */ /** * The boxplot series type. * * @private * @class * @name Highcharts.seriesTypes#boxplot * * @augments Highcharts.Series */ class BoxPlotSeries extends ColumnSeries { /* * * * Functions * * */ // Get presentational attributes pointAttribs() { // No attributes should be set on point.graphic which is the group return {}; } // Get an SVGPath object for both whiskers getWhiskerPair(halfWidth, stemX, upperWhiskerLength, lowerWhiskerLength, point) { const strokeWidth = point.whiskers.strokeWidth(), getWhisker = (xLen, yPos) => { const halfLen = relativeLength(xLen, 2 * halfWidth) / 2, crispedYPos = crisp(yPos, strokeWidth); return [ [ 'M', crisp(stemX - halfLen), crispedYPos ], [ 'L', crisp(stemX + halfLen), crispedYPos ] ]; }; return [ ...getWhisker(upperWhiskerLength, point.highPlot), ...getWhisker(lowerWhiskerLength, point.lowPlot) ]; } // Translate data points from raw values x and y to plotX and plotY translate() { const series = this, yAxis = series.yAxis, pointArrayMap = series.pointArrayMap; super.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 }); } /** * Draw the data points * @private */ drawPoints() { const series = this, points = series.points, options = series.options, chart = series.chart, renderer = chart.renderer, // Error bar inherits this series type but doesn't do quartiles doQuartiles = series.doQuartiles !== false, whiskerLength = series.options.whiskerLength; let q1Plot, q3Plot, highPlot, lowPlot, medianPlot, medianPath, boxPath, graphic, width, x, right; for (const point of points) { graphic = point.graphic; const verb = graphic ? 'animate' : 'attr', shapeArgs = point.shapeArgs, boxAttr = {}, stemAttr = {}, whiskersAttr = {}, medianAttr = {}, color = point.color || series.color, pointWhiskerLength = (point.options.whiskerLength || whiskerLength); if (typeof point.plotY !== 'undefined') { // Vector coordinates width = shapeArgs.width; x = shapeArgs.x; right = x + width; q1Plot = doQuartiles ? point.q1Plot : point.lowPlot; q3Plot = doQuartiles ? point.q3Plot : point.lowPlot; highPlot = point.highPlot; lowPlot = 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 (pointWhiskerLength) { 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); } let d; // The stem const stemX = crisp((point.plotX || 0) + (series.pointXOffset || 0) + ((series.barW || 0) / 2), point.stem.strokeWidth()); d = [ // Stem up ['M', stemX, q3Plot], ['L', stemX, highPlot], // Stem down ['M', stemX, q1Plot], ['L', stemX, lowPlot] ]; point.stem[verb]({ d }); // The box if (doQuartiles) { const boxStrokeWidth = point.box.strokeWidth(); q1Plot = crisp(q1Plot, boxStrokeWidth); q3Plot = crisp(q3Plot, boxStrokeWidth); x = crisp(x, boxStrokeWidth); right = crisp(right, boxStrokeWidth); d = [ ['M', x, q3Plot], ['L', x, q1Plot], ['L', right, q1Plot], ['L', right, q3Plot], ['L', x, q3Plot], ['Z'] ]; point.box[verb]({ d }); } // The whiskers if (pointWhiskerLength) { const halfWidth = width / 2, whiskers = this.getWhiskerPair(halfWidth, stemX, (point.upperWhiskerLength ?? options.upperWhiskerLength ?? pointWhiskerLength), (point.lowerWhiskerLength ?? options.lowerWhiskerLength ?? pointWhiskerLength), point); point.whiskers[verb]({ d: whiskers }); } // The median medianPlot = crisp(point.medianPlot, point.medianShape.strokeWidth()); d = [ ['M', x, medianPlot], ['L', right, medianPlot] ]; point.medianShape[verb]({ d }); } } } // Return a plain array for speedy calculation toYData(point) { return [point.low, point.q1, point.median, point.q3, point.high]; } } /* * * * Static Properties * * */ BoxPlotSeries.defaultOptions = merge(ColumnSeries.defaultOptions, BoxPlotSeriesDefaults); extend(BoxPlotSeries.prototype, { // Array point configs are mapped to this pointArrayMap: ['low', 'q1', 'median', 'q3', 'high'], // Defines the top of the tracker pointValKey: 'high', // Disable data labels for box plot drawDataLabels: noop, setStackedPoints: noop // #3890 }); SeriesRegistry.registerSeriesType('boxplot', BoxPlotSeries); /* * * * Default Export * * */ export default BoxPlotSeries;