1
votes

I'm trying to create a dashboard with the Google Visualization tools and I am having a couple of problems.

I'm bring in data that is all text and calculating the chart data on the fly by setting the calc property in the ChartWrapper and when the datatable is ready I call a function to set the view of the chart. The full code is below.

The problems I'm having are:

  1. Getting invalid row index errors when I filter the table with a control. These go away once the view is set and the chart is redrawn but the "One or more participants failed to draw()" error remains.
  2. The chart is displayed with the raw data before the view is set when first loaded.
  3. I return the text data with numbers (i.e. "1. Male") so the columns sort correctly but I do not want to see the number in the chart. Any idea on how to clean up the labels?

Any help would be greatly appreciated. If you have any suggestions on a better way to handle this kind of data I'm all ears.

I've created a fiddle: https://jsfiddle.net/kq8fcdee/1/

Full code:

<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

<style>
#table_div{display:none;}
.chart_div{display:inline-block;border:1px solid #888;margin-right:10px;}
</style>

</head>
<body>

<!--Div that will hold the dashboard-->
<div id="dashboard_div">
  <!--Divs that will hold each control and chart-->
  <div id="filter_div"></div>
  <div id="table_div"></div>
</div>


<script>

var data=
[
["Gender","Last time","Origin\/transfer","How arrived","Reason for flight","Section","Shop concessions","Purchase merchandise","Purchase services","Connect to WIFI","Overall experience","Country","Age group","Zip code","Education","Employment Status","Employment Status Group","Household income","Hispanic","Race","2. Children"],
["1. Male","3. 1-2 years","1. Starting","8. Other","3. Other","1. First\/Business","1. Yes","2. No","1. Yes","2. No","4. Very good","2. Other","5. 45-54",75201,"5. College degree","4. Student","2. Not Employed","4. 100k - 150k","1. Yes","4. Native American \/ American Indian","1. No children"],
["2. Female","2. 6-12 months","2. Connection","1. Drove self","1. Business","1. First\/Business","2. No","1. Yes","1. Yes","1. Yes","4. Very good","1. USA","3. 26-34",32003,"4. Some college","2. Part-time","1. Employed","2. 40k - 75k","1. Yes","2. Black \/ African American","1. No children"],
["1. Male","2. 6-12 months","2. Connection","5.R ental","2. Leisure","2. Economy\/Coach","1. Yes","2. No","1. Yes","2. No","4. Very good","1. USA","5. 45-54",64101,"4. Some college","5. Homemaker","2. Not Employed","5. 150k+","1. Yes","5. Other","1. No children"],
["2. Female","2. 6-12 months","2. Connection","1. Drove self","2. Leisure","1. First\/Business","2. No","2. No","1. Yes","2. No","4. Very good","1. USA","5. 45-54",75201,"2. Some HS","4. Student","2. Not Employed","4. 100k - 150k","2. No","4. Native American \/ American Indian","Children"],
["2. Female","2. 6-12 months","2. Connection","5.R ental","2. Leisure","2. Economy\/Coach","1. Yes","2. No","1. Yes","2. No","4. Very good","1. USA","2. 22-25",84101,"4. Some college","4. Student","2. Not Employed","4. 100k - 150k","1. Yes","4. Native American \/ American Indian","1. No children"],
["2. Female","5. 3+ years","1. Starting","4. Bus\/shuttle","2. Leisure","1. First\/Business","2. No","2. No","2. No","1. Yes","4. Very good","2. Other","3. 26-34",32003,"5. College degree","6. Unemployed not looking","2. Not Employed","6. Refused","2. No",null,"1. No children"],
["2. Female","4. 2-3 years","1. Starting","8. Other","3. Other","2. Economy\/Coach","2. No","2. No","2. No","2. No","1. Poor","2. Other","6. 55-64",99501,"4. Some college","3. Retired","2. Not Employed","3. 75k - 100k","2. No","3. Asian","1. No children"],
["2. Female","5. 3+ years","1. Starting","5.R ental","3. Other","1. First\/Business","2. No","1. Yes","2. No","1. Yes","1. Poor","1. USA","1. 16-21",15668,"4. Some college","1. Full-time","1. Employed","1. < 40k","2. No","1. White \/ Caucasian","1. No children"]
];


var proc = function(){
    //get the data from the server (php that gets data from mysql and reruns json array
    //$.getJSON('getdata.php',function(data) {

        //load the library
        google.charts.load('current', {'packages':['corechart', 'controls']});

        //function that is called when libary loaded
        google.charts.setOnLoadCallback(function() {

            nCols=data[0].length;
            charts=[];
            data = google.visualization.arrayToDataTable(data);
            dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div'));

            //create Gender filter
            var genderSelection = new google.visualization.ControlWrapper({
                'controlType': 'CategoryFilter',
                'containerId': 'filter_div',
                'options': {
                    'filterColumnLabel': 'Gender',
                }
            });

            //create the table
            var table = new google.visualization.ChartWrapper({
                'chartType': 'Table',
                'dataTable':data,
                'containerId': 'table_div',
                'options': {'width': '800px'}
            });

            //create the charts (one chart for each column in the table)
            for(qidx=0;qidx < nCols;qidx++) {

                var createChart = function(qidx) {

                    //add place for chart
                    $('#dashboard_div').append('<div class="chart_div" id="chart'+qidx+'_div"></div>');

                    //create chart
                    charts[qidx] = new google.visualization.ChartWrapper({
                        'chartType': 'ColumnChart',
                        'containerId': 'chart'+qidx+'_div',
                        'options': {
                            'title': data.getColumnLabel(qidx),
                            'xwidth': 400,
                            'xheight': 300,
                            'vAxis': {
                                'minValue': 0,
                                'maxValue': 100
                            },
                            'chartArea': {
                                'width': '80%',
                                'height': '60%',
                                'top': 30,
                             },
                            'legend':'bottom'
                        },
                        'view': {
                            'columns': [qidx, {calc:function(dataTable, rowNum) {
                                var curr_stat = dataTable.getValue(rowNum,qidx);
                                var distinct_values = dataTable.getDistinctValues(qidx);
                                var count = 0;
                                var numRows=dataTable.getNumberOfRows();

                                for (var ii=0; ii<numRows; ii++) {
                                    stat = dataTable.getValue(ii,qidx);
                                    if (stat == curr_stat) {
                                        count++;
                                    }
                                }
                                return count/numRows*100; //return percentage
                            }, type:'number', label: '1Q'}]
                        }
                    });

                }(qidx);
            }

            //set the rows of the chart
            function setChartRows () {

                //loop though the charts
                for(qidx=0;qidx < nCols;qidx++) {

                    var dt = charts[qidx].getDataTable();
                    var rows = dt.getNumberOfRows();
                    var distinct_values = dt.getDistinctValues(qidx);
                    var arRows = [];

                    for (var ii=0; ii<distinct_values.length; ii++) {
                        for (var jj=0; jj<rows; jj++) {
                            var curr_stat = dt.getValue(jj,qidx);
                            if (curr_stat == distinct_values[ii]) {
                                arRows.push(jj);
                                break;
                            }
                        }
                    }

                    var view = charts[qidx].getView() || {};
                    view.rows = arRows;
                    charts[qidx].setView(view);
                    charts[qidx].draw();

                }
            }

            //when the table is created/changed update the chart
            google.visualization.events.addListener(table, 'ready', setChartRows);

            charts.push(table);
            dashboard.bind(genderSelection, charts);
            dashboard.draw(data);

        });
    //});

};

//run
proc();

//refresh every 5 minutes
var run = setInterval(proc,300000)

</script>

</body>
</html>
1

1 Answers

0
votes

1 & 2) these issues can be corrected by disconnecting the charts from the dashboard

the charts can be drawn independently when the 'ready' event fires on the table,
using the filtered data from the table chart

this will prevent the errors and the initial raw data from being drawn when first loaded


3) to custom sort the labels, use object notation in the data array {}

each cell in the data table must have a value (v:),
and optionally, may have a formatted value (f:)

in the data array, instead of using --> "1. Male"

use object notation instead --> {v: "1", f: "Male"}

note: charts display the formatted value by default

however, two options must be set on the CategoryFilter

set option --> useFormattedValue: true -- to display formatted values in the control

set option --> ui.sortValues: false -- to display the categories as found in the data


see following working snippet

the first two columns in the data have been updated to use object notation as mentioned above...

//load the library
google.charts.load('current', {
  callback: function () {
    var data = [
      ["Gender","Last time","Origin\/transfer","How arrived","Reason for flight","Section","Shop concessions","Purchase merchandise","Purchase services","Connect to WIFI","Overall experience","Country","Age group","Zip code","Education","Employment Status","Employment Status Group","Household income","Hispanic","Race","2. Children"],
      [{v: "1", f: "Male"},{v: "3", f: "1-2 years"},"1. Starting","8. Other","3. Other","1. First\/Business","1. Yes","2. No","1. Yes","2. No","4. Very good","2. Other","5. 45-54",75201,"5. College degree","4. Student","2. Not Employed","4. 100k - 150k","1. Yes","4. Native American \/ American Indian","1. No children"],
      [{v: "2", f: "Female"},{v: "2", f: "6-12 months"},"2. Connection","1. Drove self","1. Business","1. First\/Business","2. No","1. Yes","1. Yes","1. Yes","4. Very good","1. USA","3. 26-34",32003,"4. Some college","2. Part-time","1. Employed","2. 40k - 75k","1. Yes","2. Black \/ African American","1. No children"],
      [{v: "1", f: "Male"},{v: "2", f: "6-12 months"},"2. Connection","5.R ental","2. Leisure","2. Economy\/Coach","1. Yes","2. No","1. Yes","2. No","4. Very good","1. USA","5. 45-54",64101,"4. Some college","5. Homemaker","2. Not Employed","5. 150k+","1. Yes","5. Other","1. No children"],
      [{v: "2", f: "Female"},{v: "2", f: "6-12 months"},"2. Connection","1. Drove self","2. Leisure","1. First\/Business","2. No","2. No","1. Yes","2. No","4. Very good","1. USA","5. 45-54",75201,"2. Some HS","4. Student","2. Not Employed","4. 100k - 150k","2. No","4. Native American \/ American Indian","Children"],
      [{v: "2", f: "Female"},{v: "2", f: "6-12 months"},"2. Connection","5.R ental","2. Leisure","2. Economy\/Coach","1. Yes","2. No","1. Yes","2. No","4. Very good","1. USA","2. 22-25",84101,"4. Some college","4. Student","2. Not Employed","4. 100k - 150k","1. Yes","4. Native American \/ American Indian","1. No children"],
      [{v: "2", f: "Female"},{v: "5", f: "3+ years"},"1. Starting","4. Bus\/shuttle","2. Leisure","1. First\/Business","2. No","2. No","2. No","1. Yes","4. Very good","2. Other","3. 26-34",32003,"5. College degree","6. Unemployed not looking","2. Not Employed","6. Refused","2. No",null,"1. No children"],
      [{v: "2", f: "Female"},{v: "4", f: "2-3 years"},"1. Starting","8. Other","3. Other","2. Economy\/Coach","2. No","2. No","2. No","2. No","1. Poor","2. Other","6. 55-64",99501,"4. Some college","3. Retired","2. Not Employed","3. 75k - 100k","2. No","3. Asian","1. No children"],
      [{v: "2", f: "Female"},{v: "5", f: "3+ years"},"1. Starting","5.R ental","3. Other","1. First\/Business","2. No","1. Yes","2. No","1. Yes","1. Poor","1. USA","1. 16-21",15668,"4. Some college","1. Full-time","1. Employed","1. < 40k","2. No","1. White \/ Caucasian","1. No children"]
    ];

    nCols=data[0].length;
    charts=[];
    data = google.visualization.arrayToDataTable(data);
    dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div'));

    //create Gender filter
    var genderSelection = new google.visualization.ControlWrapper({
      'controlType': 'CategoryFilter',
      'containerId': 'filter_div',
      'options': {
        'filterColumnLabel': 'Gender',
        'useFormattedValue': true,
        'ui': {
          'sortValues': false
        }
      }
    });

    //create the table
    var table = new google.visualization.ChartWrapper({
      'chartType': 'Table',
      'dataTable': data,
      'containerId': 'table_div',
      'options': {'width': '800px'}
    });

    //create the charts
    for(qidx=0;qidx < nCols;qidx++) {
      var createChart = function(qidx) {
        //add place for chart
        $('#dashboard_div').append('<div class="chart_div" id="chart'+qidx+'_div"></div>');

        //create chart
        charts[qidx] = new google.visualization.ChartWrapper({
          'chartType': 'ColumnChart',
          'containerId': 'chart'+qidx+'_div',
          'options': {
            'title': data.getColumnLabel(qidx),
            'xwidth': 400,
            'xheight': 300,
            'vAxis': {
              'minValue': 0,
              'maxValue': 100
            },
            'chartArea': {
              'width': '80%',
              'height': '60%',
              'top': 30,
             },
            'legend':'bottom'
          },
          'view': {
            'columns': [qidx, {calc:function(dataTable, rowNum) {
              var curr_stat = dataTable.getValue(rowNum,qidx);
              var distinct_values = dataTable.getDistinctValues(qidx);
              var count = 0;
              var numRows=dataTable.getNumberOfRows();

              for (var ii=0; ii<numRows; ii++) {
                stat = dataTable.getValue(ii,qidx);
                if (stat == curr_stat) {
                  count++;
                }
              }
              return count/numRows*100; //return percentage
            }, type:'number', label: '1Q'}]
          }
        });
      }(qidx);
    }

    //set the rows of the chart
    function setChartRows (dt) {
      //loop though the charts
      for(qidx=0;qidx < nCols;qidx++) {
        var rows = dt.getNumberOfRows();
        var distinct_values = dt.getDistinctValues(qidx);
        var arRows = [];

        for (var ii=0; ii<distinct_values.length; ii++) {
          for (var jj=0; jj<rows; jj++) {
            var curr_stat = dt.getValue(jj,qidx);
            if (curr_stat == distinct_values[ii]) {
              arRows.push(jj);
              break;
            }
          }
        }

        var view = charts[qidx].getView() || {};
        view.rows = arRows;
        charts[qidx].setDataTable(dt);
        charts[qidx].setView(view);
        charts[qidx].draw();
      }
    }

    //when the table is created/changed update the chart
    google.visualization.events.addListener(table, 'ready', function () {
      setChartRows(table.getDataTable());
    });

    dashboard.bind(genderSelection, table);
    dashboard.draw(data);
  },
  packages: ['corechart', 'controls']
});
#table_div{display:none;}
.chart_div{display:inline-block;border:1px solid #888;margin-right:5px;}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div id="dashboard_div">
  <div id="filter_div"></div>
  <div id="table_div"></div>
</div>