Search code examples
javascripthtmlchartsgoogle-visualizationreact-google-charts

Google Charts Rendering Empty Chart addRows() Errror


I have a JavaScript array which looks like this:

[
{x: "Station a", y: -77.33333333333333},
{x: "Station b", y: -19},
{x: "Station c", y: 9.492537313432836},
...
]

I wish to create a bar chart using Google Charts. The following code gives me an empty chart... there are no bars. The x-value is supposed to be the label and the y-value creates the bar in the chart. Do I have to manually push the array values into the data table? If so, how would this be done? This is an excerpt of the code, n being the JavaScript array:

// load necessary google charts libraries
google.charts.load("visualization", "1", {'packages':["corechart"]});
google.charts.load('current', {'packages':['bar']});

function plot1() {
        var dataPoints = []; // temporary array
        var n = []; // final array with data
        
        var chartData = new google.visualization.DataTable();
        chartData.addColumn('string', 'Station');
        chartData.addColumn('number', 'Arrival');
        
        n.forEach(function (row) {
            chartData.addRow([row.x, row.y]);
        });
        
        url = // localhost url with json data
        
        // push json data to array
        function addData(data) {
            for (var i = 0; i < data.length; i++) {
                for (var j = 0; j < data[i].delays.length; j++) {
                    dataPoints.push({
                        x: data[i].delays[j].Station,
                        y: data[i].delays[j].Arrival
                    });
                }
            }
            
            // filter "no information" values from array
            values = ["no information"]
            dataPoints = dataPoints.filter(item => !values.includes(item.y));
            
            // eliminate duplicates of x and get average of the y values
            const map = new Map();
            dataPoints.forEach(({ x, y }) => {
                const [total, count] = map.get(x) ?? [null, 0];
                map.set(x, [(total ?? 0) + parseInt(y), count + 1]);
            });
            
            // final array with data in desired format
            n = [...map].map(([k, v]) => ({ x: k, y: v[0] / v[1] }));
            
            console.log(n);
        }
        
        $.getJSON(url, addData);

        var options = {
            width: 700,
            legend: { position: 'none' },
            chart: {
            title: 'Verteilung der Verspätungen bei Ankunft (in Sekunden)'},
            axes: {
                x: {
                0: { side: 'top', label: 'Stationen'} // Top x-axis.
                }
            },
            bar: { groupWidth: "90%" }
                };
        
        var chart = new google.charts.Bar(document.getElementById('plot1'));
        chart.draw(chartData, google.charts.Bar.convertOptions(options));
    };

google.charts.setOnLoadCallback(plot1);

In the HTML header I have specified

<script src = "http://code.jquery.com/jquery-latest.js"></script>
<script src = "https://www.gstatic.com/charts/loader.js"></script>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>

Solution

  • arrayToDataTable expects a simple two-dimensional array, with intrinsic values only.
    with the column headings as the first row.

    var data = google.visualization.arrayToDataTable([
      ['Station', 'Arrival'],
      ['Station a', -77.33333333333333],
      ['Station b', -19],
    ]);
    

    you can find the details here.

    it returns the full data table, so there is no need to use addColumn afterwards.

    -- OR --

    you can create a blank data table, then add the columns and rows.

    var data = new google.visualization.DataTable();
    data.addColumn('string', 'Station');
    data.addColumn('number', 'Arrival');
    
    n.forEach(function (row) {
      data.addRow([row.x, row.y]);
    });
    

    EDIT

    $.getJSON runs asynchronously. so you have to wait until it has finished,
    before the data will be available.

    move the loop to the end of addData.
    then draw the chart once the data is ready.

    note: you don't need the first load statement.

    see following snippet...

    google.charts.load('current', {
      packages: ['bar']
    }).then(plot1);
    
    function plot1() {
        var dataPoints = []; // temporary array
        var n = []; // final array with data
    
        var chartData = new google.visualization.DataTable();
        chartData.addColumn('string', 'Station');
        chartData.addColumn('number', 'Arrival');
    
        url = // localhost url with json data
    
        // push json data to array
        function addData(data) {
            for (var i = 0; i < data.length; i++) {
                for (var j = 0; j < data[i].delays.length; j++) {
                    dataPoints.push({
                        x: data[i].delays[j].Station,
                        y: data[i].delays[j].Arrival
                    });
                }
            }
    
            // filter "no information" values from array
            values = ["no information"]
            dataPoints = dataPoints.filter(item => !values.includes(item.y));
    
            // eliminate duplicates of x and get average of the y values
            const map = new Map();
            dataPoints.forEach(({ x, y }) => {
                const [total, count] = map.get(x) ?? [null, 0];
                map.set(x, [(total ?? 0) + parseInt(y), count + 1]);
            });
    
            // final array with data in desired format
            n = [...map].map(([k, v]) => ({ x: k, y: v[0] / v[1] }));
    
            console.log(n);
    
            n.forEach(function (row) {
                chartData.addRow([row.x, row.y]);
            });
    
    
            var options = {
                width: 700,
                legend: { position: 'none' },
                chart: {
                title: 'Verteilung der Verspätungen bei Ankunft (in Sekunden)'},
                axes: {
                    x: {
                    0: { side: 'top', label: 'Stationen'} // Top x-axis.
                    }
                },
                bar: { groupWidth: "90%" }
                    };
    
            var chart = new google.charts.Bar(document.getElementById('plot1'));
            chart.draw(chartData, google.charts.Bar.convertOptions(options));
    
        }
    
        $.getJSON(url, addData);
    
    };