Search code examples
javascriptbar-chartamcharts

amcharts 4 wrong stacking


My goal is to combine a clustered and a stacked chart with amcharts 4. Each stack can contain both positive and negative values.

I am using a this example Stacked and Clustered Column Chart, but I modified the data in chart.data.

/**
 * ---------------------------------------
 * This demo was created using amCharts 4.
 * 
 * For more information visit:
 * https://www.amcharts.com/
 * 
 * Documentation is available at:
 * https://www.amcharts.com/docs/v4/
 * ---------------------------------------
 */

// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end

// Create chart instance
var chart = am4core.create("chartdiv", am4charts.XYChart);

// Add data
chart.data = [ {
  "year": "2003",
  "europe": -2.5,
  "namerica": -2.5,
  "asia": 2.1,
  "lamerica": 1.2,
  "meast": 0.2,
  "africa": -1
}, {
  "year": "2004",
  "europe": 2.6,
  "namerica": 2.7,
  "asia": 2.2,
  "lamerica": 1.3,
  "meast": 0.3,
  "africa": 0.1
}, {
  "year": "2005",
  "europe": 2.8,
  "namerica": 2.9,
  "asia": 2.4,
  "lamerica": 1.4,
  "meast": 0.3,
  "africa": 0.1
} ];

// Create axes
var categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "year";
categoryAxis.title.text = "Local country offices";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 20;
categoryAxis.renderer.cellStartLocation = 0.1;
categoryAxis.renderer.cellEndLocation = 0.9;

var  valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.title.text = "Expenditure (M)";

// Create series
function createSeries(field, name, stacked) {
  var series = chart.series.push(new am4charts.ColumnSeries());
  series.dataFields.valueY = field;
  series.dataFields.categoryX = "year";
  series.name = name;
  series.columns.template.tooltipText = "{name}: [bold]{valueY}[/]";
  series.stacked = stacked;
  series.columns.template.width = am4core.percent(95);
}

createSeries("europe", "Europe", false);
createSeries("namerica", "North America", true);
createSeries("asia", "Asia", false);
createSeries("lamerica", "Lating America", true);
createSeries("meast", "Middle East", true);
createSeries("africa", "Africa", true);

// Add legend
chart.legend = new am4charts.Legend();
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}

#chartdiv {
  width: 100%;
  height: 500px;
}
<script src="https://www.amcharts.com/lib/4/core.js"></script>
<script src="https://www.amcharts.com/lib/4/charts.js"></script>
<script src="https://www.amcharts.com/lib/4/themes/animated.js"></script>
<div id="chartdiv"></div>

This is the result I got

As you see the stacking for the first element is wrong, as i stacks over the last negative element, not over the current stack.

I manage to get the expected behavior by adding empty series between the stacks; but then I am left with a blank space I am unable to remove. unsatisfying workaround

Is there a way to either:

a) Correctly stack the series

b) Totally hide an empty series, including its space on the category axis?

Thank you


Solution

  • I found the correct workaround for my needs: insert empty series with stacked=false to separate the stacks but put stacked=true for all other series:

    createSeries("europe", "Europe", true);
    createSeries("namerica", "North America", true);
    // empty series to separate stacks //
       var series = chart.series.push(new am4charts.ColumnSeries());
       series.dataFields.valueY = '';
       series.dataFields.categoryX = '';
    ////
    createSeries("asia", "Asia", true);
    createSeries("lamerica", "Lating America", true);
    createSeries("meast", "Middle East", true);
    createSeries("africa", "Africa", true);
    

    this way I managed to get the expected result without having to re-order the series

    The main drawback is that an empty element is created in the legend. In my case it didn't matter because I used a custom legend created in an external div.