0

How can i display different labels under each column and also have another label for the entire group?

As you can see in the picture below i want to use fontawesome icons for each column but another label for the main group. I found other answers how to use fa icons but don't know how to position them under each bar.

The trendlines which connect distinct columns are not so important but would be great if i can find out how to add them also.

Also the chart needs to be scrollable as it can hold lots of data and the labels need to be shown. I found some examples with scroll as well.

Any info is highly appreciated. Or are there any other open source chart frameworks in which i could implement this or something similar to fit my needs?

Chart

Radu Turtoi
  • 189
  • 9

1 Answers1

2

using google charts...

on the chart's 'ready' event,
you can use chart method --> getChartLayoutInterface()

var chartLayout = chart.getChartLayoutInterface();

the interface has a method --> getBoundingBox()
which will return the position of requested chart element
to get the position of a bar...

var barBounds = chartLayout.getBoundingBox('bar#0#0');

where the first #0 is the series, and the second is the row,
'bar#0#0' would get the first bar on the first row

we can also get the position of the axis label...

var labelBounds = chartLayout.getBoundingBox('hAxis#0#label#0');

we can use a combination of the bar and label bounds to position the icon
we want the left position from the bar, and the top position from the label

see following working snippet,
a column property is used to store the icon name,
the x-axis labels are used for the group
once the icon is in position, the axis label is moved down to make room

google.charts.load('current', {
  packages: ['corechart']
}).then(function () {
  var data = new google.visualization.DataTable({
    cols: [
      {label: 'x', type: 'string'},
      {label: 'file', type: 'number', p: {icon: 'fa-file'}},
      {label: 'database', type: 'number', p: {icon: 'fa-database'}},
      {label: 'random', type: 'number', p: {icon: 'fa-random'}},
    ],
    rows: [
      {c:[{v: 'Label 1'}, {v: 11}, {v: 6}, {v: 15}]},
      {c:[{v: 'Label 2'}, {v: 8}, {v: null}, {v: 12}]},
      {c:[{v: 'Label 3'}, {v: 6}, {v: 8}, {v: 18}]},
      {c:[{v: 'Label 4'}, {v: null}, {v: 1}, {v: 16}]},
    ]
  });

  var options = {
    bar: {
      groupWidth: '50%',
      width: 20
    },
    colors: ['#ffc107', '#d32f2f', '#00bcd4'],
    height: 600,
    legend: 'none',
    title: 'Capacities',
    width: 1000,
  };

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

  google.visualization.events.addListener(chart, 'ready', function () {
    // initialize bounds variables
    var axisLabels = container.getElementsByTagName('text');
    var chartLayout = chart.getChartLayoutInterface();
    var chartBounds = chartLayout.getChartAreaBoundingBox();
    var containerBounds = container.getBoundingClientRect();
    var labelIndex;

    // add icons
    for (var r = 0; r < data.getNumberOfRows(); r++) {
      var barBounds;
      var icon;
      var iconBounds;
      var labelBounds = chartLayout.getBoundingBox('hAxis#0#label#' + r);
      for (var c = 1; c < data.getNumberOfColumns(); c++) {
        barBounds = chartLayout.getBoundingBox('bar#' + (c - 1) + '#' + r);
        if (barBounds !== null) {
          icon = container.appendChild(document.createElement('i'));
          icon.className = 'fa ' + data.getColumnProperty(c, 'icon');
          icon.style.position = 'absolute';
          iconBounds = icon.getBoundingClientRect();
          icon.style.top = (containerBounds.top + labelBounds.top) + 'px';
          icon.style.left = (barBounds.left + containerBounds.left + (barBounds.width / 2) - (iconBounds.width / 2)) + 'px';
        }
      }

      // move axis label down
      labelIndex = -1;
      Array.prototype.forEach.call(axisLabels, function(label) {
        if (label.getAttribute('text-anchor') === 'middle') {
          labelIndex++;
          if (labelIndex === r) {
            label.setAttribute('y', (parseFloat(label.getAttribute('y')) + (iconBounds.height * 2)));
          }
        }
      });
    }
  });

  chart.draw(data, options);
});
i {
  color: #00bcd4;
}
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
WhiteHat
  • 59,912
  • 7
  • 51
  • 133
  • Wow, this is awesome! I don't have much experience with google charts but this is almost perfect. One more thing would be needed for the implementation. Scrolling the chart as it can hold up to 40 groups, each with 2, 3, 4 columns. I don't want to be a pain in the ass but could you assist me or point me in the right direction to also make the scrolling work? I spent almost all last week trying to make this with work with fusioncharts (which is great, but lacks deep customization) and also chart.js. Anyway i will accept your answer as it gives me hope at least :D Thanks a lot! – Radu Turtoi Sep 10 '18 at 06:38
  • i saw your answer on this question, i will try implementing this https://stackoverflow.com/a/43788639/2799174 also in this scenario can i stack other columns above, as i need to represent also inactive data (which in my example are the columns with white background and dotted borders)? – Radu Turtoi Sep 10 '18 at 06:46
  • stacking multiple columns will be difficult, which is why I didn't add here -- it's easy with a _material_ chart, but _material_ doesn't support `getChartLayoutInterface`, which is what you need for icon position, it's possible with _classic_ chart, but requires significant data manipulation, here is an example of both --> [create a stacked bar chart for three sets of data](https://stackoverflow.com/a/40050099/5090771) – WhiteHat Sep 10 '18 at 11:37
  • i see, so i'll have to manipulate the data in a way that i add spacer columns. Anyway, you were very helpful and i really appreciate it! – Radu Turtoi Sep 11 '18 at 06:57