Google Visualization cluster multiple column stacks having goal lines

本秂侑毒 提交于 2021-02-11 17:42:12

问题


My goal is to cluster two or more column stacks having corresponding goal lines.

What is the best way to get this chart?

My initial thought was to use a ComboChart. I can only accomplish a single stack bar with multiple lines.

I was able to try a 'bar' type chart (below snippet) and accomplished two clustered bar stacks using two axis but cannot get lines. I also think this could be limiting in the future to only two stacks.

Any ideas?? Thanks as always!

google.setOnLoadCallback(drawChart);

      function drawChart() {
        var data = google.visualization.arrayToDataTable([
          ['Year', 'Sales', 'Expenses', 'Goal Line 1', 'Profit', 'Bonus', 'Goal Line 2'],
          ['2014', 1000, 400, 400, 200, 50, 500],
          ['2015', 1170, 460, 400, 250, 2000, 500],
          ['2016', 660, 1120, 400, 300, 10, 500],
          ['2017', 1030, 540, 400, 350, 15, 500]
        ]);

        var options = {

          bars: 'vertical',
          isStacked: true,
          series: {
            0: { targetAxisIndex: 1 },
            1: { targetAxisIndex: 1 },
            2: { targetAxisIndex: 1 }, //this should be a line 
            3: { targetAxisIndex: 2 },
            4: { targetAxisIndex: 2 },
            5: { targetAxisIndex: 2 } //this should be a line 
          },
          vAxes: {

          },

          height: 400,
          xcolors: ['#1b9e77', '#d95f02', '#7570b3']
        };

        var chart = new google.charts.Bar(document.getElementById('chart_div'));

        chart.draw(data, google.charts.Bar.convertOptions(options));


      }
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1.1','packages':['bar']}]}"></script>
       <div id="chart_div"></div>
    <br/>

============UPDATE=============
So I've tried now a ColumnChart with two axis. I can see that I get two stacks now but they are one in front of the other. You can see that the red/orange stack sticks out from behind the blue/purple stack.

Is it possible to offset stack 1 to the left and stack 2 to the right? Then I think visually the chart could work for my application.

      google.charts.load('current', {
        'packages': ['corechart']
      });
      google.charts.setOnLoadCallback(drawVisualization);

      function drawVisualization() {
        // Some raw data (not necessarily accurate)
        var data = google.visualization.arrayToDataTable([
          ['Month', 'Ax1 line', 'Stack1 1', 'Stack1 2', 'Ax2 line', 'Stack2 1', 'Stack2 2'],
          ['2004/05', 10, 110, 210, 710, 810, 910],
          ['2005/06', 20, 120, 220, 720, 820, 920],
          ['2006/07', 30, 130, 230, 730, 830, 930],
          ['2007/08', 40, 140, 240, 740, 840, 940],
          ['2008/09', 50, 150, 250, 750, 850, 950]
        ]);

        var options = {
          title: 'Monthly Coffee Production by Country',
          vAxis: {
            title: 'Cups'
          },
          hAxis: {
            title: 'Month'
          },
          seriesType: 'bars',
          series: {
            0: { type: 'line', targetAxisIndex: 1},
            1: { type: 'bars', targetAxisIndex: 1},
          	2: { type: 'bars', targetAxisIndex: 1},
            3: { type: 'line', targetAxisIndex: 2},   
            4: { type: 'bars', targetAxisIndex: 2},  
            5: { type: 'bars', targetAxisIndex: 2}
          },
          isStacked: true
        };

        var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));
        chart.draw(data, options);
      }
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
       <div id="chart_div" style="width: 900px; height: 500px;"></div>
   

回答1:


the problem here, material charts do not support combo charts (adding the line).
and classic charts do not support multiple stacks.

however, by adding a line series in between the columns series,
and moving one to another y-axis,
the stacks will become separated, though on top of one another.

on the chart's 'ready' event, we can reduce the size of both stacks by half,
then move the second stack to the right.

the biggest problem here is on interactivity.
the chart will reset all the columns back to their original position on interactivity, such as hover.
as such, we must use a MutationObserver to prevent the reset.

however, there is still a couple minor issues I haven't worked out.

when you hover the first stack, the tooltip appears above the original position.
and there is a highlight surrounding the original position.

here is what I have so far...

google.charts.load('current', {
  packages: ['corechart']
}).then(function () {
  var data = google.visualization.arrayToDataTable([
    ['Month', 'Ax1 line', 'Stack1 1', 'Stack1 2', 'Ax2 line', 'Stack2 1', 'Stack2 2'],
    ['2004/05', 10, 110, 210, 710, 810, 910],
    ['2005/06', 20, 120, 220, 720, 820, 920],
    ['2006/07', 30, 130, 230, 730, 830, 930],
    ['2007/08', 40, 140, 240, 740, 840, 940],
    ['2008/09', 50, 150, 250, 750, 850, 950]
  ]);

  var options = {
    title: 'Monthly Coffee Production by Country',
    vAxis: {
      title: 'Cups'
    },
    hAxis: {
      title: 'Month'
    },
    seriesType: 'bars',
    series: {
      0: { type: 'line', targetAxisIndex: 0},
      1: { type: 'bars', targetAxisIndex: 0},
      2: { type: 'bars', targetAxisIndex: 0},
      3: { type: 'line', targetAxisIndex: 1},
      4: { type: 'bars', targetAxisIndex: 1},
      5: { type: 'bars', targetAxisIndex: 1}
    },
    isStacked: true,
    legend: {
      maxLines: 2,
      position: 'top'
    },
    tooltip: {
      target: 'both'
    }
  };

  var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));
  var chartLayout;
  var chartBounds;
  var legendColors = [];
  var saveCoords = {};
  google.visualization.events.addListener(chart, 'ready', function () {
    // init chart elements
    chartLayout = chart.getChartLayoutInterface();
    chartBounds = chartLayout.getChartAreaBoundingBox();
    var bars = chart.getContainer().getElementsByTagName('rect');

    // find legend markers
    Array.prototype.forEach.call(bars, function (bar) {
      var color = bar.getAttribute('fill');
      var xCoord = parseFloat(bar.getAttribute('x'));
      var yCoord = parseFloat(bar.getAttribute('y'));
      if ((xCoord >= chartBounds.left) && (yCoord < chartBounds.top) && (color !== '#ffffff')) {
        legendColors.push(color);
      }
    });

    // find bars
    Array.prototype.forEach.call(bars, function (bar, index) {
      var xCoord = parseFloat(bar.getAttribute('x'));
      var yCoord = parseFloat(bar.getAttribute('y'));
      if ((xCoord > chartBounds.left) && (yCoord > chartBounds.top)) {
        var color = bar.getAttribute('fill');
        var width = parseFloat(bar.getAttribute('width')) / 2;
        bar.setAttribute('width', width);
        saveCoords[xCoord + yCoord] = {
          width: width,
          x: xCoord
        };
        if (legendColors.indexOf(color) > 1) {
          bar.setAttribute('x', xCoord + width);
          saveCoords[xCoord + yCoord].x = xCoord + width;
          saveCoords[xCoord + width + yCoord] = saveCoords[xCoord + yCoord];
        }
      }
    });

    var observer = new MutationObserver(function () {
      // reset bars
      var bars = chart.getContainer().getElementsByTagName('rect');
      Array.prototype.forEach.call(bars, function (bar, index) {
        var xCoord = parseFloat(bar.getAttribute('x'));
        var yCoord = parseFloat(bar.getAttribute('y'));
        if ((xCoord > chartBounds.left) && (yCoord > chartBounds.top)) {
          var color = bar.getAttribute('fill');
          if (saveCoords.hasOwnProperty(xCoord + yCoord)) {
            bar.setAttribute('width', saveCoords[xCoord + yCoord].width);
            bar.setAttribute('x', saveCoords[xCoord + yCoord].x);
          }
        }
      });
    });
    observer.observe(chart.getContainer(), {
      childList: true,
      subtree: true
    });
  });

  chart.draw(data, options);
  window.addEventListener('resize', function () {
    chart.draw(data, options);
  });
});
#chart_div {
  height: 400px;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Note: I also found an issue once posted here. For now, the legend must be fully displayed on one line. To run the example here, you must run the snippet, then click Full page.



来源:https://stackoverflow.com/questions/62284910/google-visualization-cluster-multiple-column-stacks-having-goal-lines

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!