Hello, I have a timeline made with google charts, what I want to do is update it every so often, for example every 5 seconds, I get the data from a json, I tried it this way but it didn't work for me:
setTimeout(function () {
drawChart();
}, 5000);
Is there any way to do it?
google.load("visualization", "1", {packages: ["timeline"]});
google.setOnLoadCallback(drawChart);
// google.charts.setOnLoadCallback(drawChart);
function drawChart() {
$(".timeline").each(function () {
var obje = {{ devicejson|safe }};
var elem = $(this),
id = elem.attr('id');
var container = document.getElementById(id);
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({type: 'string', id: 'Role'});
dataTable.addColumn({type: 'string', id: 'Name'});
dataTable.addColumn({type: 'date', id: 'Start'});
dataTable.addColumn({type: 'date', id: 'End'});
dataTable.addColumn({type: 'string', id: 'TimeEst'});
dataTable.addColumn({type: 'string', role: 'style'});
for (n = 0; n < obje.length; ++n) {
if (obje[n].device_id == id) {
dataTable.addRows([
['Department', obje[n].digitaloutput_user_description, new Date('"' + obje[n].startdatetime + '"'), new Date('"' + obje[n].enddatetime + '"'), obje[n].lighstate_user_description, obje[n].color],
]);
}
}
for (n = 0; n < obje.length; ++n) {
if (obje[n].device_id == id) {
console.log(obje[n].color)
}
}
var options = {
chartArea: {
height: '90%',
width: '100%',
top: 36,
right: 12,
bottom: 2,
left: 12
},
height: 100,
tooltip: {isHtml: true},
timeline: {
showRowLabels: false,
},
avoidOverlappingGridLines: false,
{#hAxis: {format: 'dd-MMM-yyyy HH:mm:ss'}#}
};
var formatTime = new google.visualization.DateFormat({
pattern: 'yyyy-MM-dd HH:mm:ss a'
});
var view = new google.visualization.DataView(dataTable);
view.setColumns([0, 1, {
role: 'tooltip',
type: 'string',
calc: function (dt, row) {
// build tooltip
var dateBegin = dt.getValue(row, 2);
var dateEnd = dt.getValue(row, 3);
var oneHour = (60 * 1000);
var duration = (dateEnd.getTime() - dateBegin.getTime()) / oneHour;
var tooltip = '<div><div class="ggl-tooltip"><span>';
tooltip += dt.getValue(row, 0) + ':</span> ' + dt.getValue(row, 1) + '</div>';
tooltip += '<div class="ggl-tooltip"><div>' + formatTime.formatValue(dateBegin) + ' - ';
tooltip += formatTime.formatValue(dateEnd) + '</div>';
tooltip += '<div><span>Duration: </span>' + duration.toFixed(0) + ' minutes</div>';
tooltip += '<div><span>Estate: </span>' + dt.getValue(row, 5) + '</div></div>';
return tooltip;
},
p: {html: true}
}, 2, 3]);
google.visualization.events.addListener(chart, 'ready', function () {
var labels = container.getElementsByTagName('text');
Array.prototype.forEach.call(labels, function (label) {
label.setAttribute('font-weight', 'normal');
});
});
chart.draw(view.toDataTable(), options);
})
setTimeout(function () {
drawChart();
}, 5000);
}
for starters, the old version of google charts should no longer be used.
replace the old library...
<script src="https://www.google.com/jsapi"></script>
with the new library...
<script src="https://www.gstatic.com/charts/loader.js"></script>
this will only change the load statement.
which can be found in the following snippet...
next, it appears you are loading the data from the server, here...
var obje = {{ devicejson|safe }};
JavaScript runs on the client.
so no matter how many times drawChart is called,
it will always use the same data received from the server on the first page load.
instead, try this...
create two pages, one that returns the data, and the other that draws the chart.
then you can use AJAX to get the data and draw the chart as many times as needed.
create a new page, and all you really need in the new page is the following line of code.
{{ devicejson|safe }}
aside from any header information, and existing code you're using to get the data to the current page.
but you don't want to assign it to a variable in the new page.
just write the data to the page using the above statement.
then you can use AJAX to get the data and draw the chart.
function getData() {
$.ajax({
url: 'get_data.url', // <-- use url to new page here
dataType: 'JSON'
}).done(function (data) {
drawChart(data);
});
}
then, wait for the chart's ready event, before calling setTimeout.
because we don't know how long it will take to get the data and draw the chart.
so let's wait for the first chart to finish, before trying again.
see following snippet...
google.charts.load('current', {
packages:['timeline']
}).then(getData); // <-- load google charts, then get the data
function getData() {
$.ajax({
url: 'get_data.url', // <-- use url to new page here
dataType: 'JSON'
}).done(function (data) {
drawChart(data); // <-- draw chart with data from new page
});
}
function drawChart(obje) {
$(".timeline").each(function () {
var elem = $(this),
id = elem.attr('id');
var container = document.getElementById(id);
var chart = new google.visualization.Timeline(container);
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({type: 'string', id: 'Role'});
dataTable.addColumn({type: 'string', id: 'Name'});
dataTable.addColumn({type: 'date', id: 'Start'});
dataTable.addColumn({type: 'date', id: 'End'});
dataTable.addColumn({type: 'string', id: 'TimeEst'});
dataTable.addColumn({type: 'string', role: 'style'});
for (n = 0; n < obje.length; ++n) {
if (obje[n].device_id == id) {
dataTable.addRows([
['Department', obje[n].digitaloutput_user_description, new Date('"' + obje[n].startdatetime + '"'), new Date('"' + obje[n].enddatetime + '"'), obje[n].lighstate_user_description, obje[n].color],
]);
}
}
for (n = 0; n < obje.length; ++n) {
if (obje[n].device_id == id) {
console.log(obje[n].color)
}
}
var options = {
chartArea: {
height: '90%',
width: '100%',
top: 36,
right: 12,
bottom: 2,
left: 12
},
height: 100,
tooltip: {isHtml: true},
timeline: {
showRowLabels: false,
},
avoidOverlappingGridLines: false,
{#hAxis: {format: 'dd-MMM-yyyy HH:mm:ss'}#}
};
var formatTime = new google.visualization.DateFormat({
pattern: 'yyyy-MM-dd HH:mm:ss a'
});
var view = new google.visualization.DataView(dataTable);
view.setColumns([0, 1, {
role: 'tooltip',
type: 'string',
calc: function (dt, row) {
// build tooltip
var dateBegin = dt.getValue(row, 2);
var dateEnd = dt.getValue(row, 3);
var oneHour = (60 * 1000);
var duration = (dateEnd.getTime() - dateBegin.getTime()) / oneHour;
var tooltip = '<div><div class="ggl-tooltip"><span>';
tooltip += dt.getValue(row, 0) + ':</span> ' + dt.getValue(row, 1) + '</div>';
tooltip += '<div class="ggl-tooltip"><div>' + formatTime.formatValue(dateBegin) + ' - ';
tooltip += formatTime.formatValue(dateEnd) + '</div>';
tooltip += '<div><span>Duration: </span>' + duration.toFixed(0) + ' minutes</div>';
tooltip += '<div><span>Estate: </span>' + dt.getValue(row, 5) + '</div></div>';
return tooltip;
},
p: {html: true}
}, 2, 3]);
google.visualization.events.addListener(chart, 'ready', function () {
var labels = container.getElementsByTagName('text');
Array.prototype.forEach.call(labels, function (label) {
label.setAttribute('font-weight', 'normal');
});
setTimeout(getData, 5000); // <-- get data and draw again in 5 seconds
});
chart.draw(view.toDataTable(), options);
});
}
Related
I want to use anchorBgColor attribute for Real-time Line chart.
Real-time Line chart.
function updateData() {
var t = new Date(),
date =
t.getHours() + ":" + t.getMinutes() + ":" + t.getSeconds(),
val = Math.floor(Math.random() * (7800 - 7200 + 1)) + 7200,
strData = "&label=" + date + "&value=" + val;
// Feed it to chart.
chartRef.feedData(strData);
}
Could you recommend how to change anchorBgColor for this chart?
If you are wanting to have all the points the same color all you have to do is include the anchorBgColor property in the chart object
{
...
dataSource: {
chart: {
...
anchorBgColor: "#26aa5a"
}
}
}
If you want points to change colors as you add them you have manipulate the chart data object and use setJSONData rather than using the feedData method.
<div id="chart-container">FusionCharts will render here</div>
FusionCharts.ready(function() {
var chartObj = new FusionCharts({
type: 'line',
renderAt: 'chart-container',
id: 'myChart',
width: '500',
height: '300',
dataFormat: 'json',
dataSource: {
"chart": {
"theme": "fusion",
"anchorRadius": "6",
"theme": "fusion"
},
"data": []
}
});
chartObj.render();
function pushNewPoint() {
var t = new Date(),
date =
t.getHours() + ":" + t.getMinutes() + ":" + t.getSeconds(),
val = Math.floor(Math.random() * (7800 - 7200 + 1)) + 7200,
randomColor = Math.floor(Math.random()*16777215).toString(16)
newEntry = {
label: date,
value: val,
anchorBgColor: "#" + randomColor
}
chartData = chartObj.getChartData('json')
chartData.data.push(newEntry)
chartObj.setJSONData(chartData)
}
var counter = 0;
var i = setInterval(function(){
pushNewPoint()
counter++;
if(counter === 10) {
clearInterval(i);
}
}, 1000);
});
Example can be seen here
I have the following code, in which I iterate through a list of items and create dynamic div elements. Then, at the end of each loop, I want to create a barchart using google visualisations and append it to the divs created. However, I receive the following error.
Container is not defined
I am new to google visualisations. I wonder if this is possible. Below is my code:
function drawBarChart() {
PAGES = _.keys(_.countBy(perct_by_page_COLL_vs_NCOLL, function(data) { return data.PAGE_SHORT; }));
$.each(PAGES, function(index, item){
$("#BLOQUES").append("<div class='" + item + "'></div>")
page_sub = $.grep(perct_by_page_COLL_vs_NCOLL, function(n, i) {return n.PAGE_SHORT === item;});
container = $("#BLOQUES").find('.' + item)
container.append('<div>' + page_sub[0].PAGE_TITLE + '</div>')
container.append("<div class='barchart'></div>")
x = page_sub[0]
a = x['PAGE_SHORT']
b = x['PERC_ALL']
c = x['PERC_NCOLL']
d = x['PERC_ALL']
var data = google.visualization.arrayToDataTable([
['PAGE_SHORT', 'PERC_ALL', 'PERC_COLL', 'PERC_NCOLL'], [a, b, c, d]
]);
var options = {
title: '',
chartArea: {width: '50%'},
hAxis: {
title: 'Total Population',
minValue: 0
},
vAxis: {
title: 'City'
}
};
var chart = new google.visualization.BarChart(container.find('.barchart'));
chart.draw(data, options);
});
}
the chart is expecting a specific dom element
not a jquery reference, which is basically an array of elements
recommend using the loop index to assign an id to the chart element
container.append("<div id='chart" + index + "' class='barchart'></div>")
then using the id to create the chart
var chart = new google.visualization.BarChart($('#chart' + index)[0]);
full snippet...
function drawBarChart() {
PAGES = _.keys(_.countBy(perct_by_page_COLL_vs_NCOLL, function(data) { return data.PAGE_SHORT; }));
$.each(PAGES, function(index, item){
$("#BLOQUES").append("<div class='" + item + "'></div>")
page_sub = $.grep(perct_by_page_COLL_vs_NCOLL, function(n, i) {return n.PAGE_SHORT === item;});
container = $("#BLOQUES").find('.' + item)
container.append('<div>' + page_sub[0].PAGE_TITLE + '</div>')
container.append("<div id='chart" + index + "' class='barchart'></div>")
x = page_sub[0]
a = x['PAGE_SHORT']
b = x['PERC_ALL']
c = x['PERC_NCOLL']
d = x['PERC_ALL']
var data = google.visualization.arrayToDataTable([
['PAGE_SHORT', 'PERC_ALL', 'PERC_COLL', 'PERC_NCOLL'], [a, b, c, d]
]);
var options = {
title: '',
chartArea: {width: '50%'},
hAxis: {
title: 'Total Population',
minValue: 0
},
vAxis: {
title: 'City'
}
};
var chart = new google.visualization.BarChart($('#chart' + index)[0]);
chart.draw(data, options);
});
}
I'm trying to edit the tooltip in a c3 line-chart. Specifically, I need to access the current x-value within the chart.tooltip.format.value function. However, the function is not passed the x-value explicitly.
var chart = c3.generate({
tooltip: {
format: {
value: function (value, ratio, id, index) {
return value;
}
}
},
data: {
x: 'YEAR',
xFormat: '%Y',
url: myURL',
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y'
}
},
},
});
You can use the tooltip's contents property to create a custom tooltip, and in there you can access the X value via: d[0].x.
Edit: use d[0].x.getFullYear() to retrieve only the year part of the date (it's a time series so C3 internally stores the supplied year as a javascript date object)
Here's code I've taken from this discussion https://github.com/c3js/c3/issues/444, and modified:
function tooltip_contents(d, defaultTitleFormat, defaultValueFormat, color) {
var $$ = this, config = $$.config, CLASS = $$.CLASS,
titleFormat = config.tooltip_format_title || defaultTitleFormat,
nameFormat = config.tooltip_format_name || function (name) { return name; },
valueFormat = config.tooltip_format_value || defaultValueFormat,
text, i, title, value, name, bgcolor;
// You can access all of data like this:
//console.log($$.data.targets);
for (i = 0; i < d.length; i++) {
if (! (d[i] && (d[i].value || d[i].value === 0))) { continue; }
// to exclude
//if (d[i].name === 'data2') { continue; }
if (! text) {
title = 'MY TOOLTIP # ' + d[0].x.getFullYear(); // SHOW X-VALUE, year only (given it is a time series)
text = "<table class='" + CLASS.tooltip + "'>" + (title || title === 0 ? "<tr><th colspan='2'>" + title + "</th></tr>" : "");
}
name = nameFormat(d[i].name);
value = valueFormat(d[i].value, d[i].ratio, d[i].id, d[i].index);
bgcolor = $$.levelColor ? $$.levelColor(d[i].value) : color(d[i].id);
text += "<tr class='" + CLASS.tooltipName + "-" + d[i].id + "'>";
text += "<td class='name'><span style='background-color:" + bgcolor + "'></span>" + name + "</td>";
text += "<td class='value'>" + value + "</td>";
text += "</tr>";
}
return text + "</table>";
}
var chart = c3.generate({
data: {
x: 'year',
xFormat: '%Y',
columns: [
['year', '1970', '1975', '1980', '1985', '1990'],
['data1', 100, 200, 150, 300, 200],
['data2', 400, 500, 250, 700, 300],
]
},
axis: {
x: {
type: 'timeseries',
tick: {
format: '%Y'
}
},
},
tooltip: {
contents: tooltip_contents
}
});
My fiddle, showing the current x-value: http://jsfiddle.net/w7h385h3/5/
I am using google visualization table to display some data in a table. And I add a listener to the table to track the user click event.
some codes like this:
// Add our selection handler.
google.visualization.events.addListener(table, 'select', selectHandler);
// The selection handler.
// Loop through all items in the selection and concatenate
// a single message from all of them.
function selectHandler() {
var selection = table.getSelection();
var message = '';
for (var i = 0; i < selection.length; i++) {
var item = selection[i];
if (item.row != null && item.column != null) {
var str = data.getFormattedValue(item.row, item.column);
message += '{row:' + item.row + ',column:' + item.column + '} = ' + str + '\n';
} else if (item.row != null) {
var str = data.getFormattedValue(item.row, 0);
message += '{row:' + item.row + ', column:none}; value (col 0) = ' + str + '\n';
} else if (item.column != null) {
var str = data.getFormattedValue(0, item.column);
message += '{row:none, column:' + item.column + '}; value (row 0) = ' + str + '\n';
}
}
if (message == '') {
message = 'nothing';
}
alert('You selected ' + message);
}
The problem is that, It seems that the listener can only detect which row it is clicked, but have no idea about the column information. Is someone know how to access a particular cell in google visualization table by user click? Or some other js library which are more powerful I can use?
There is no API-based access to the column-index, you must use the DOM.
Add a click-listener to the <table> and when it fires detect the column-index by using the index of the <td> in the childNodes of the parent <tr>
Simple example:
google.setOnLoadCallback(drawTable);
function drawTable() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Name');
data.addColumn('number', 'Salary');
data.addColumn('boolean', 'Full Time Employee');
data.addRows([
['Mike', {
v: 10000,
f: '$10,000'
},
true
],
['Jim', {
v: 8000,
f: '$8,000'
},
false
],
['Alice', {
v: 12500,
f: '$12,500'
},
true
],
['Bob', {
v: 7000,
f: '$7,000'
},
true
]
]);
var table = new google.visualization.Table(document.getElementById('table_div'));
google.visualization.events.addListener(table, 'ready', function() {
document.querySelector('#table_div tbody').addEventListener('click', function(e) {
var cell = e.srcElement || e.target,
column = null;
if (table.getSelection().length && cell !== cell.parentNode.firstChild) {
for (var i = 0; i < cell.parentNode.childNodes.length; ++i) {
if (cell.parentNode.childNodes[i] === cell) {
column = i - 1;
break;
}
}
if (column !== null) {
var msg = ['column-index is ' + column];
var selection = table.getSelection();
//if current row has been selected
if (/\bgoogle-visualization-table-tr-sel\b/.test(cell.parentNode.className)) {
msg.push('row-index is ' + selection[selection.length - 1].row);
msg.push('value of clicked cell is:' + data.getValue(selection[selection.length - 1].row, column));
} else {
msg.push('current row is not selected');
}
alert(msg.join('\n---------\n'))
}
} else {
alert('no row selected');
}
});
});
table.draw(data, {
showRowNumber: true
});
}
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1.1','packages':['table']}]}"></script>
<div id="table_div"></div>
For google.visualization.table you could consider the following approach to access column property (from table cell):
google.load('visualization', '1', {
packages: ['table']
});
google.setOnLoadCallback(drawTable);
function drawTable() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Name');
data.addColumn('number', 'Salary');
data.addColumn('boolean', 'Full Time Employee');
data.addRows([
['Mike', {
v: 10000,
f: '$10,000'
},
true
],
['Jim', {
v: 8000,
f: '$8,000'
},
false
],
['Alice', {
v: 12500,
f: '$12,500'
},
true
],
['Bob', {
v: 7000,
f: '$7,000'
},
true
]
]);
var table = new google.visualization.Table(document.getElementById('table_div'));
google.visualization.events.addListener(table, 'select', function(){
selectHandler(table);
});
table.draw(data, {
showRowNumber: false
});
}
function selectHandler(table) {
var selection = table.getSelection();
if(selection.length === 0)
return;
var cell = event.target; //get selected cell
document.getElementById('output').innerHTML = "Row: " + selection[0].row + " Column: " + cell.cellIndex;
}
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<div id="table_div"></div>
<div id="output"></div>
I've combined a jquery datepicker widget with jqplot, and am using the values of the datepicker to recreate the plot when something changes.
However, during large date ranges, the labels on the x-axis run together to look very ugly.
Here's it regularly:
Here's it with a large range:
My question is -- is it possible to change the rendering of the axis, so if there is a large range that not every single date shows? Maybe 2 or 3, but always including the beginning date and end date.
Here's my code -- please let me know if you can help!
$(document).ready(function(){
var ajaxDataRenderer = function(url, plot, options) {
var ret = null;
$.ajax({
async: false,
url: url,
type: "GET",
dataType:"json",
data: {metricName: ""},
success: function(data) {
ret = data;
},
error:function (xhr, ajaxOptions, thrownError){
alert(xhr.responseText);
}
});
return ret;
};
//var jsonurl = "reports/reportData.json";
var jsonurl = "tenant/metrics/get.json";
var currentTime = new Date()
var month = currentTime.getMonth() + 1;
var day = currentTime.getDate();
var year = currentTime.getFullYear();
var today = month + "-" + day + "-" + year;
var currentDatePlus = new Date(new Date().getTime() + 24 * 60 * 60 * 1000);
var dayPlus = currentDatePlus.getDate()
var monthPlus = currentDatePlus.getMonth() + 1
var yearPlus = currentDatePlus.getFullYear()
var tomorrow = monthPlus + "/" + dayPlus + "/" + yearPlus;
function getLastWeek(){
var today = new Date();
var lastWeek = new Date(today.getFullYear(), today.getMonth(), today.getDate() - 7);
return lastWeek ;
}
var lastWeek = getLastWeek();
var lastWeekMonth = lastWeek.getMonth() + 1;
var lastWeekDay = lastWeek.getDate();
var lastWeekYear = lastWeek.getFullYear();
var lastWeekDisplay = lastWeekMonth + "/" + lastWeekDay + "/" + lastWeekYear;
var datepickerBegin = $("#datepicker_start").val();
var datepickerEnd = $("#datepicker_to").val();
$('#applyBtn').click( function() {
// Check to make sure the datepicker isn't empty
if ($("#datepicker_start").val() !== "" && $("#datepicker_to").val() !== "") {
var datepickerBegin = $("#datepicker_start").val();
var datepickerEnd = $("#datepicker_to").val();
//Recreate the plot
var plot2 = $.jqplot('chart2', jsonurl,{
title: "",
dataRenderer: ajaxDataRenderer,
dataRendererOptions: {unusedOptionalUrl: jsonurl},
axes: {
xaxis: {
//'numberTicks' : 7,
min: datepickerBegin,
max: datepickerEnd,
renderer:$.jqplot.DateAxisRenderer,
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
tickInterval: '1 day',
tickOptions:{formatString:'%#m/%#d/%Y'
}
//rendererOptions: {sdaTickInterval: [1, 'month']}
},
yaxis: {
label: "MB",
tickOptions:{formatString:'%d '},
// Comment the next line out to allow negative values (and therefore rounded ones)
min: 0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
}
});
//redraw the plot now.
//plot2.reInitialize({});
plot2.replot({});
}
})
// The default plot
var plot2 = $.jqplot('chart2', jsonurl,{
title: "",
dataRenderer: ajaxDataRenderer,
dataRendererOptions: {unusedOptionalUrl: jsonurl},
axes: {
xaxis: {
//'numberTicks' : 7,
min: lastWeekDisplay,
max: tomorrow,
renderer:$.jqplot.DateAxisRenderer,
rendererOptions:{tickRenderer:$.jqplot.CanvasAxisTickRenderer},
tickInterval: '1 day',
tickOptions:{formatString:'%#m/%#d/%Y'
}
//rendererOptions: {sdaTickInterval: [1, 'month']}
},
yaxis: {
label: "MB",
tickOptions:{formatString:'%d '},
// Comment the next line out to allow negative values (and therefore rounded ones)
min: 0
}
},
highlighter: {
show: true,
sizeAdjust: 7.5
}
});
});
Okay - I've got a better grasp on this issue.
I've limited the tick number using the 'numberTicks' property, so it has a certain number if the date range is over a specificed value.
The only issue left is that, with numberTicks, the intersections aren't always correct (meaning that on the x-axis 10/05/12, the value isn't always placed there when numberTicks is limited.