4

I am trying to achieve something like column 2014 but I haven't found a way to provide layering like this. This is a fiddle of what I have till now.
2 columns having different stacks

I need to make a stacked column graph that looks like either 2014 column or 2015 column.(which ever is feasible)

  • The problem with column 2014 is that I am not able to find any property to give (negative) margin to achieve the above result.

  • The problem with the column 2015 is that I am not able to add border radius to the top-left and top-right corners alone.

As links to fiddle must be accompanied by the code

Highcharts.chart('container', {
  chart: {
    type: 'column',
    spacingBottom: 0
  },
  title: {
    text: ''
  },
  xAxis: {
    categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas'],
    offset: 7,
    lineWidth: 0,
    tickLength: 0
  },
  yAxis: {
    min: 0,
    title: {
      text: ''
    },
    stackLabels: {
      enabled: false,
      style: {
        fontWeight: 'bold',
        color: 'gray'
      }
    },
    visible: false
  },
  legend: {
    align: 'center',

    verticalAlign: 'bottom',

  },
  tooltip: {
    headerFormat: '<b>{point.x}</b><br/>',
    pointFormat: '{series.name}: {point.y}<br/>Total: {point.stackTotal}'
  },
  plotOptions: {
    series: {

    },
    column: {
      stacking: 'normal',
      borderWidth: 0,
      borderRadius: 5,
      dataLabels: {
        enabled: true,
        color: 'white'
      }
    }
  },
  series: [{
    name: 'John',
    data: [5, 3, 4, 7, 2],

  }, {
    name: 'Jane',
    data: [2, 2, 3, 2, 1]
  }, {
    name: 'Joe',
    data: [3, 4, 4, 2, 5]
  }]
});
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>

<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
mukesh.kumar
  • 1,100
  • 16
  • 30

2 Answers2

2

To achieve your a 2014 result, you can use a highchart wrapper, and change how the points are drawn, like this:

(function (H) {
  H.wrap(H.seriesTypes.column.prototype, 'drawPoints', function (proceed) {
    $.each(this.points, function (i,point) {
      let borderRadius = this.options.borderRadius;
      point.shapeArgs.y -=  borderRadius; //move the point down by borderRadius pixels
      point.shapeArgs.height +=  borderRadius; //add borderRadius pixels to the total height of a point (to cover the gap)
    });
    proceed.apply(this, Array.prototype.slice.call(arguments, 1));
  });
}(Highcharts));

    (function (H) {
      H.wrap(H.seriesTypes.column.prototype, 'drawPoints', function (proceed) {
        let seriesIndex = this.index
        $.each(this.points, function (i,point) {
         point.shapeArgs.y -= seriesIndex == 0 ? 0 : 5; //if it is not the first series, then move the series down 5 pixels
            point.shapeArgs.height +=  5; //add 5 pixels to the total height(to cover the gap)
          });
          proceed.apply(this, Array.prototype.slice.call(arguments, 1));
        });
    }(Highcharts));

Highcharts.chart('container', {
  chart: {
    type: 'column',
    spacingBottom: 0
  },
  title: {
    text: ''
  },
  xAxis: {
    categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas'],
    offset: 7,
    lineWidth: 0,
    tickLength: 0
  },
  yAxis: {
    min: 0,
    title: {
      text: ''
    },
    stackLabels: {
      enabled: false,
      style: {
        fontWeight: 'bold',
        color: 'gray'
      }
    },
    visible: false
  },
  legend: {
    align: 'center',

    verticalAlign: 'bottom',

  },
  tooltip: {
    headerFormat: '<b>{point.x}</b><br/>',
    pointFormat: '{series.name}: {point.y}<br/>Total: {point.stackTotal}'
  },
  plotOptions: {
    series: {

    },
    column: {
      stacking: 'normal',
      borderWidth: 0,
      borderRadius: 5,
      dataLabels: {
        enabled: true,
        color: 'white'
      }
    }
  },
  series: [{
    name: 'John',
    data: [5, 3, 4, 7, 2],

  }, {
    name: 'Jane',
    data: [2, 2, 3, 2, 1]
  }, {
    name: 'Joe',
    data: [3, 4, 4, 2, 5]
  }]
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>

<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>

Working JSFiddle: https://jsfiddle.net/ewolden/cyfv64ub/122/

If you wanted the 2015 result instead, you use the same function, like this:

(function(H) {
  H.wrap(H.seriesTypes.column.prototype, 'drawPoints', function(proceed) {
    let seriesIndex = this.index,
      firstIndex = this.chart.series[0].index,
      lastIndex = this.chart.series[this.chart.series.length - 1].index,
      borderRadius = this.options.borderRadius;

    this.options.borderRadius = 0; //Remove the border radius

    $.each(this.points, function(i, point) {
      if (seriesIndex != firstIndex && seriesIndex != lastIndex) {
        point.shapeArgs.y -= borderRadius; //make sure the middle points cover the outer points
        point.shapeArgs.height += borderRadius*2; 
      }
    });

    proceed.apply(this, Array.prototype.slice.call(arguments, 1));

    $.each(this.points, function(i, point) {
      if (seriesIndex == firstIndex || seriesIndex == lastIndex) {
        point.graphic.attr({
          r: borderRadius //set the borer radius to be whatever it was before to only the outer points
        });
      }
    });
  });
}(Highcharts));

I set the zIndex on the series manually, but that can be done as well. Just don't have time right now to find where to set it.

(function(H) {
  H.wrap(H.seriesTypes.column.prototype, 'drawPoints', function(proceed) {
    let seriesIndex = this.index,
      firstIndex = this.chart.series[0].index,
      lastIndex = this.chart.series[this.chart.series.length - 1].index,
      borderRadius = this.options.borderRadius;

    this.options.borderRadius = 0; //Remove the border radius

    $.each(this.points, function(i, point) {
      if (seriesIndex != firstIndex && seriesIndex != lastIndex) {
        point.shapeArgs.y -= borderRadius; //make sure the middle points cover the outer points
        point.shapeArgs.height += borderRadius*2; 
      }
    });

    proceed.apply(this, Array.prototype.slice.call(arguments, 1));

    $.each(this.points, function(i, point) {
      if (seriesIndex == firstIndex || seriesIndex == lastIndex) {
        point.graphic.attr({
          r: borderRadius //set the borer radius to be whatever it was before to only the outer points
        });
      }
    });
  });
}(Highcharts));

Highcharts.chart('container', {
  chart: {
    type: 'column',
    spacingBottom: 0
  },
  title: {
    text: ''
  },
  xAxis: {
    categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas'],
    offset: 7,
    lineWidth: 0,
    tickLength: 0
  },
  yAxis: {
    min: 0,
    title: {
      text: ''
    },
    stackLabels: {
      enabled: false,
      style: {
        fontWeight: 'bold',
        color: 'gray'
      }
    },
    visible: false
  },
  legend: {
    align: 'center',

    verticalAlign: 'bottom',

  },
  tooltip: {
    headerFormat: '<b>{point.x}</b><br/>',
    pointFormat: '{series.name}: {point.y}<br/>Total: {point.stackTotal}'
  },
  plotOptions: {
    series: {

    },
    column: {
      stacking: 'normal',
      borderWidth: 0,
      borderRadius: 5,
      dataLabels: {
        enabled: true,
        color: 'white'
      }
    }
  },
  series: [{
    name: 'John',
    data: [5, 3, 4, 7, 2],
    zIndex: 0
  }, {
    name: 'Jane',
    data: [2, 2, 3, 2, 1],
    zIndex: 1
  }, {
    name: 'Joe',
    data: [3, 4, 4, 2, 5],
    zIndex: 0
  }]
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>

<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>

Working JSFiddle: https://jsfiddle.net/ewolden/kqrLs3m8/

Do also note that I am manipulating the function drawPoints here, and as the docs state, it is only run once at the start. So if you start disabling/enabling series, then they will not necessarily look as you expect them to.

ewolden
  • 5,722
  • 4
  • 19
  • 29
  • Thanks. Works like charm on fiddle, will have to check on device. Just few questions - 1. If one value is too big and one data is too small (say, i pass data: [1, 30, 4, 7, 2]) wouldn't this hide the graph for smaller data? – mukesh.kumar Sep 05 '18 at 10:21
  • Question 2 - "make sure it is not the first or last point in the series" - how can I do this? I have never used Wrapper functions, so will this work on older versions of high charts, say v4.2? – mukesh.kumar Sep 05 '18 at 10:23
  • 1
    It could potentionally remove the very small data, yes. Of course you can add checks for that, like, if the `point.shapeArgs.y <= 5` then change how it behaves. As for the second question, let me add it to the fiddle then... And yes, quoting: *Since version 2.3, Highcharts is built in a modular way with extensions in mind.* – ewolden Sep 05 '18 at 10:27
  • 'point.shapeArgs.height += X' should this X value be same as borderRadius? – mukesh.kumar Sep 05 '18 at 10:36
  • Yes, that is correct. I updated the fiddle to answer question 2 and reflect the borderRadius change. – ewolden Sep 05 '18 at 11:22
0

To achieve a 2015 result you can use this rounded-corners.js plugin: https://rawgit.com/highcharts/rounded-corners/master/rounded-corners.js

series: [{
  data: [307, 231, 335, 203],
  borderRadiusTopLeft: '20px',
  borderRadiusTopRight: '20px'
}, {
  data: [183, 196, 547, 408]
}, {
  data: [414, 441, 514, 627],
  borderRadiusBottomLeft: '20px',
  borderRadiusBottomRight: '20px'
}]

jsFiddle: https://jsfiddle.net/BlackLabel/tdp1y0wb

raf18seb
  • 2,096
  • 1
  • 7
  • 19
  • Thanks, @raf18seb for the answer, but I already tried this approach. The problem with this is that if either the top or bottom element is 0, the rounded corners are gone. Although, We can apply the logic like if the topmost data is 0 apply the rounded corner to the second element and so forth, but this will be a very dirty hack! – mukesh.kumar Feb 08 '19 at 18:23
  • 1
    Thanks for pointing this. You are right, it's obviously unwanted behavior. I will report this and we will try to improve this module in the future ;) – raf18seb Feb 09 '19 at 10:47