1
votes

In a grouped column chart of two groups, I would like to center the column when the other column has height 0.

So for example,

enter image description here

The bars for the years 2013 to 2016 should be centered on the year label. This is because the second value in the group is 0, so the height of the bar is 0, so no bar displays:

  data = [
     ["2012", 900, 950],
     ["2013", 1000, 0],
     ["2014", 1170, 0],
     ["2015", 1250, 0],
     ["2016", 1530, 0]
  ];

How can I do this with google charts?

1
no options available out of the box, you would need to move the bar (<rect>) element manually, on the chart's 'ready' event...WhiteHat
@WhiteHat Thanks. Would I have to manually move the rect element using D3.js?Kingamere
you can use regular javascript, but it won't be easy. after moving the bars, the chart will move them back if someone hovers, clicks, etc. you'd have to turn off interactivity or use a MutationObserver to know if the chart moved it back. Plus it's difficult to determine which <rect> elements represent the bars, vs. the bars in the legend, chart area, gridlines, etc., it would be an ordeal, but it is possible. I can get you started, if you really want...WhiteHat

1 Answers

1
votes

see following working snippet...

the bars are centered where their counterpart is blank.
however, it breaks once the user hovers a bar.

the bars are represented by <rect> elements,
which are used to draw the chart itself, the gridlines, the legend bars, etc.
3 <rect> elements are used to highlight the hovered bar.

this is what breaks the code below, it throws off the routine to find the bars.

here's how it works now...

there will be the same number of bars / <rect> elements as there are rows and series,
even if a bar is not visible.
they will be next to last in the list of elements.
the last <rect> element is the x-axis.

the code below works backwards, skipping the last element,
and counts the number of rows / series to gather the bars that may need to be moved.

when the users hovers, there are 3 elements inserted, so the routine will need to change to accommodate.
and they will also need to be moved in order to highlight properly.

otherwise, you can just turn off interactivity and be done...

enableInteractivity: false

google.charts.load('current', {
  packages: ['corechart']
}).then(function () {
  var data = google.visualization.arrayToDataTable([
    ["Year", "Asia", "Mama"],
    ["2012", 900, 950],
    ["2013", 1000, 0],
    ["2014", 1170, 0],
    ["2015", 1250, 0],
    ["2016", 1530, 0]
  ]);

  var options = {
    chartArea: {
      height: '100%',
      width: '100%',
      top: 32,
      left: 48,
      right: 128,
      bottom: 48
    },
    height: 400,
    width: '100%'
  };

  var container = document.getElementById('chart');
  var chart = new google.visualization.ColumnChart(container);

  google.visualization.events.addListener(chart, 'ready', function () {
    // get chart layout
    var chartLayout = chart.getChartLayoutInterface();

    // create mutation observer
    var observer = new MutationObserver(function () {
      // get bar elements
      var rects = container.getElementsByTagName('rect');
      var barLength = data.getNumberOfRows() * (data.getNumberOfColumns() - 1);
      var bars = [];
      for (var i = rects.length - 1; i > ((rects.length - 1) - (barLength + 1)); i--) {
        if (i < (rects.length - 1)) {
          bars.unshift(rects[i]);
        }
      }

      // process each row
      for (var r = 0; r < data.getNumberOfRows(); r++) {
        // process each series
        for (var s = 1; s < data.getNumberOfColumns(); s++) {
          // get chart element bounds
          var boundsBar = chartLayout.getBoundingBox('bar#' + (s - 1) + '#' + r);
          var boundsLabel = chartLayout.getBoundingBox('hAxis#0#label#' + r);

          // determine if bar is hidden
          if (boundsBar.height < 1) {
            // determine series shown, new x coordinate
            var seriesShown = (s === 1) ? 1 : 0;
            var xCoord = boundsLabel.left + (boundsLabel.width / 2);

            // move bar
            bars[r + (data.getNumberOfRows() * seriesShown)].setAttribute('x', (xCoord - (boundsBar.width / 2)));
          }
        }
      }
    });
    observer.observe(container, {
      childList: true,
      subtree: true
    });
  });

  chart.draw(data, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>