Show/hide/toggle Data Series on Google Chart by clicking Legend

Viewed 295

I have a Google line chart which displays 11 different series of data. I want the user to be able to show/hide a series by clicking on the relevant series in the legend. I found this similar question and solution elsewhere on Stackoverflow. However nothing happens when I click on a series in the legend after incorporating what I believe is the applicable code from the above solution. I've commented where the modified code from the solution starts at the end of my code below.

Would really appreciate some help please.

<html>
  <head>
    <script src="https://www.gstatic.com/charts/loader.js"></script>
    <script type="text/javascript">
  
  google.charts.load('current', {packages: ['controls']}).then(initialize);
  
  function initialize() {
    var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1001YnF8LCwKyzpS4pgmZnwFVigL-LlnqwWn6xjnso2k/edit#gid=0&range=A:BT');
        query.send(drawDashboard);
  }
  
  function drawDashboard(response) {
  
    var data = response.getDataTable();
    //Asign units of 'mm' to data.
    var formatMS = new google.visualization.NumberFormat({pattern: '# mm'});
  
    // format data into mm.
    for (var colIndex = 1; colIndex < data.getNumberOfColumns(); colIndex++) {formatMS.format(data, colIndex);}
    var dataView = new google.visualization.DataView(data);
    dataView.setColumns([0,1,2,{sourceColumn: 3, role: 'interval'}, {sourceColumn: 4, role: 'interval'}, 5,{sourceColumn: 6, 'type': 'string', role: 'tooltip', 'p': {'html': true}},7,8,9,10,11,12,13,14,15,{sourceColumn: 16, role: 'interval'}, {sourceColumn: 17, role: 'interval'}, 18,{sourceColumn: 19, 'type': 'string', role: 'tooltip', 'p': {'html': true}},20,21,22,23,24,25,26,27,28,{sourceColumn: 29, role: 'interval'}, {sourceColumn: 30, role: 'interval'},31,{sourceColumn: 32, 'type': 'string', role: 'tooltip', 'p': {'html': true}},33,34,35,36,37,38,39,40,41,{sourceColumn: 42, role: 'interval'}, {sourceColumn: 43, role: 'interval'}, 44,{sourceColumn: 45, 'type': 'string', role: 'tooltip', 'p': {'html': true}},46,47,48,49,50,51,52,53,54,{sourceColumn: 55, role: 'interval'}, {sourceColumn: 56, role: 'interval'}, 57,{sourceColumn: 58, 'type': 'string', role: 'tooltip', 'p': {'html': true}},59,60,61,62,63,64,65,66,67,68,69,70,71]);
  
    var YearPicker = new google.visualization.ControlWrapper({
      controlType: 'NumberRangeFilter',
      containerId: 'filter_div',
      options: {
        maxValue: 2150,
        minValue: 1891,
        filterColumnLabel: 'Year',
        ui: {
          cssClass: 'filter-date',
          format: {pattern: '0000'},
          labelStacking: 'vertical',
          allowTyping: true,
          allowMultiple: false
        }
      },
      state: {lowValue: 1891, highValue: 2150},
    });
  
    google.visualization.events.addListener(YearPicker, 'statechange', function () {
      var state = YearPicker.getState();
      state.lowValue = Math.min(2021, state.lowValue);
      YearPicker.setState({
        lowValue: state.lowValue,
        highValue: state.highValue
      });
      YearPicker.draw();
    });
  
    var MSLChart = new google.visualization.ChartWrapper({
      chartType: 'LineChart',
      containerId: 'chart_div',
      dataTable: dataView,
      options: {
        fontSize: '20',
        title: 'NZ Annual Relative Mean Sea Level Summary',
        hAxis: {title: 'Year', format: '0000'},
        vAxis: {title: 'Height above 1986-2005 IPCC Baseline (mm)', format:'###0'},
        height: 600,
        chartArea: {height: '81%', width: '85%', left: 100},
        legend: {position: 'top', maxLines: 5, alignment: 'start', textStyle: {fontSize: 9}},
        colors: ['blue'],
        tooltip: {
          isHtml: true
        },
        trendlines: {
          9: {
            type: 'linear',
            color: 'black',
            visibleInLegend: true,
            showR2: true,
          },
          10: {
            type: 'polynomial',
            color: 'purple',
            degree: 2,
            visibleInLegend: true,
            showR2: true,
          },
        },
        series: {
          0: {color:'navy', visibleInLegend: true}, //Auckland
          1: {color:'green', visibleInLegend: true }, //Taranaki
          2: {color:'gold', visibleInLegend: true}, //Wellington
          3: {color:'red', visibleInLegend: true}, //Lyttelton
          4: {color:'DeepSkyBlue', visibleInLegend: true}, //Dunedin
          5: {color:'#47d045', visibleInLegend: true}, //NZ RCP2.6 M
          6: {color:'#0e038d', visibleInLegend: true}, //NZ RCP4.5 M
          7: {color:'#b8bb62', visibleInLegend: true}, //NZ RCP8.5 M
          8: {color:'#b22123', visibleInLegend: true}, //NZ RCP8.5 H
          9: {color:'blue', visibleInLegend: true}, //NZ Mean
          10: {color:'blue', visibleInLegend: false}, //NZ Mean
        },
        intervals: { 'style':'bars','color': '#6292dc' },
        interpolateNulls: 'True',  
      },
      view: {columns: [0,7,20,33,46,59,66,67,68,69,70,71]}
    });
  
var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div')).bind(YearPicker, MSLChart).draw(dataView);

 //Code taken from above solution begins here: 
  var columns = [];
  var series = {};
  for (var i = 0; i < data.getNumberOfColumns(); i++) {
    columns.push(i);
    if (i > 0) {
      series[i - 1] = {};
    }
  }
  google.visualization.events.addListener(MSLChart, 'select', function () {

    var sel = MSLChart.getChart().getSelection();
    // if selection length is 0, we deselected an element
    if (sel.length > 0) {
       // if row is undefined, we clicked on the legend
       if (sel[0].row === null) {
           var col = sel[0].column;
           if (columns[col] == col) {
              // hide the data series
              columns[col] = {
                 label: data.getColumnLabel(col),
                 type: data.getColumnType(col),
                 calc: function () {
                     return null;
                 }
              };
              // grey out the legend entry
              series[col - 1].color = '#CCCCCC';
          } else {
              // show the data series
              columns[col] = col;
              series[col - 1].color = null;
          }
          var view = new google.visualization.DataView(data);
          view.setColumns(columns);
          MSLChart.draw();
       }
  }
});
  }
  </script>
  </head>
  <body>
      <div id="dashboard_div" >
         <div id="filter_div"></div>
         <div id="chart_div"></div>
       </div>
  </body>
</html>
1 Answers

first, need to assign chart events before chart is drawn,
just need to move select event above dashboard.draw

next, since you are setting specific view columns on the chart wrapper,
we need to take those into account when making changes to the series.
we'll save a reference to the view columns...

var viewColumns = [0,7,20,33,46,59,66,67,68,69,70,71];

then use that reference when hiding / showing the series...

var col = viewColumns[sel[0].column];

next, when the new columns are set and the chart is to be re-drawn,
we have to update the chart's datatable with the new view...

var view = new google.visualization.DataView(dataView);
view.setColumns(columns);
MSLChart.setDataTable(view);  // <-- here

something that didn't work in the referenced answer,
was making the chart legend for the series grayed out.
once we make changes to the series colors,
we have to reset the series option...

MSLChart.setOption('series', series);

finally, I added one other routine, to prevent the y-axis from changing,
when a series is removed and the chart is re-drawn.
we can use chart method getVAxisValue to determine the min and max values of the y-axis, from the initial draw.

var chartArea = MSLChart.getChart().getChartLayoutInterface().getChartAreaBoundingBox();
var vAxisTop = MSLChart.getChart().getChartLayoutInterface().getVAxisValue(chartArea.top);
var vAxisBtm = MSLChart.getChart().getChartLayoutInterface().getVAxisValue(chartArea.top + chartArea.height);

MSLChart.setOption('vAxis.viewWindow.max', vAxisTop);
MSLChart.setOption('vAxis.viewWindow.min', vAxisBtm);

following is the updated code...

google.charts.load('current', {packages: ['controls']}).then(initialize);

function initialize() {
  var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1001YnF8LCwKyzpS4pgmZnwFVigL-LlnqwWn6xjnso2k/edit#gid=0&range=A:BT');
      query.send(drawDashboard);
}

function drawDashboard(response) {

    var data = response.getDataTable();
    //Asign units of 'mm' to data.
    var formatMS = new google.visualization.NumberFormat({pattern: '# mm'});

    // format data into mm.
    for (var colIndex = 1; colIndex < data.getNumberOfColumns(); colIndex++) {formatMS.format(data, colIndex);}
    var dataView = new google.visualization.DataView(data);
    dataView.setColumns([0,1,2,{sourceColumn: 3, role: 'interval'}, {sourceColumn: 4, role: 'interval'}, 5,{sourceColumn: 6, 'type': 'string', role: 'tooltip', 'p': {'html': true}},7,8,9,10,11,12,13,14,15,{sourceColumn: 16, role: 'interval'}, {sourceColumn: 17, role: 'interval'}, 18,{sourceColumn: 19, 'type': 'string', role: 'tooltip', 'p': {'html': true}},20,21,22,23,24,25,26,27,28,{sourceColumn: 29, role: 'interval'}, {sourceColumn: 30, role: 'interval'},31,{sourceColumn: 32, 'type': 'string', role: 'tooltip', 'p': {'html': true}},33,34,35,36,37,38,39,40,41,{sourceColumn: 42, role: 'interval'}, {sourceColumn: 43, role: 'interval'}, 44,{sourceColumn: 45, 'type': 'string', role: 'tooltip', 'p': {'html': true}},46,47,48,49,50,51,52,53,54,{sourceColumn: 55, role: 'interval'}, {sourceColumn: 56, role: 'interval'}, 57,{sourceColumn: 58, 'type': 'string', role: 'tooltip', 'p': {'html': true}},59,60,61,62,63,64,65,66,67,68,69,70,71]);
    var viewColumns = [0,7,20,33,46,59,66,67,68,69,70,71];

    var YearPicker = new google.visualization.ControlWrapper({
      controlType: 'NumberRangeFilter',
      containerId: 'filter_div',
      options: {
        maxValue: 2150,
        minValue: 1891,
        filterColumnLabel: 'Year',
        ui: {
          cssClass: 'filter-date',
          format: {pattern: '0000'},
          labelStacking: 'vertical',
          allowTyping: true,
          allowMultiple: false
        }
      },
      state: {lowValue: 1891, highValue: 2150},
    });

    google.visualization.events.addListener(YearPicker, 'statechange', function () {
      var state = YearPicker.getState();
      state.lowValue = Math.min(2021, state.lowValue);
      YearPicker.setState({
        lowValue: state.lowValue,
        highValue: state.highValue
      });
      YearPicker.draw();
    });

    var series = {
      0: {color:'navy', visibleInLegend: true}, //Auckland
      1: {color:'green', visibleInLegend: true }, //Taranaki
      2: {color:'gold', visibleInLegend: true}, //Wellington
      3: {color:'red', visibleInLegend: true}, //Lyttelton
      4: {color:'DeepSkyBlue', visibleInLegend: true}, //Dunedin
      5: {color:'#47d045', visibleInLegend: true}, //NZ RCP2.6 M
      6: {color:'#0e038d', visibleInLegend: true}, //NZ RCP4.5 M
      7: {color:'#b8bb62', visibleInLegend: true}, //NZ RCP8.5 M
      8: {color:'#b22123', visibleInLegend: true}, //NZ RCP8.5 H
      9: {color:'blue', visibleInLegend: true}, //NZ Mean
      10: {color:'blue', visibleInLegend: false}, //NZ Mean
    };

    // save series colors
    var seriesColors = {};
    for (var column in series) {
      if (series.hasOwnProperty(column)) {
        seriesColors[column] = series[column].color;
      }
    }


    var MSLChart = new google.visualization.ChartWrapper({
      chartType: 'LineChart',
      containerId: 'chart_div',
      dataTable: dataView,
      options: {
        fontSize: '20',
        title: 'NZ Annual Relative Mean Sea Level Summary',
        hAxis: {title: 'Year', format: '0000'},
        vAxis: {title: 'Height above 1986-2005 IPCC Baseline (mm)', format:'###0'},
        height: 600,
        chartArea: {height: '81%', width: '85%', left: 100},
        legend: {position: 'top', maxLines: 5, alignment: 'start', textStyle: {fontSize: 9}},
        colors: ['blue'],
        tooltip: {
          isHtml: true
        },
        trendlines: {
          9: {
            type: 'linear',
            color: 'black',
            visibleInLegend: true,
            showR2: true,
          },
          10: {
            type: 'polynomial',
            color: 'purple',
            degree: 2,
            visibleInLegend: true,
            showR2: true,
          },
        },
        series: series,
        intervals: { 'style':'bars','color': '#6292dc' },
        interpolateNulls: 'True',
      },
      view: {columns: viewColumns}
    });

    //Code taken from above solution begins here:
    var columns = [];
    for (var i = 0; i < dataView.getNumberOfColumns(); i++) {
      columns.push(i);
    }
    google.visualization.events.addListener(MSLChart, 'select', function () {
      var chartArea = MSLChart.getChart().getChartLayoutInterface().getChartAreaBoundingBox();
      var vAxisTop = MSLChart.getChart().getChartLayoutInterface().getVAxisValue(chartArea.top);
      var vAxisBtm = MSLChart.getChart().getChartLayoutInterface().getVAxisValue(chartArea.top + chartArea.height);

      var sel = MSLChart.getChart().getSelection();

      // if selection length is 0, we deselected an element
      if (sel.length > 0) {
         // if row is undefined, we clicked on the legend
         if ((sel[0].row === null) && (sel[0].column > 0)) {
             // get actual column number from view
             var col = viewColumns[sel[0].column];
             if (columns[col] == col) {
                // hide the data series
                columns[col] = {
                   label: dataView.getColumnLabel(col),
                   type: dataView.getColumnType(col),
                   calc: function () {
                      return null;
                   }
                };

                // grey out the legend entry
                series[sel[0].column - 1].color = '#CCCCCC';
            } else {
                // show the data series
                columns[col] = col;
                series[sel[0].column - 1].color = seriesColors[sel[0].column - 1];
            }

            // set new view columns
            var view = new google.visualization.DataView(dataView);
            view.setColumns(columns);
            MSLChart.setDataTable(view);

            // prevent y-axis from changing when series is removed
            MSLChart.setOption('series', series);
            MSLChart.setOption('vAxis.viewWindow.max', vAxisTop);
            MSLChart.setOption('vAxis.viewWindow.min', vAxisBtm);

            // re-draw chart
            MSLChart.draw();
         }
      }
    });

    var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div')).bind(YearPicker, MSLChart).draw(dataView);
}
Related