CryoFusion87
CryoFusion87

Reputation: 796

Google Visualization API - formatting dual axis combo chart that can be filtered

I have created a dual axis combo chart using the Google Visualization API that displays data as column and line charts.

the page also has a set of filters for the columns which returns a new data set from the database depending on the column filters selected.

In order to assign which values are bars and lines I have used the series option which is shown in the jsfiddle and code below:

google.charts.load('current',
        {
            callback: drawChart,
            packages: ['corechart', 'controls', 'charteditor']
        });

    var chart;

    function drawChart() {

        var data = new google.visualization.arrayToDataTable([['Outcomes','Abstinent','Improved','Unchanged','Deteriorated','Average no. of days use at start','Average no. of days of use at review'],['2015/16',18036,11414,14430,1880,21.6,8.3],['2016/17',17642,11688,14010,1738,22.2,8.5],['2017/18',17282,10796,13542,1686,22.1,8.6]]);
        var width = Math.min(document.documentElement.clientWidth, window.innerWidth || 0) + 'px';

        var columnsTable = new google.visualization.DataTable();
        columnsTable.addColumn('number', 'colIndex');
        columnsTable.addColumn('string', 'colLabel');
        var initState = { selectedValues: [] };
        // put the columns into this data table (skip column 0)
        for (var i = 1; i < data.getNumberOfColumns(); i++) {
            columnsTable.addRow([i, data.getColumnLabel(i)]);
            // you can comment out this next line if you want to have a default selection other than the whole list
            initState.selectedValues.push(data.getColumnLabel(i));
        }
        // you can set individual columns to be the default columns (instead of populating via the loop above) like this:
        // initState.selectedValues.push(data.getColumnLabel(4));

        var options = {
            title: 'Treatment outcomes',
            hAxis: {
                slantedText: true,
                slantedTextAngle: 30,
                title: 'Reporting Period'
            },
            vAxes: {
                0: { logScale: false, title: 'No. of clients' },
                1: { logScale: false, title: 'Average no. of days use', maxValue: 28 }
            },
            series: {
                0: { type: "bars", targetAxisIndex: 0, color: '#FF9900' },
                1: { type: "bars", targetAxisIndex: 0, color: '#FF6400' },
                2: { type: "bars", targetAxisIndex: 0, color: '#FF0000' },
                3: { type: "bars", targetAxisIndex: 0, color: '#9A0033' },
                4: { type: "line", targetAxisIndex: 1 },
                5: { type: "line", targetAxisIndex: 1 }
            },
            chartArea: {
                top: 40,
                left: 100,
                width: "65%"
            },
            pointSize: 5,
            legend: { position: 'top' },
            width: width,
            height: '300px'
        }

        var chart = new google.visualization.LineChart(document.getElementById("chart_div"));

        chart.draw(data, options);
    }

https://jsfiddle.net/uLwtbvn5/5/

I am looking to have it so that if a value or values are removed any value with the column name Abstinent, Improved, Unchanged or Deteriorated is always shown as a bar and uses targetAxisIndex: 0 and the values with Average no. of days use at start or Average no. of days of use at review are always lines and use targetAxisIndex: 1.

Is there are way of doing this with the Google Visualization API?

Upvotes: 1

Views: 319

Answers (1)

WhiteHat
WhiteHat

Reputation: 61222

on the columnsTable, use row properties to store the series options.

then when the filter changes, pull the row properties from the columns table to draw the chart.

see following working snippet...

google.charts.load('current', {
  packages: ['corechart', 'controls', 'charteditor']
}).then(function () {
  var data = google.visualization.arrayToDataTable([['Outcomes','Abstinent','Improved','Unchanged','Deteriorated','Average no. of days use at start','Average no. of days of use at review'],['2015/16',18036,11414,14430,1880,21.6,8.3],['2016/17',17642,11688,14010,1738,22.2,8.5],['2017/18',17282,10796,13542,1686,22.1,8.6]]);
  var width = Math.min(document.documentElement.clientWidth, window.innerWidth || 0) + 'px';

  // define bars series colors
  var colors = ['#ff9900', '#ff6400', '#ff0000', '#9a0033'];

  // define filter columns
  var columnsTable = new google.visualization.DataTable();
  columnsTable.addColumn('number', 'Series #');
  columnsTable.addColumn('string', 'Select series: ');
  var initState = { selectedValues: [] };
  for (var i = 1; i < data.getNumberOfColumns(); i++) {
    var colIndex = columnsTable.addRow([i, data.getColumnLabel(i)]);
    initState.selectedValues.push(data.getColumnLabel(i));

    // assign series type, axis, and color -- store using row properties
    switch (data.getColumnLabel(i)) {
      case 'Abstinent':
      case 'Improved':
      case 'Deteriorated':
      case 'Unchanged':
        columnsTable.setRowProperty(colIndex, 'type', 'bars');
        columnsTable.setRowProperty(colIndex, 'targetAxisIndex', 0);
        columnsTable.setRowProperty(colIndex, 'color', colors[colIndex]);
        break;

      default:
        columnsTable.setRowProperty(colIndex, 'type', 'line');
        columnsTable.setRowProperty(colIndex, 'targetAxisIndex', 1);
    }
  }

  // define chart
  var chart = new google.visualization.LineChart(document.getElementById('chart_div'));

  // define filter
  var filter = new google.visualization.ControlWrapper({
    controlType: 'CategoryFilter',
    containerId: 'filter_div',
    dataTable: columnsTable,
    options: {
      filterColumnIndex: 1,
      ui: {
        caption: 'Series',
        sortValues: false
      }
    },
    state: initState
  });

  // draw chart when filter is ready or changed
  google.visualization.events.addListener(filter, 'ready', drawChart);
  google.visualization.events.addListener(filter, 'statechange', drawChart);

  // draw filter
  filter.draw();

  // draw chart
  function drawChart() {
    var options = {
      title: 'Treatment outcomes',
      hAxis: {
        slantedText: true,
        slantedTextAngle: 30,
        title: 'Reporting Period'
      },
      vAxes: {
        0: { logScale: false, title: 'No. of clients' },
        1: { logScale: false, title: 'Average no. of days use', maxValue: 28 }
      },
      series: {},
      chartArea: {
        top: 40,
        left: 100,
        width: '65%'
      },
      pointSize: 5,
      legend: { position: 'top' },
      width: width,
      height: '300px'
    };

    // define data view column and series options based on filter columns
    var viewColumns = [0];
    var state = filter.getState().selectedValues;
    state.forEach(function (column, index) {
      var colIndex;

      // find filter column index
      for (var i = 0; i < columnsTable.getNumberOfRows(); i++) {
        if (columnsTable.getValue(i, 1) === column) {
          colIndex = i;
        }
      }
      viewColumns.push(colIndex + 1);

      // define series option
      options.series[index] = {
        type: columnsTable.getRowProperty(colIndex, 'type'),
        targetAxisIndex: columnsTable.getRowProperty(colIndex, 'targetAxisIndex')
      };
      if (colIndex < 4) {
        options.series[index].color = columnsTable.getRowProperty(colIndex, 'color');
      }
    });

    // define data view
    var dataView = new google.visualization.DataView(data);
    dataView.setColumns(viewColumns);

    // ensure enough columns selected to draw chart
    if (viewColumns.length > 1) {
      chart.draw(dataView, options);
    } else {
      chart.clearChart();
    }
  }
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="filter_div"></div>
<div id="chart_div"></div>

Upvotes: 1

Related Questions