Stacked column series with rounded corners

This quick tutorial shows how you can inventively use adapters to apply custom formatting options to stacks of columns.

The task

Suppose, we have a stacked column chart. And want the top of the each stack to have rounded corners.

Here's our target chart, BTW:

See the Pen amCharts 4: Rounded-corner stacks (base chart) by amCharts (@amcharts) on CodePen.24419

Rounding the corners

One way to go about rounding the corners on this chart is to simply set cornerRadiusTopLeft and cornerRadiusTopRight on the columns of the top-most series (the one that is added last):

series3.columns.template.column.cornerRadiusTopLeft = 10;
series3.columns.template.column.cornerRadiusTopRight = 10;
series3.columns.template.column.cornerRadiusTopLeft = 10;
series3.columns.template.column.cornerRadiusTopRight = 10;
{
  // ...
  "series": [{
    // ...
  }, {
    // ...
  }, {
    // ...
    "columns": {
      "column": {
        "cornerRadiusTopLeft": 10,
        "cornerRadiusTopRight": 10
      }
    }
  }]
}

See the Pen amCharts 4: Rounded-corner stacks (1) by amCharts (@amcharts) on CodePen.24419

If that's how your data looks like, mission accomplished - go home.

Inconsistent data

But wait. What if your data is inconsistent. Say, not all stacks have all the values. One stack might have 3 columns, while the other 2, or 1.

Or, what if the top-most stack gets toggled off?

We need some way to selectively apply corner radius to the last column in each stack.

That's where adapters come in.

We'll set up a custom function which is used to determine whether radius needs to be applied to this specific column. The function will check whether the column, for which the function is called is the last in stack.

function cornerRadius(radius, item) {
  let dataItem = item.dataItem;
  
  // Find the last series in this stack
  let lastSeries;
  chart.series.each(function(series) {
    if (dataItem.dataContext[series.dataFields.valueY] && !series.isHidden && !series.isHiding) {
      lastSeries = series;
    }
  });
  
  // If current series is the one, use rounded corner
  return dataItem.component == lastSeries ? 10 : radius;
}

// ...

series.columns.template.column.adapter.add("cornerRadiusTopLeft", cornerRadius);
series.columns.template.column.adapter.add("cornerRadiusTopRight", cornerRadius);

// ...

series.columns2.template.column.adapter.add("cornerRadiusTopLeft", cornerRadius);
series.columns2.template.column.adapter.add("cornerRadiusTopRight", cornerRadius);

// ...

series.columns3.template.column.adapter.add("cornerRadiusTopLeft", cornerRadius);
series.columns3.template.column.adapter.add("cornerRadiusTopRight", cornerRadius);
function cornerRadius(radius, item) {
  var dataItem = item.dataItem;
  
  // Find the last series in this stack
  var lastSeries;
  chart.series.each(function(series) {
    if (dataItem.dataContext[series.dataFields.valueY] && !series.isHidden && !series.isHiding) {
      lastSeries = series;
    }
  });
  
  // If current series is the one, use rounded corner
  return dataItem.component == lastSeries ? 10 : radius;
}

// ...

series.columns.template.column.adapter.add("cornerRadiusTopLeft", cornerRadius);
series.columns.template.column.adapter.add("cornerRadiusTopRight", cornerRadius);

// ...

series.columns2.template.column.adapter.add("cornerRadiusTopLeft", cornerRadius);
series.columns2.template.column.adapter.add("cornerRadiusTopRight", cornerRadius);

// ...

series.columns3.template.column.adapter.add("cornerRadiusTopLeft", cornerRadius);
series.columns3.template.column.adapter.add("cornerRadiusTopRight", cornerRadius);
function cornerRadius(radius, item) {
  var dataItem = item.dataItem;
  
  // Find the last series in this stack
  var lastSeries;
  chart.series.each(function(series) {
    if (dataItem.dataContext[series.dataFields.valueY] && !series.isHidden && !series.isHiding) {
      lastSeries = series;
    }
  });
  
  // If current series is the one, use rounded corner
  return dataItem.component == lastSeries ? 10 : radius;
}

am4core.createFromConfig({
  // ...
  "series": [{
    // ...
  }, {
    // ...
  }, {
    // ...
    "columns": {
      "column": {
        "adapter": {
          "cornerRadiusTopLeft": cornerRadius,
          "cornerRadiusTopRight": cornerRadius
        }
      }
    }
  }]
}, "chartdiv", "XYChart");

See the Pen amCharts 4: Rounded-corner stacks by amCharts (@amcharts) on CodePen.24419