Stelios Voskos Stelios Voskos - 1 month ago 25
Javascript Question

Google Visualization - Explanation for ChartRangeFilter

I have implemented a line chart that plots JSON data from a geocoded address. The graph has many categories and I also implemented a category filter which works fine. The timeline of the graph is from January to December and I want to give the ability to the user to visualize particular months. Google Visualization ChartRangeFilter fits for the purpose, but there are two points that I need someone to clarify for me if possible.

a) Since we are converting the date to string in:

var chart = new google.visualization.ChartWrapper


why do we assign a date object at
data.addRow
? Also what does the
1,2,3,4
represent under the
'type': 'string'
?

b) I would like to know how can I add the feature of
ChartRangeFilter
into my current code? I already have one controller for the category filter, but I would also like to improve it with the
ChartRangeControl
.

Here is my code so far for the category filter:

var data = new google.visualization.DataTable();
data.addColumn('string', 'Month');
data.addColumn('number', 'Anti-Social Behaviour');
data.addColumn('number', 'Burglary');
data.addColumn('number', 'Drugs');
data.addColumn('number', 'Criminal-damage-arson');
data.addColumn('number', 'Other Theft');
data.addColumn('number', 'Public Disorder Weapons');
data.addColumn('number', 'Robbery');
data.addColumn('number', 'Shoplifting');
data.addColumn('number', 'Vehicle Crime');
data.addColumn('number', 'Violent Crime');
data.addColumn('number', 'Other Crime');
data.addRows([
['January', Math.floor(((Math.random()*1000)+1)/3), Math.floor((Math.random()*100)+1), Math.floor(((Math.random()*1000)+1)/3), Math.floor(((Math.random()*1000)+1)/3), Math.floor(((Math.random()*1000)+1)/3), Math.floor((Math.random()*100)+1), Math.floor((Math.random()*100)+1), Math.floor(((Math.random()*1000)+1)/3), Math.floor(((Math.random()*1000)+1)/3), Math.floor((Math.random()*100)+1), Math.floor(((Math.random()*1000)+1)/3) ],
['February', Math.floor(((Math.random()*1000)+1)/3), Math.floor((Math.random()*100)+1), Math.floor(((Math.random()*1000)+1)/3), Math.floor(((Math.random()*1000)+1)/3), Math.floor(((Math.random()*1000)+1)/3), Math.floor((Math.random()*100)+1), Math.floor((Math.random()*100)+1), Math.floor(((Math.random()*1000)+1)/3), Math.floor(((Math.random()*1000)+1)/3), Math.floor((Math.random()*100)+1), Math.floor(((Math.random()*1000)+1)/3)]
]);

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)]);
initState.selectedValues.push(data.getColumnLabel(i));
}

var chart = new google.visualization.ChartWrapper({
chartType: 'LineChart',
containerId: 'line_div',
dataTable: data,
options: {
title: 'The crime in your area by category',
width: 700,
height: 300
}
});

chart.draw();

var columnFilter = new google.visualization.ControlWrapper({
controlType: 'CategoryFilter',
containerId: 'colFilter_div',
dataTable: columnsTable,
options: {
filterColumnLabel: 'colLabel',
ui: {
label: 'Columns',
allowTyping: false,
allowMultiple: true,
selectedValuesLayout: 'belowStacked'
}
},
state: initState
});

google.visualization.events.addListener(columnFilter, 'statechange', function () {
var state = columnFilter.getState();
var row;
var columnIndices = [0];
for (var i = 0; i < state.selectedValues.length; i++) {
row = columnsTable.getFilteredRows([{column: 1, value: state.selectedValues[i]}])[0];
columnIndices.push(columnsTable.getValue(row, 0));
}
// sort the indices into their original order
columnIndices.sort(function (a, b) {
return (a - b);
});
chart.setView({columns: columnIndices});
chart.draw();
});

columnFilter.draw();


I added random values to the question, as the JSON data would make the length of the code larger.

Answer

The playground example for the ChartRangeFilter is a terrible example, as it needlessly complicates things with date to string conversions that are wholly unnecessary. If you delete the view parameter of the ChartWrapper, the example works just fine:

var chart = new google.visualization.ChartWrapper({
    chartType: 'CandlestickChart',
    containerId: 'chart',
    options: {
        // Use the same chart area width as the control for axis alignment.
        chartArea: {
            height: '80%',
            width: '90%'
        },
        vAxis: {
            viewWindow: {
                min: 0,
                max: 2000
            }
        },
        legend: {
            position: 'none'
        }
    }
});

To answer your questions directly:

The ChartRangeFilter must filter on a continuous data type column (number, date, datetime, timeofday), so that is why the first column of the DataTable is a date type. If I recall the history of the API correctly, the original implementation of CandlestickCharts could not be used with a continuous domain axis type, so the date-to-string conversion was necessary to make the chart work (though I would have preferred that the example simply use a chart that could work with a continuous axis).

The numbers below 'type': 'string' in the example are the rest of the elements in the view.columns array parameter. The array can either take either numbers for the indices of the columns in the base DataTable to reference, or they can take objects that tell the view how to calculate the values to be used in that column. In the example, the first element of the array is an object that converts the dates into strings, and the rest of the elements are the column indices for the data columns.