arjary
arjary

Reputation: 169

Highcharts - stacked column - order series index dynamically for each category

I have a scenario where i am drawing a stacked column chart, currently stuck at a point where i need to order the series in a specific order in case of multiple data points on the same day. The issue is, the series order can be different for each day - I have the code and example below -

Highcharts.chart('container', {
chart: {
    type: 'column'
},
title: {
    text: 'Stacked column chart'
},
xAxis: {
    categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas']
},
yAxis: {
    min: 0,
    title: {
        text: 'Total fruit consumption'
    },
    stackLabels: {
        enabled: true,
        style: {
            fontWeight: 'bold',
            color: (Highcharts.theme && Highcharts.theme.textColor) || 
    'gray'
        }
    }
},
legend: {
    align: 'right',
    x: -30,
    verticalAlign: 'top',
    y: 25,
    floating: true,
    backgroundColor: (Highcharts.theme && Highcharts.theme.background2) 
      || 'white',
    borderColor: '#CCC',
    borderWidth: 1,
    shadow: false
 },
tooltip: {
    headerFormat: '<b>{point.x}</b><br/>',
    pointFormat: '{series.name}: {point.y}<br/>Total: 
                 {point.stackTotal}'
},
plotOptions: {
    column: {
        stacking: 'normal',
        dataLabels: {
            enabled: true,
            color: (Highcharts.theme && 
                   Highcharts.theme.dataLabelsColor) || 'white'
        }
    }
},
series: [{
    name: 'John',
    data: [5, 3, 4, 7, 2]
}, {
    name: 'Mary',
    data: [2, 2, 3, 2, 1]
}, {
    name: 'Kevin',
    data: [3, 4, 4, 2, 5]
}]
});

JSFiddle : http://jsfiddle.net/arj_ary/4kz7rdpn/2/

Requirement: In the above JSFiddle, for each category i want the lowest datapoint to show up at the top. So for Apples i want Mary at the top since it has a value of 2. Similarly for Oranges and Pears.

Can this be accomplished? Any help is appreciated.

Upvotes: 1

Views: 1932

Answers (1)

Wojciech Chmiel
Wojciech Chmiel

Reputation: 7372

It can be done using this approach:

  1. create a function which sort columns:
  • loop through points and group them by category (apples, oranges etc)
  • loop through each group of points and sort the group by y value
  • loop through points in each group and set a newly calculated y position using SVGElement.attr method (point.graphic is an SVGElement instance). Do the same for point.dataLabel and set a new position to point.tooltipPos array (otherwise tooltip will be placed wrongly).
function sortColumns() {
  var chart = this,
    pointsByCat = {},
    zeroPixels,
    bottomYPositive,
    bottomYNegative,
    shapeArgs;

  chart.series.forEach(function(serie) {
    serie.points.forEach(function(point, index) {

      if (pointsByCat[point.category] === undefined) {
        pointsByCat[point.category] = [];
      }

      pointsByCat[point.category].push(point);
    });
  });

  Highcharts.objectEach(pointsByCat, function(points, key) {
    zeroPixels = chart.yAxis[0].toPixels(0) - chart.plotTop;
    bottomYPositive = bottomYNegative = zeroPixels;

    points.sort(function(a, b) {
      return b.y - a.y;
    });

    points.forEach(function(point) {
      if (point.series.visible) {
        if (point.shapeArgs.y < zeroPixels) {

          // Positive values
          point.graphic.attr({
            y: bottomYPositive - point.shapeArgs.height
          });

          point.dataLabel.attr({
            y: bottomYPositive - point.shapeArgs.height / 2 - point.dataLabel.height / 2
          });

          point.tooltipPos[1] = bottomYPositive - point.shapeArgs.height;
          bottomYPositive = bottomYPositive - point.shapeArgs.height;
        } else {

          // Negative values
          point.graphic.attr({
            y: bottomYNegative
          });

          point.dataLabel.attr({
            y: bottomYNegative + point.shapeArgs.height / 2 - point.dataLabel.height / 2
          });

          point.tooltipPos[1] = bottomYNegative;
          bottomYNegative = bottomYNegative + point.shapeArgs.height;
        }
      }
    });
  });
}



2) set above function as a callback to chart.events.load and chart.events.redraw events:

  chart: {
    type: 'column',
    events: {
      load: sortColumns,
      redraw: sortColumns
    }
  }

Demo:

API reference:

Upvotes: 3

Related Questions