Google Timeline Visualization doesn't change series row height on slider interaction - javascript

So I've got a timeline with data in it that can be concurrent...
When I move the ChartRangeSlider to a different timeframe, some of the timeline bars will either disappear or show because there is nothing happening in the timeframe that is active.
These is how the timeline and the range slider are set up. I don't have any event listeners running...
// Configure range slider
var timelineRangeSlider = new google.visualization.ControlWrapper({
'controlType': 'ChartRangeFilter',
'containerId': 'timeline-filter',
'state':
{
'range':
{
'start': currentTime,
'end': fourWeek
}
},
'options':
{
'filterColumnIndex': 4,
'ui':
{
'chartType': 'ScatterChart',
'chartOptions':
{
'width': '100%',
'height': '50',
'chartArea':
{
'width': '80%', // make sure this is the same for the chart and control so the axes align right
'height': '80%'
},
'hAxis':
{
'baselineColor': 'none'
}
},
'chartView':
{
'columns': [4,6]
}
}
}
});
// Configure timeline
var timeline = new google.visualization.ChartWrapper({
'chartType': 'Timeline',
'containerId': 'timeline-chart',
'options':
{
'timeline':
{
'showBarLabels': false
},
'width': '100%',
'height': '325',
'tooltip':
{
'isHtml': true
},
'chartArea':
{
'width': '80%', // make sure this is the same for the chart and control so the axes align right
'height': '80%'
},
},
'view':
{
'columns': [0,1,2,3,4,5]
}
});
How can I stop this from happening, and have each of the four separate rows (one for each series) have a static height that won't change when I interact with the range slider?

to display the same number of rows, regardless of the filter settings,
replace the rows removed by the filter with "blank" rows,
doing so will require some manipulation
if you're using a dashboard to bind the chart and filter,
it will probably be easier to draw each independently
listen for the 'statechange' event on the filter,
to know when to re-draw the chart
use a data view to exclude the rows hidden by the filter,
add blank rows in their place
use the colors option on the timeline to set blank rows to 'transparent'
also use a blank tooltip for these rows
see following working snippet, for an example of how this could be accomplished...
google.charts.load('current', {
packages: ['controls', 'timeline']
}).then(function () {
var dataTable = google.visualization.arrayToDataTable([
['Row Label', 'Bar Label', {role: 'tooltip', type: 'string', p: {html: true}}, 'Start', 'End', 'Scatter', 'Data / Blank'],
['A', 'Series 0', null, new Date(2018, 1, 1), new Date(2018, 1, 28), 1, 'data'],
['B', 'Series 1', null, new Date(2018, 4, 1), new Date(2018, 4, 31), 1, 'data'],
['C', 'Series 2', null, new Date(2018, 7, 1), new Date(2018, 7, 31), 1, 'data'],
['D', 'Series 3', null, new Date(2018, 10, 1), new Date(2018, 10, 30), 1, 'data']
]);
var blankTooltip = '<div class="hidden"></div>';
var colorPallette = ['cyan', 'magenta', 'lime', 'yellow'];
var dateRange = {
start: dataTable.getColumnRange(3),
end: dataTable.getColumnRange(4)
};
// Configure range slider
var timelineRangeSlider = new google.visualization.ControlWrapper({
controlType: 'ChartRangeFilter',
containerId: 'timeline-filter',
dataTable: dataTable,
state: {
range: {
start: dateRange.start.min,
end: dateRange.end.max
}
},
options: {
filterColumnIndex: 3,
ui: {
chartType: 'ScatterChart',
chartOptions: {
width: '100%',
height: '50',
chartArea: {
width: '80%',
height: '80%'
},
hAxis: {
baselineColor: 'none'
}
},
chartView: {
columns: [3,5]
}
}
}
});
google.visualization.events.addListener(timelineRangeSlider, 'statechange', function (props) {
// filter state
var state = timelineRangeSlider.getState();
// wait until statechange has finished
if (!props.inProgress) {
// delete previously added blank rows
var blankRows = dataTable.getFilteredRows([{
column: 6,
value: 'blank'
}]);
var i = blankRows.length;
while (i--) {
dataTable.removeRow(blankRows[i]);
}
// add blank rows for non-visible rows
var blankRows = [];
var timelineColors = [];
var visibleRows = dataTable.getFilteredRows([{
column: 3,
minValue: state.range.start
}, {
column: 4,
maxValue: state.range.end
}]);
for (var i = 0; i < dataTable.getNumberOfRows(); i++) {
if (visibleRows.indexOf(i) === -1) {
blankRows.push([
dataTable.getValue(i, 0),
dataTable.getValue(i, 1),
blankTooltip,
state.range.start,
state.range.start,
1,
'blank'
]);
timelineColors.push('transparent');
} else {
timelineColors.push(colorPallette[i]);
}
}
// build timeline view rows
var lastRowIndex = dataTable.addRows(blankRows);
var i = blankRows.length;
while (i--) {
visibleRows.push((lastRowIndex - i));
}
// re-config timeline
var timelineView = new google.visualization.DataView(dataTable);
timelineView.setRows(visibleRows);
timelineView = timelineView.toDataTable();
timelineView.sort([{column: 0}]);
timeline.setDataTable(timelineView);
timeline.setOption('colors', timelineColors);
timeline.draw();
}
});
timelineRangeSlider.draw();
// Configure timeline
var timeline = new google.visualization.ChartWrapper({
chartType: 'Timeline',
containerId: 'timeline-chart',
dataTable: dataTable,
options: {
colors: colorPallette,
timeline: {
showBarLabels: false
},
width: '100%',
height: '325',
tooltip: {
isHtml: true
},
chartArea: {
width: '80%',
height: '80%'
}
},
view: {
columns: [0,1,2,3,4]
}
});
timeline.draw();
});
.hidden {
display: none;
visibility: hidden;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="dashboard_div">
<div id="timeline-filter"></div>
<div id="timeline-chart"></div>
</div>

Related

How to show annotations on column chart? [duplicate]

I've been trying to add annotations to google bar chart. I've seen related questions about this, but can't solve it. There are 2 bar in charts and i want to show numbers on bars. I added 2 annotation column to my datatable. Chart draw works without annotations and there is no error at console. So i need some help. Here is my code:
var options = {
chart: {
title: 'xxxx',
subtitle: 'xxxx',
focusTarget:'category'
},
annotations: {
textStyle: {
color: 'black',
fontSize: 11,
},
alwaysOutside: true
},
height:300,
vAxis: {format: 'short'},
colors: ['#17807E', '#4285F4']
};
function drawAudits(Data) //data comes from another function with ajax call
{
var dataTbl = new google.visualization.DataTable();
dataTbl.addColumn('string', 'Months');
dataTbl.addColumn('number', 'Scheduled');
dataTbl.addColumn('number', 'Done');
dataTbl.addColumn({ type:'number' , role: 'annotation' });
dataTbl.addColumn({ type:'number' , role: 'annotation' });
for (var i = 0; i < Data.length; i++)
{
dataTbl.addRow([Data[i].month, Data[i].AllAudits, Data[i].DoneAudits, Data[i].AllAudits, Data[i].DoneAudits]);
} //last 2 column for annotations
var chart = new google.charts.Bar(document.getElementById('columnChartDiv'));
chart.draw(dataTbl,options);
}
thanks in advance.
annotations.* are not supported by Material charts, along with several other options...
see --> Tracking Issue for Material Chart Feature Parity #2143
the following option will style Core charts, similar to Material...
theme: 'material'
note: the annotation column should directly follow the series it represents...
see following working snippet...
google.charts.load('current', {
callback: function () {
var options = {
annotations: {
textStyle: {
color: 'black',
fontSize: 11,
},
alwaysOutside: true
},
chartArea: {
left: 36,
width: '100%'
},
colors: ['#17807E', '#4285F4'],
focusTarget:'category',
height:300,
legend: {
position: 'bottom'
},
theme: 'material',
title: 'xxxx',
vAxis: {
format: 'short',
viewWindow: {
max: 12
}
},
};
drawAudits();
window.addEventListener('resize', drawAudits, false);
function drawAudits(Data) {
var dataTbl = new google.visualization.DataTable();
dataTbl.addColumn('string', 'Months');
dataTbl.addColumn('number', 'Scheduled');
dataTbl.addColumn({ type:'number' , role: 'annotation' });
dataTbl.addColumn('number', 'Done');
dataTbl.addColumn({ type:'number' , role: 'annotation' });
Data = [
{month: 'Jan', AllAudits: 10, DoneAudits: 5},
{month: 'Feb', AllAudits: 10, DoneAudits: 6}
];
for (var i = 0; i < Data.length; i++) {
dataTbl.addRow([Data[i].month, Data[i].AllAudits, Data[i].AllAudits, Data[i].DoneAudits, Data[i].DoneAudits]);
}
var chart = new google.visualization.ColumnChart(document.getElementById('columnChartDiv'));
chart.draw(dataTbl,options);
}
},
packages: ['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="columnChartDiv"></div>

Google Bar Chart with Percentage of Total and with Category Filters

I am fairly new to Google Charts and was trying to create a bar chart with % of total, along with the ability to filter the data by using Google Dashboard Controls... I followed this (thanks to #asgallant for this!) google.visualization.ChartWrapper Group Columns View and was able to get a bar chart which picks up data from a google sheet, and draws the chart with counts and also have the ability to filter the data using Google Category filters.
However, this is where I am stuck - when I try to add another columns (dataview) for calculating the total (so that I can draw the chart using the percentage and also show the percentage in the bar labels) - my chart is still drawing using the counts.. Can anyone please let me know what am I going wrong here:
function drawVisualization() {
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1LBoS8Q7qdpWVjks3FytQAefThzY3VbAHllf04nE6qO8/edit?gid=1629614877&range=A:D');
query.send(handleQueryResponse);
function handleQueryResponse(response) {
if (response.isError()) {return; }
var data = response.getDataTable();
// Define category pickers for All Filters
var CardTier = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'control1',
'options': {
'filterColumnLabel': 'CardTier Filter',
'ui': {
'labelStacking': 'vertical',
'allowTyping': false,
'allowMultiple': false
}
}
});
var Campaign = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'control2',
'options': {
'filterColumnLabel': 'Campaign Filter',
'ui': {
'labelStacking': 'vertical',
'allowTyping': false,
'allowMultiple': false
}
}
});
// Define a bar chart to show 'Population' data
var barChart = new google.visualization.ChartWrapper({
'chartType': 'BarChart',
'containerId': 'chart1',
'options': {
'width': 400,
'height': 300,
'chartArea': {top: 0, right: 0, bottom: 0}
},
// Configure the barchart to use columns 0 (Card Tier) and 1 (Campaign Filter) (Basically the filters)
'view': {'columns': [0, 1]}
});
var proxyTable = new google.visualization.ChartWrapper({
chartType: 'Table',
containerId: 'proxyTable',
options: {
// minimize the footprint of the table in HTML
page: 'enable',
pageSize: 1
},
view: {
columns: [0]
}
});
// create a "ready" event handler for proxyTable the handles data aggregation and drawing barChart
// Add The question's column index here. We want to draw Status so we Group 2 with dt and also its count...
google.visualization.events.addListener(proxyTable, 'ready', function () {
var dt = proxyTable.getDataTable();
var groupedData = google.visualization.data.group(dt, [2], [{
column: 3,
type: 'number',
label: dt.getColumnLabel(2),
aggregation: google.visualization.data.count
}]);
var view = new google.visualization.DataView(groupedData);
view.setColumns([0, 1, {
calc: function (dt, row) {
var amount = formatShort.formatValue(dt.getValue(row, 1));
var percent = formatPercent.formatValue(dt.getValue(row, 1) / groupedData.getValue(0, 1));
return amount + ' (' + percent + ')';
},
type: 'string',
role: 'annotation'
}]);
// after grouping, the data will be sorted by column 0, then 1, then 2
// if you want a different order, you have to re-sort
barChart.setDataTable(view);
barChart.draw();
});
// Create the dashboard.
new google.visualization.Dashboard(document.getElementById('dashboard')).
// Configure the controls :
bind(CardTier, Campaign).
bind(Campaign, proxyTable).
// Draw the dashboard
draw(data);
}
}
google.load('visualization', '1', {packages:['corechart', 'controls', 'table'], callback: drawVisualization});
</script>
</head>
<body>
<div id="dashboard">
<table>
<tr style='vertical-align: top'>
<td style='width: 300px; font-size: 0.9em;'>
<div id="control1"></div>
<div id="control2"></div>
</td>
<td style='width: 600px'>
<div style="float: left;" id="chart1"></div>
<div style="float: left;" id="chart2"></div>
</td>
</tr>
</table>
<div id="proxyTable" style="display: none;"></div>
</div>
</body>
</html>
for starters, recommend using the newer library loader.js
<script src="https://www.gstatic.com/charts/loader.js"></script>
instead of jsapi, according to the release notes...
The version of Google Charts that remains available via the jsapi loader is no longer being updated consistently. Please use the new gstatic loader from now on.
this will only change the load statement, see following working snippet...
next, didn't see the definitions for the number formatters
formatShort and formatPercent
need to add those
groupedData will give you the total for each status
to get the the total for all the rows,
need to use the modifier function
this will change the value to 'Total' for the first column of all rows
allowing the group method to aggregate all rows
var totalData = google.visualization.data.group(
dataTable,
[{column: 0, type: 'string', modifier: function () {return 'Total';}}],
[{
column: 3,
type: 'number',
label: dataTable.getColumnLabel(2),
aggregation: google.visualization.data.count
}]
);
finally, remove the view option from barChart
since we're providing the view we want drawn
see following working snippet...
google.charts.load('current', {
callback: drawVisualization,
packages: ['corechart', 'controls', 'table']
});
function drawVisualization() {
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1LBoS8Q7qdpWVjks3FytQAefThzY3VbAHllf04nE6qO8/edit?gid=1629614877&range=A:D');
query.send(handleQueryResponse);
function handleQueryResponse(response) {
if (response.isError()) {return; }
var data = response.getDataTable();
// Define category pickers for All Filters
var CardTier = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'control1',
'options': {
'filterColumnLabel': 'CardTier Filter',
'ui': {
'labelStacking': 'vertical',
'allowTyping': false,
'allowMultiple': false
}
}
});
var Campaign = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'control2',
'options': {
'filterColumnLabel': 'Campaign Filter',
'ui': {
'labelStacking': 'vertical',
'allowTyping': false,
'allowMultiple': false
}
}
});
// Define a bar chart to show 'Population' data
var barChart = new google.visualization.ChartWrapper({
'chartType': 'BarChart',
'containerId': 'chart1',
'options': {
'width': 400,
'height': 300,
'chartArea': {top: 0, right: 0, bottom: 0}
}
});
var proxyTable = new google.visualization.ChartWrapper({
chartType: 'Table',
containerId: 'proxyTable',
options: {
// minimize the footprint of the table in HTML
page: 'enable',
pageSize: 1
},
view: {
columns: [0]
}
});
// create a "ready" event handler for proxyTable the handles data aggregation and drawing barChart
// Add The question's column index here. We want to draw Status so we Group 2 with dt and also its count...
google.visualization.events.addListener(proxyTable, 'ready', function () {
var formatShort = new google.visualization.NumberFormat({
pattern: 'short'
});
var formatPercent = new google.visualization.NumberFormat({
pattern: '0.0%'
});
var dataTable = proxyTable.getDataTable();
// group by status
var groupedData = google.visualization.data.group(
dataTable,
[2],
[{
column: 3,
type: 'number',
label: dataTable.getColumnLabel(2),
aggregation: google.visualization.data.count
}]
);
// status total
var totalData = google.visualization.data.group(
dataTable,
[{column: 0, type: 'string', modifier: function () {return 'Total';}}],
[{
column: 3,
type: 'number',
label: dataTable.getColumnLabel(2),
aggregation: google.visualization.data.count
}]
);
var view = new google.visualization.DataView(groupedData);
view.setColumns([0, 1, {
calc: function (dt, row) {
var amount = dt.getValue(row, 1);
var total = totalData.getValue(0, 1);
var percent = 0;
if (total > 0) {
percent = amount / total;
}
return formatShort.formatValue(amount) + ' (' + formatPercent.formatValue(percent) + ')';
},
type: 'string',
role: 'annotation'
}]);
// after grouping, the data will be sorted by column 0, then 1, then 2
// if you want a different order, you have to re-sort
barChart.setDataTable(view);
barChart.draw();
});
// Create the dashboard.
new google.visualization.Dashboard(document.getElementById('dashboard')).
// Configure the controls :
bind(CardTier, Campaign).
bind(Campaign, proxyTable).
// Draw the dashboard
draw(data);
}
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="control1"></div>
<div id="control2"></div>
<div id="chart1"></div>
<div id="proxyTable"></div>
UPDATE
to draw the percentages instead of counts,
just need to add another calculated column to the view
as for showing zero values,
use the original data table to get a distinct list of status values
check if the status exists in groupedData
if not, add a row for the status
// add back missing status
var statusValues = data.getDistinctValues(2);
statusValues.forEach(function (status) {
var statusRow = groupedData.getFilteredRows([{
column: 0,
value: status
}]);
if (statusRow.length === 0) {
groupedData.addRow([
status,
0
]);
}
});
groupedData.sort([{column: 0}]);
see following working snippet...
google.charts.load('current', {
callback: drawVisualization,
packages: ['corechart', 'controls', 'table']
});
function drawVisualization() {
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/1LBoS8Q7qdpWVjks3FytQAefThzY3VbAHllf04nE6qO8/edit?gid=1629614877&range=A:D');
query.send(handleQueryResponse);
function handleQueryResponse(response) {
if (response.isError()) {return; }
var data = response.getDataTable();
// Define category pickers for All Filters
var CardTier = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'control1',
'options': {
'filterColumnLabel': 'CardTier Filter',
'ui': {
'labelStacking': 'vertical',
'allowTyping': false,
'allowMultiple': false
}
}
});
var Campaign = new google.visualization.ControlWrapper({
'controlType': 'CategoryFilter',
'containerId': 'control2',
'options': {
'filterColumnLabel': 'Campaign Filter',
'ui': {
'labelStacking': 'vertical',
'allowTyping': false,
'allowMultiple': false
}
}
});
// Define a bar chart to show 'Population' data
var barChart = new google.visualization.ChartWrapper({
'chartType': 'BarChart',
'containerId': 'chart1',
'options': {
'width': 400,
'height': 300,
'chartArea': {top: 0, right: 0, bottom: 0}
}
});
var proxyTable = new google.visualization.ChartWrapper({
chartType: 'Table',
containerId: 'proxyTable',
options: {
// minimize the footprint of the table in HTML
page: 'enable',
pageSize: 1
},
view: {
columns: [0]
}
});
// create a "ready" event handler for proxyTable the handles data aggregation and drawing barChart
// Add The question's column index here. We want to draw Status so we Group 2 with dt and also its count...
google.visualization.events.addListener(proxyTable, 'ready', function () {
var formatShort = new google.visualization.NumberFormat({
pattern: 'short'
});
var formatPercent = new google.visualization.NumberFormat({
pattern: '0.0%'
});
var dataTable = proxyTable.getDataTable();
// group by status
var groupedData = google.visualization.data.group(
dataTable,
[2],
[{
column: 3,
type: 'number',
label: dataTable.getColumnLabel(2),
aggregation: google.visualization.data.count
}]
);
// add back missing status
var statusValues = data.getDistinctValues(2);
statusValues.forEach(function (status) {
var statusRow = groupedData.getFilteredRows([{
column: 0,
value: status
}]);
if (statusRow.length === 0) {
groupedData.addRow([
status,
0
]);
}
});
groupedData.sort([{column: 0}]);
// status total
var totalData = google.visualization.data.group(
dataTable,
[{column: 0, type: 'string', modifier: function () {return 'Total';}}],
[{
column: 3,
type: 'number',
label: dataTable.getColumnLabel(2),
aggregation: google.visualization.data.count
}]
);
var view = new google.visualization.DataView(groupedData);
view.setColumns([0, {
calc: function (dt, row) {
var amount = dt.getValue(row, 1);
var total = totalData.getValue(0, 1);
var percent = 0;
if (total > 0) {
percent = amount / total;
}
return {
v: percent,
f: formatPercent.formatValue(percent)
};
},
type: 'number',
label: 'Percent'
}, {
calc: function (dt, row) {
var amount = dt.getValue(row, 1);
var total = totalData.getValue(0, 1);
var percent = 0;
if (total > 0) {
percent = amount / total;
}
return formatPercent.formatValue(percent) + ' (' + formatShort.formatValue(amount) + ')';
},
type: 'string',
role: 'annotation'
}]);
// after grouping, the data will be sorted by column 0, then 1, then 2
// if you want a different order, you have to re-sort
barChart.setDataTable(view);
barChart.draw();
});
// Create the dashboard.
new google.visualization.Dashboard(document.getElementById('dashboard')).
// Configure the controls :
bind(CardTier, Campaign).
bind(Campaign, proxyTable).
// Draw the dashboard
draw(data);
}
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="control1"></div>
<div id="control2"></div>
<div id="chart1"></div>
<div id="proxyTable"></div>
UPDATE 2
to find the total of a multiple choice question,
create a view with a calculated column
the new column should test that all question columns are not blank
then total the view on the calculated column
see following working snippet...
google.charts.load('current', {
callback: drawVisualization,
packages: ['corechart', 'controls', 'table']
});
function drawVisualization() {
var query = new google.visualization.Query('https://docs.google.com/spreadsheets/d/19VWNZkHG5GEuYCibDmtOlKblKiOWcx94Wi9jyuhvEUo/edit#gid=0');
query.setQuery('select A,B,C,D,E,F,G');
query.send(handleQueryResponse);
function handleQueryResponse(response) {
if (response.isError()) {return;}
var data = response.getDataTable();
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, 2, 3, 4, 5, 6, {
calc: function (dt, row) {
var answered = 0;
var q1_1 = dt.getValue(row, 3) || '';
var q1_2 = dt.getValue(row, 4) || '';
var q1_3 = dt.getValue(row, 5) || '';
var q1_4 = dt.getValue(row, 6) || '';
if ((q1_1 !== '') || (q1_2 !== '') || (q1_3 !== '') || (q1_4 !== '')) {
answered = 1;
}
return answered;
},
label: 'Answered',
type: 'number'
}]);
var totalAnswered = google.visualization.data.group(
view,
[{column: 0, type: 'string', modifier: function () {return 'Total';}}],
[{
column: view.getNumberOfColumns() - 1,
type: 'number',
label: view.getColumnLabel(view.getNumberOfColumns() - 1),
aggregation: google.visualization.data.sum
}]
);
var proxyTable = new google.visualization.ChartWrapper({
chartType: 'Table',
containerId: 'proxyTable',
dataTable: view
});
proxyTable.draw();
document.getElementById('proxyTableTotal').innerHTML = 'Total Answered = ' + totalAnswered.getValue(0, 1);
}
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="proxyTable"></div>
<div id="proxyTableTotal"></div>

Google chart - role: annotation in candlestick bar [duplicate]

i'm trying to use Google Chart API for building an Waterfall chart. I noticed that Candlestick/Waterfall charts are not supporting the annotations.
See this jsfiddle sample
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Category');
data.addColumn('number', 'MinimumLevel');
data.addColumn('number', 'MinimumLevel1');
data.addColumn('number', 'MaximumLevel');
data.addColumn('number', 'MaximumLevel1');
data.addColumn({type: 'number', role: 'tooltip'});
data.addColumn({type: 'string', role: 'style'});
data.addColumn({type: 'number', role: 'annotation'});
data.addRow(['Category 1', 0 , 0, 5, 5, 5,'gray',5]);
data.addRow(['Category 2', 5 , 5, 10, 10, 10,'red',10]);
data.addRow(['Category 3', 10 , 10, 15, 15, 15,'blue',15]);
data.addRow(['Category 4', 15 , 15, 10, 10, 10,'yellow',10]);
data.addRow(['Category 5', 10 , 10, 5, 5, 5,'gray',5]);
var options = {
legend: 'none',
bar: { groupWidth: '60%' } // Remove space between bars.
};
var chart = new google.visualization.CandlestickChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
I would like to put the value of the 5th column at the top of every candlestick.
It should look like this :
Is there a way to do this?
Thanks
I add annotations to candlestick charts by adding annotations to a hidden scatter plot. You can set exactly where you want the annotations to sit by changing the plot.
google.charts.load('current', { 'packages': ['corechart'] });
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', 'Low');
data.addColumn('number', 'Open');
data.addColumn('number', 'Close');
data.addColumn('number', 'High');
data.addColumn('number'); //scatter plot for annotations
data.addColumn({ type: 'string', role: 'annotation' }); // annotation role col.
data.addColumn({ type: 'string', role: 'annotationText' }); // annotationText col.
var high, low, open, close = 160;
for (var i = 0; i < 10; i++) {
open = close;
close += ~~(Math.random() * 10) * Math.pow(-1, ~~(Math.random() * 2));
high = Math.max(open, close) + ~~(Math.random() * 10);
low = Math.min(open, close) - ~~(Math.random() * 10);
annotation = '$' + close;
annotation_text = 'Close price: $' + close;
data.addRow([new Date(2014, 0, i + 1), low, open, close, high, high, annotation, annotation_text]);
}
var view = new google.visualization.DataView(data);
var chart = new google.visualization.ComboChart(document.querySelector('#chart_div'));
chart.draw(view, {
height: 400,
width: 600,
explorer: {},
chartArea: {
left: '7%',
width: '70%'
},
series: {
0: {
color: 'black',
type: 'candlesticks',
},
1: {
type: 'scatter',
pointSize: 0,
targetAxisIndex: 0,
},
},
candlestick: {
color: '#a52714',
fallingColor: { strokeWidth: 0, fill: '#a52714' }, // red
risingColor: { strokeWidth: 0, fill: '#0f9d58' } // green
},
});
}
<script type="text/javascript"src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div" style="width: 900px; height: 500px;"></div>
just so happens, i ran into the same problem this week
so I added my own annotations, during the 'animationfinish' event
see following working snippet...
google.charts.load('current', {
callback: drawChart,
packages:['corechart']
});
function drawChart() {
var dataChart = new google.visualization.DataTable({"cols":[{"label":"Category","type":"string"},{"label":"Bottom 1","type":"number"},{"label":"Bottom 2","type":"number"},{"label":"Top 1","type":"number"},{"label":"Top 2","type":"number"},{"role":"style","type":"string","p":{"role":"style"}}],"rows":[{"c":[{"v":"Budget"},{"v":0},{"v":0},{"v":22707893.613},{"v":22707893.613},{"v":"#007fff"}]},{"c":[{"v":"Contract Labor"},{"v":22707893.613},{"v":22707893.613},{"v":22534350.429},{"v":22534350.429},{"v":"#1e8449"}]},{"c":[{"v":"Contract Non Labor"},{"v":22534350.429},{"v":22534350.429},{"v":22930956.493},{"v":22930956.493},{"v":"#922b21"}]},{"c":[{"v":"Materials and Equipment"},{"v":22930956.493},{"v":22930956.493},{"v":22800059.612},{"v":22800059.612},{"v":"#1e8449"}]},{"c":[{"v":"Other"},{"v":22800059.612},{"v":22800059.612},{"v":21993391.103},{"v":21993391.103},{"v":"#1e8449"}]},{"c":[{"v":"Labor"},{"v":21993391.103},{"v":21993391.103},{"v":21546003.177999996},{"v":21546003.177999996},{"v":"#1e8449"}]},{"c":[{"v":"Travel"},{"v":21546003.177999996},{"v":21546003.177999996},{"v":21533258.930999994},{"v":21533258.930999994},{"v":"#1e8449"}]},{"c":[{"v":"Training"},{"v":21533258.930999994},{"v":21533258.930999994},{"v":21550964.529999994},{"v":21550964.529999994},{"v":"#922b21"}]},{"c":[{"v":"Actual"},{"v":0},{"v":0},{"v":21550964.52999999},{"v":21550964.52999999},{"v":"#007fff"}]}]});
var waterFallChart = new google.visualization.ChartWrapper({
chartType: 'CandlestickChart',
containerId: 'chart_div',
dataTable: dataChart,
options: {
animation: {
duration: 1500,
easing: 'inAndOut',
startup: true
},
backgroundColor: 'transparent',
bar: {
groupWidth: '85%'
},
chartArea: {
backgroundColor: 'transparent',
height: 210,
left: 60,
top: 24,
width: '100%'
},
hAxis: {
slantedText: false,
textStyle: {
color: '#616161',
fontSize: 9
}
},
height: 272,
legend: 'none',
tooltip: {
isHtml: true,
trigger: 'both'
},
vAxis: {
format: 'short',
gridlines: {
count: -1
},
textStyle: {
color: '#616161'
},
viewWindow: {
max: 24000000,
min: 16000000
}
},
width: '100%'
}
});
google.visualization.events.addOneTimeListener(waterFallChart, 'ready', function () {
google.visualization.events.addListener(waterFallChart.getChart(), 'animationfinish', function () {
var annotation;
var chartLayout;
var container;
var numberFormatShort;
var positionY;
var positionX;
var rowBalance;
var rowBottom;
var rowFormattedValue;
var rowIndex;
var rowTop;
var rowValue;
var rowWidth;
container = document.getElementById(waterFallChart.getContainerId());
chartLayout = waterFallChart.getChart().getChartLayoutInterface();
numberFormatShort = new google.visualization.NumberFormat({
pattern: 'short'
});
rowIndex = 0;
Array.prototype.forEach.call(container.getElementsByTagName('rect'), function(rect) {
switch (rect.getAttribute('fill')) {
// use colors to identify bars
case '#922b21':
case '#1e8449':
case '#007fff':
rowWidth = parseFloat(rect.getAttribute('width'));
if (rowWidth > 2) {
rowBottom = waterFallChart.getDataTable().getValue(rowIndex, 1);
rowTop = waterFallChart.getDataTable().getValue(rowIndex, 3);
rowValue = rowTop - rowBottom;
rowBalance = Math.max(rowBottom, rowTop);
positionY = chartLayout.getYLocation(rowBalance) - 6;
positionX = parseFloat(rect.getAttribute('x'));
rowFormattedValue = numberFormatShort.formatValue(rowValue);
if (rowValue < 0) {
rowFormattedValue = rowFormattedValue.replace('-', '');
rowFormattedValue = '(' + rowFormattedValue + ')';
}
annotation = container.getElementsByTagName('svg')[0].appendChild(container.getElementsByTagName('text')[0].cloneNode(true));
$(annotation).text(rowFormattedValue);
annotation.setAttribute('x', (positionX + (rowWidth / 2)));
annotation.setAttribute('y', positionY);
annotation.setAttribute('font-weight', 'bold');
rowIndex++;
}
break;
}
});
});
});
$(window).resize(function() {
waterFallChart.draw();
});
waterFallChart.draw();
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Google Charts: ChartRangeFilter and CategoryFilter with discontinuous DataTable

I'm trying to use Google Charts to display multiple curves in a LineChart with two controls: CategoryFilter and ChartRangeFilter.
I want ChartRangeFilter to display only selected columns in CategoryFilter, but I often get exceptions:
One or more participants failed to draw()
and/or
undefined is not an object (evaluating 'd[0].x')
I think the problem is that the data I need to show are discontinuous, here's an example:
var data = new google.visualization.DataTable();
data.addColumn('date', 'DATE');
data.addColumn('number', 'DATA.1');
data.addColumn('number', 'DATA.2');
data.addColumn('number', 'DATA.3');
data.addRows
([
[new Date(2016,9,19), 18, null, null],
[new Date(2016,9,20), 24, null, null],
[new Date(2016,9,21), 41, null, null],
[new Date(2016,9,22), 47, null, null],
[new Date(2016,9,23), 60, null, null],
[new Date(2016,9,24), 79, null, null],
[new Date(2016,9,25), null, 3, null],
[new Date(2016,9,26), null, 4, null],
[new Date(2016,9,27), null, 10, null],
[new Date(2016,9,28), null, 11, 123],
[new Date(2016,9,29), null, 4, 130],
[new Date(2016,9,30), 6, null, 132],
]);
and a code snippet of my chart:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<title>NEW CHART</title>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
.labels{font-size: 50%;}
</style>
<script type="text/javascript" src="../cal/formatDate.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
//-------------------------------------------------------------------GOOGLE CHARTS ->
google.charts.load('current', {'packages':['corechart', 'controls']});
google.charts.setOnLoadCallback(drawChart);
function drawChart ()
{
var data = new google.visualization.DataTable();
data.addColumn('date', 'DATE');
data.addColumn('number', 'DATA.1');data.addColumn('number', 'DATA.2');data.addColumn('number', 'DATA.3');
data.addRows
([
[new Date(2016,9,19),18,null,null],
[new Date(2016,9,20),24,null,null],
[new Date(2016,9,21),41,null,null],
[new Date(2016,9,22),47,null,null],
[new Date(2016,9,23),60,null,null],
[new Date(2016,9,24),79,null,null],
[new Date(2016,9,25),null,3,null],
[new Date(2016,9,26),null,4,null],
[new Date(2016,9,27),null,10,null],
[new Date(2016,9,28),null,11,123],
[new Date(2016,9,29),null,4,130],
[new Date(2016,9,30),6,null,132],
]);
var dash = new google.visualization.Dashboard(document.getElementById('dashboard'));
var columnsTable = new google.visualization.DataTable();
columnsTable.addColumn('number', 'colIndex');
columnsTable.addColumn('string', 'colLabel');
var initState= {selectedValues: []};
// populate columnsTable Rows skipping column 0
for (var i = 1; i < data.getNumberOfColumns(); i++)
{
columnsTable.addRow([i, data.getColumnLabel(i)]);
// comment out next line to have a default selection other than the whole list
// initState.selectedValues.push(data.getColumnLabel(i));
}
// set individual columns to be the default columns (not with the loop above)
// initState.selectedValues.push(data.getColumnLabel(4));
// initialize the CategoryFilter
var CategoryFilter_control = new google.visualization.ControlWrapper
({
controlType: 'CategoryFilter',
containerId: 'CategoryFilter_div',
dataTable: columnsTable,
options:
{
filterColumnLabel: 'colLabel',
ui:
{
label: 'Columns',
allowTyping: false,
allowMultiple: true,
allowNone: false,
selectedValuesLayout: 'aside'
}
},
'state': {'selectedValues': ['DATA.1']}
});
// initialize the LineChart
var chart = new google.visualization.ChartWrapper
({
chartType: 'LineChart',
containerId: 'chart_div',
dataTable: data,
options:
{
title: 'SOCIAL FOLLOWERS',
curveType: 'function',
interpolateNulls: 'true',
pointSize: 2,
legend: { position: 'right', textStyle:{ fontSize: 10 } },
chartArea: { left: 50, top: 10, width: "80%", height: "90%" },
trendlines:
{
0:
{
type: 'linear',
lineWidth: 1,
dataOpacity: 0.2,
showR2: false,
pointSize: 0,
visibleInLegend: false
},
1:
{
type: 'linear',
lineWidth: 1,
dataOpacity: 0.2,
showR2: false,
pointSize: 0,
visibleInLegend: false
},
2:
{
type: 'linear',
lineWidth: 1,
dataOpacity: 0.2,
showR2: false,
pointSize: 0,
visibleInLegend: false
},
}
}
});
// initialize the ChartRangeFilter
var ChartRangeFilter_control = new google.visualization.ControlWrapper
({
controlType: 'ChartRangeFilter',
containerId: 'ChartRangeFilter_div',
dataTable: data,
options:
{
filterColumnIndex: 0,
ui: { chartOptions: {height:50,width:'100%',chartArea:{left:50,width: '80%'}} }
}
});
// set the chart
function setChartView ()
{
var state = CategoryFilter_control.getState();
var row;
var view = {columns: [0]};
for (var i = 0; i < state.selectedValues.length; i++)
{
row = columnsTable.getFilteredRows([{column: 1, value: state.selectedValues[i]}])[0];
view.columns.push(columnsTable.getValue(row, 0));
}
chart.setView(view);
chart.draw();
}
// set the ChartRangeFilter ??
function setRangeFilterView ()
{
var state = CategoryFilter_control.getState();
var row;
var view = {columns: [0]};
for (var i = 0; i < state.selectedValues.length; i++)
{
row = columnsTable.getFilteredRows([{column: 1, value: state.selectedValues[i]}])[0];
view.columns.push(columnsTable.getValue(row, 0));
}
ChartRangeFilter_control.setView(view);
ChartRangeFilter_control.draw();
}
// display selected Range
function displayRange()
{
var v = ChartRangeFilter_control.getState();
document.getElementById('dbgchart').innerHTML = v.range.start.format("%d-%m-%Y")+ ' to ' +v.range.end.format("%d-%m-%Y");
return 0;
}
// draw the CategoryFilter
CategoryFilter_control.draw();
// bind LineChart and ChartRangeFilter
dash.bind([ChartRangeFilter_control], [chart]);
dash.draw(data);
setChartView();
setRangeFilterView();
google.visualization.events.addListener(CategoryFilter_control, 'statechange', setChartView);
google.visualization.events.addListener(CategoryFilter_control, 'statechange', setRangeFilterView);
google.visualization.events.addListener(ChartRangeFilter_control, 'statechange', displayRange);
}
//-------------------------------------------------------------------GOOGLE CHARTS <-
</script>
</head>
<body>
<h3>Social Followers Overview</h3>
<div id="dashboard">
<div id="CategoryFilter_div"></div>
<div id="chart_div" style="margin:0; padding:0; width: auto; height: 75vh"></div>
<div id="ChartRangeFilter_div"></div>
<p><span id='dbgchart' style="padding-top: 50; padding-left: 50px;"></span></p>
</div>
</body>
</html>
What should I do to prevent trying to draw null data when there are holes in the DataTable?
I've found a workaround here
How to remove default error message in google chart
so I added this code to drawChart() function:
google.visualization.events.addListener(chart, 'error', function (googleError) {
google.visualization.errors.removeError(googleError.id);
});
google.visualization.events.addListener(dash, 'error', function (googleError) {
google.visualization.errors.removeError(googleError.id);
});
but if you move the thumbs to select a range where some data are missing, the error occurs (even if it is removed) and the chart won't change...

Google Charts - switch between table and chart view

There was a similar question (Google Charts: Switching between Line and Column charts) about switching between line and columns charts, but it doesn't seem to work for tables.
I have a line chart that I want to change into table and back... the only way i see this happening is by redeclaring a table similar to...
function changeIntoTable() {
var table = new google.visualization.Table(document.getElementById('dashboard_div'));
table.draw(data, {showRowNumber: true, width: '100%', height: '100%'});
}
function changeIntoChart() {
// Create a dashboard.
var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div'));
// Create a line chart, passing some options
var lineChart = new google.visualization.ChartWrapper({
'chartType': 'LineChart',
'containerId': 'chart_div',
'options': {
backgroundColor: { fill:'transparent' },
'legend': 'right',
'pointSize': 5,
crosshair: { trigger: 'both' }, // Display crosshairs on focus and selection.
hAxis: {
title: 'Time'
},
vAxis: {
title: 'Value'
}
}
});
dashboard.bind(lineChart);
dashboard.draw(data);
}
So, i am wondering if there is a simpler solution?
What you have should work fine but...
You could take advantage of the ChartWrapper Class which can draw any chart type...
Here's an example, click the button to switch the chart...
google.charts.load('current', {
packages: ['corechart', 'table'],
callback: initChart
});
function initChart() {
var button;
var chart;
var data;
var showChart = 'Table';
data = google.visualization.arrayToDataTable([
['Year', 'Sales', 'Expenses'],
['2004', 1000, 400],
['2005', 1170, 460],
['2006', 660, 1120],
['2007', 1030, 540]
]);
chart = new google.visualization.ChartWrapper({
containerId: 'chart_div',
dataTable: data
});
button = document.getElementById('btnSwitch');
button.addEventListener('click', switchChart, false);
// draw initial chart
switchChart();
function switchChart() {
button.value = showChart;
showChart = (showChart === 'Table') ? 'LineChart' : 'Table';
drawChart(showChart);
}
function drawChart(chartType) {
chart.setChartType(chartType);
chart.setOptions(getOptions(chartType));
chart.draw();
}
function getOptions(chartType) {
var options;
switch (chartType) {
case 'LineChart':
options = {
backgroundColor: {
fill:'transparent'
},
legend: 'right',
pointSize: 5,
crosshair: {
trigger: 'both'
},
hAxis: {
title: 'Time'
},
vAxis: {
title: 'Value'
}
};
break;
case 'Table':
options = {
showRowNumber: true,
width: '100%',
height: '100%'
};
break;
default:
options = {};
}
return options;
}
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
<input type="button" id="btnSwitch" />

Categories

Resources