Search code examples
javascriptamcharts

How to unstack the line series on a chart: AMCharts3


So on my chart I have bars and lines and I want the bar series to be stacked like in the image below but I don't want the line series to be stacked. Example of the current Chart

In the example image, the top line series value for the first 3 points is 100,000 but it sits at 150,000, whereas the bottom line series is valued at 50,000 and sits at 50,000 so I believe they are stacking which I don't want to happen.

I want the chart to look exactly the same but having the line series to sit at the correct value on the yAxis.

let {dataProvider, displayCurrency} = this.props;
    var config = {
        "type":"serial",
        "categoryField":"category",
        "fontFamily":"arial",
        "categoryAxis":{
            "gridPosition":"start",
            "title":"Time",
            "titleRotation":0
        },
        "trendLines":[],
        "graphs":[
            {
                "balloonText": "[[title]] of [[category]]:[[value]]",
                "balloonFunction" : formatGraphMoneyBallon("Hedging"),
                "balloonColor": "#000066",
                "fillColors": "#000066",
                "lineColor": "#000066",
                "fillAlphas": 1,
                "id": "coverUpper",
                "title": "Hedging",
                "newStack": false,
                "type": "column",
                "valueField": "hedging"
            },
            {
                "balloonText": "[[title]] of [[category]]:[[value]]",
                "balloonFunction" : formatGraphMoneyBallon("Maximum Additional Hedging"),
                "balloonColor": "#000066",
                "fillColors": "#000066",
                "lineColor": "#000066",
                "fillAlphas": 1,
                "id": "coverAdditional",
                "title": "Maximum Additional Hedging",
                "type": "column",
                "newStack": false,
                "valueField": "maxAdditionalHedging",
                "pattern": {
                    "url": "https://www.amcharts.com/lib/3/patterns/black/pattern5.png",
                    "width": 4,
                    "height": 4
                      }
            }, {
                "balloonText": "[[title]] of [[category]]:[[value]]",
                "balloonFunction" : formatGraphMoneyBallon("Forecasted Cashflows"),
                "fillAlphas": 1,
                "balloonColor": "#B0DE09",
                "fillColors": "#B0DE09",
                "lineColor": "#B0DE09",
                "id": "cfGraph",
                "title": "Forecasted Cashflows",
                "type": "column",
                "newStack": true,
                "valueField": "exposure"
            }, {
                "balloonText": "[[title]] of [[category]]:[[value]]",
                "balloonFunction" : formatGraphMoneyBallon("Policy Minimum"),
                "bullet": "triangleUp",
                "balloonColor": "#014310",
                "fillColors": "#014310",
                "lineColor": "#014310",
                "bulletAlpha": 0.6,
                "dashLength": 7,
                "id": "polMin",
                "valueAxis": "dollarAxis",
                "newStack": true,
                "title": "Policy Minimum",
                "valueField": "minHedge"
            }, {
                "balloonText": "[[title]] of [[category]]:[[value]]",
                "balloonFunction" : formatGraphMoneyBallon("Policy Maximum"),
                "bullet": "triangleDown",
                "type": "line",
                "balloonColor": "#014310",
                "fillColors": "#014310",
                "lineColor": "#014310",
                "bulletAlpha": 0.6,
                "dashLength": 7,
                "newStack": true,
                "id": "polMax",
                "valueAxis": "dollarAxis",
                "title": "Policy Maximum",
                "valueField": "maxHedge"
            }
        ],
        "guides":[],
        "valueAxes":[
            {"id":"dollarAxis","stackType":"regular","title":"Amount " + displayCurrency}
        ],
        "allLabels":[],
        "balloon":{},
        "legend":{
            "enabled":true,
            "useGraphSettings":true,
            "spacing": 0,
            "valueWidth":15
        },
        "export":{"enabled":true},
        "dataProvider":dataProvider
    }

Solution

  • Stacking is accomplished through the axis. If you want the lines to not stack, put them in a separate value axis that does not have stackType set and link the graphs to the value axis id through the valueAxis property. You'll also want to consider disabling the lines' value axis' labels, grid and sync the min/max as well:

    graphs:[{
      type: "line",
      // ...
      valueAxis: "dollarAxis-line"
     },
      // ...
    ],
    valueAxes: [
      {
        "id": "dollarAxis",
        "includeHidden": true,
         // ...
      },
      {
        "id": "dollarAxis-line",
        "labelsEnabled": false,
        "gridAlpha": 0,
        "axisAlpha": 0,
      }
    ],
    "listeners": [{
      "event": "init",
      "method": function(e) {
        // sync axes
        e.chart.valueAxes[0].maximum = Math.max(e.chart.valueAxes[0].max, e.chart.valueAxes[1].max);
        e.chart.valueAxes[0].minimum = Math.min(e.chart.valueAxes[0].min, e.chart.valueAxes[1].min);
        e.chart.valueAxes[1].maximum = Math.max(e.chart.valueAxes[0].max, e.chart.valueAxes[1].max);
        e.chart.valueAxes[1].minimum = Math.min(e.chart.valueAxes[0].min, e.chart.valueAxes[1].min);
        e.chart.validateData();
    
      }
    }]
    

    Edit: If you're starting with an empty chart, you may want to consider using the dataUpdated event instead so that it will resync the axes after you update the dataProvider. The validateData method triggers this event, so you'll want to wrap this call in a conditional that checks for a flag so that the chart doesn't keep calling this method infinitely.

      "listeners": [{
        "event": "dataUpdated",
        "method": function(e) {
          setTimeout(function() {
            if (!e.chart.updating) {
              e.chart.updating = true;
              // sync axes
              e.chart.valueAxes[0].maximum = undefined;
              e.chart.valueAxes[0].minimum = undefined;
              e.chart.valueAxes[1].maximum = undefined;
              e.chart.valueAxes[1].minimum = undefined;
              e.chart.validateData();
              e.chart.valueAxes[1].maximum = e.chart.valueAxes[0].maximum = Math.max(e.chart.valueAxes[0].max, e.chart.valueAxes[1].max);
              e.chart.valueAxes[1].minimum = e.chart.valueAxes[0].minimum = Math.min(e.chart.valueAxes[0].min, e.chart.valueAxes[1].mix);
              e.chart.validateData();
              e.chart.updating = false;
            }
          }, 100);
        }
      }]
    

    Demo below:

    var chart = AmCharts.makeChart("chartdiv", {
      "type": "serial",
      "categoryField": "category",
      "fontFamily": "arial",
      "categoryAxis": {
        "gridPosition": "start",
        "title": "Time",
        "titleRotation": 0
      },
      "trendLines": [],
      "graphs": [{
          "balloonText": "[[title]] of [[category]]:[[value]]",
          "balloonColor": "#000066",
          "fillColors": "#000066",
          "lineColor": "#000066",
          "fillAlphas": 1,
          "id": "coverUpper",
          "title": "Hedging",
          "newStack": false,
          "type": "column",
          "valueField": "hedging"
        },
        {
          "balloonText": "[[title]] of [[category]]:[[value]]",
          "balloonColor": "#000066",
          "fillColors": "#000066",
          "lineColor": "#000066",
          "fillAlphas": 1,
          "id": "coverAdditional",
          "title": "Maximum Additional Hedging",
          "type": "column",
          "newStack": false,
          "valueField": "maxAdditionalHedging",
          "pattern": {
            "url": "https://www.amcharts.com/lib/3/patterns/black/pattern5.png",
            "width": 4,
            "height": 4
          }
        }, {
          "balloonText": "[[title]] of [[category]]:[[value]]",
          "fillAlphas": 1,
          "balloonColor": "#B0DE09",
          "fillColors": "#B0DE09",
          "lineColor": "#B0DE09",
          "id": "cfGraph",
          "title": "Forecasted Cashflows",
          "type": "column",
          "newStack": true,
          "valueField": "exposure"
        }, {
          "balloonText": "[[title]] of [[category]]:[[value]]",
          "bullet": "triangleUp",
          "balloonColor": "#014310",
          "fillColors": "#014310",
          "lineColor": "#014310",
          "bulletAlpha": 0.6,
          "dashLength": 7,
          "id": "polMin",
          "valueAxis": "dollarAxis-line",
          "title": "Policy Minimum",
          "valueField": "minHedge"
        }, {
          "balloonText": "[[title]] of [[category]]:[[value]]",
          "bullet": "triangleDown",
          "type": "line",
          "balloonColor": "#014310",
          "fillColors": "#014310",
          "lineColor": "#014310",
          "bulletAlpha": 0.6,
          "dashLength": 7,
          "id": "polMax",
          "valueAxis": "dollarAxis-line",
          "title": "Policy Maximum",
          "valueField": "maxHedge"
        }
      ],
      "guides": [],
      "valueAxes": [{
          "id": "dollarAxis",
          "stackType": "regular",
          "includeHidden": true,
          "title": "Amount ($)"
        },
        {
          "id": "dollarAxis-line",
          "stackType": "none",
          "labelsEnabled": false,
          "gridAlpha": 0,
          "axisAlpha": 0
        }
      ],
      "allLabels": [],
      "balloon": {},
      "legend": {
        "enabled": true,
        "useGraphSettings": true,
        "spacing": 0,
        "valueWidth": 15
      },
      "export": {
        "enabled": true
      },
      "dataProvider": [{
          "category": "Category 1",
          "minHedge": 151218,
          "hedging": 111436,
          "maxHedge": 236849,
          "maxAdditionalHedging": 272065,
          "additionalHedging": 233236,
          "exposure": 163731
        },
        {
          "category": "Category 2",
          "minHedge": 150517,
          "hedging": 137299,
          "maxHedge": 258805,
          "maxAdditionalHedging": 254932,
          "additionalHedging": 183495,
          "exposure": 111847
        },
        {
          "category": "Category 3",
          "minHedge": 158456,
          "hedging": 198219,
          "maxHedge": 231274,
          "maxAdditionalHedging": 259615,
          "additionalHedging": 187108,
          "exposure": 168738
        },
        {
          "category": "Category 4",
          "minHedge": 167931,
          "hedging": 173121,
          "maxHedge": 263334,
          "maxAdditionalHedging": 260449,
          "additionalHedging": 229972,
          "exposure": 156174
        },
        {
          "category": "Category 5",
          "minHedge": 164449,
          "hedging": 157823,
          "maxHedge": 268980,
          "maxAdditionalHedging": 268896,
          "additionalHedging": 180031,
          "exposure": 100893
        },
        {
          "category": "Category 6",
          "minHedge": 166465,
          "hedging": 128504,
          "maxHedge": 241036,
          "maxAdditionalHedging": 269858,
          "additionalHedging": 230233,
          "exposure": 117158
        },
        {
          "category": "Category 7",
          "minHedge": 167117,
          "hedging": 110649,
          "maxHedge": 229749,
          "maxAdditionalHedging": 262671,
          "additionalHedging": 201500,
          "exposure": 188282
        },
        {
          "category": "Category 8",
          "minHedge": 151167,
          "hedging": 123489,
          "maxHedge": 249919,
          "maxAdditionalHedging": 268805,
          "additionalHedging": 206302,
          "exposure": 120797
        },
        {
          "category": "Category 9",
          "minHedge": 154961,
          "hedging": 185898,
          "maxHedge": 269185,
          "maxAdditionalHedging": 271949,
          "additionalHedging": 173116,
          "exposure": 184312
        },
        {
          "category": "Category 10",
          "minHedge": 154385,
          "hedging": 141307,
          "maxHedge": 234509,
          "maxAdditionalHedging": 274889,
          "additionalHedging": 216132,
          "exposure": 111974
        }
      ],
      "listeners": [{
        "event": "dataUpdated",
        "method": function(e) {
          setTimeout(function() {
            if (!e.chart.updating) {
              e.chart.updating = true;
              // sync axes
              e.chart.valueAxes[0].maximum = undefined;
              e.chart.valueAxes[0].minimum = undefined;
              e.chart.valueAxes[1].maximum = undefined;
              e.chart.valueAxes[1].minimum = undefined;
              e.chart.validateData();
              e.chart.valueAxes[1].maximum = e.chart.valueAxes[0].maximum = Math.max(e.chart.valueAxes[0].max, e.chart.valueAxes[1].max);
              e.chart.valueAxes[1].minimum = e.chart.valueAxes[0].minimum = Math.min(e.chart.valueAxes[0].min, e.chart.valueAxes[1].mix);
              e.chart.validateData();
              e.chart.updating = false;
            }
          }, 100);
        }
      }]
    });
    
    function generateData() {
      var data = [];
      for (var i = 0; i < 10; ++i) {
        data.push({
          "category": "Category " + (i + 1),
          "minHedge": Math.floor(Math.random() * 100000 + 200000),
          "hedging": Math.floor(Math.random() * 150000 + 225000),
          "maxHedge": Math.floor(Math.random() * 250000 + 200000),
          "maxAdditionalHedging": Math.floor(Math.random() * 100000 + 200000),
          "additionalHedging": Math.floor(Math.random() * 150000 + 275000),
          "exposure": Math.floor(Math.random() * 225000 + 375000)
        });
      }
      chart.dataProvider = data;
      chart.validateData();
    }
    <script type="text/javascript" src="https://www.amcharts.com/lib/3/amcharts.js"></script>
    <script type="text/javascript" src="https://www.amcharts.com/lib/3/serial.js"></script>
    <div id="chartdiv" style="width: 100%; height: 300px;"></div>
    <button onclick="generateData()">Generate Data</button>