I want to create a loop, in my google chart, i have 200 points in the chart, and it moves 1 point to the right per second,but i want to repeat the chart when it reach all points.
here is my code of the chart:
function drawChart5() {
var options = {
'backgroundColor': 'transparent',
width: 1200,
height: 240,
animation: {
duration: 1000,
easing: 'in',
},
hAxis: {viewWindow: {min:0, max:200}}
};
var chart = new google.visualization.AreaChart(
document.getElementById('visualization'));
var data = new google.visualization.DataTable();
data.addColumn('string', 'x');
data.addColumn('number', 'y');
var MAX = 100;
var x=0;
var f=20;
var T= 1/f;
var PI = Math.PI;
var DT=T/MAX;
for (var i = 0; i < 2*MAX; i++)
{
x=(Math.sin((2*PI)*f*i*DT));
data.addRow([i.toString(), x]);
console.log(x)
}
function drawChart() {
google.visualization.events.addListener(chart, 'ready',
function() {
});
chart.draw(data, options);
}
setInterval(function()
{
options.hAxis.viewWindow.min += 1;
options.hAxis.viewWindow.max += 1;
chart.draw(data,options)
},2 000);
drawChart();
}
This is the chart
I would achieve the effect you are going for like this:
Use a DataView instead of a DataTable, and use DataView.setColumns() to create a calculated column that runs the formula defined above. As far as I can tell, the algorithm you use to calculate your values is deterministic, so all you need to run your calculations is the x-value and you can determine the y-value for any given position.
With this method, you'll never have to populate a DataTable yourself, because the chart uses your function to calculate the y-value on demand. Whatever range your chart displays, it will calculate the values it needs when the chart is drawn.
Related
I have a page in an app that displays all sorts of ticket metrics from several different ticketing systems we use. I use the same function to build each of the charts and display them:
function drawChart(chartData, chartStyle, chartTitle, chartSpanID) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Hour');
data.addColumn('number', 'Tickets');
data.addRows(chartData);
if(chartStyle == "column"){
var chart = new google.visualization.ColumnChart(document.getElementById(chartSpanID));
var options = {title:chartTitle,chartArea: {left: 30, top:20, bottom:30, right:10},animation:{startup: true, duration:2000}}
};
if(chartStyle == "pie"){
var chart = new google.visualization.PieChart(document.getElementById(chartSpanID));
var options = {title:chartTitle,
chartArea:{left: 0, top:20, bottom:10, right: 0},
is3D: true,
sliceVisibilityThreshold: .01,
animation:{startup: true,easing: 'in', duration:1000},
pieResidueSliceLabel: "Other( < 1%)"};
};
chart.draw(data, options);
}
This works for 13 of the 14 charts displayed on the page. 8 Columns and 5 out of 6 pie charts all display perfectly. One of the pie charts will ONLY display if I draw the chart a second time.
function drawChart(chartData, chartStyle, chartTitle, chartSpanID) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Hour');
data.addColumn('number', 'Tickets');
data.addRows(chartData);
if(chartStyle == "column"){
var chart = new google.visualization.ColumnChart(document.getElementById(chartSpanID));
var options = {title:chartTitle,chartArea: {left: 30, top:20, bottom:30, right:10},animation:{startup: true, duration:2000}}
};
if(chartStyle == "pie"){
var chart = new google.visualization.PieChart(document.getElementById(chartSpanID));
var options = {title:chartTitle,
chartArea:{left: 0, top:20, bottom:10, right: 0},
is3D: true,
sliceVisibilityThreshold: .01,
animation:{startup: true,easing: 'in', duration:1000},
pieResidueSliceLabel: "Other( < 1%)"};
};
chart.draw(data, options);
chart.draw(data, options);
}
The data is delivered correctly, all calls are made through google.charts.setOnLoadCallback, it all works just fine once called again... so why is it only working on the second call? Why just the one chart that doesn't display as expected? What have I missed?
Google charts' line chart
So, at the moment, I'm just getting used to google charts. But in future, I'm looking to plot a function. Once that line is drawn, I'd like to add a dynamic indicator circle that will travel along the path of the line as I adjust the values that plotted the line.
So to summarise:
Plot a permanent line from a function*
Have a circle that travels the path of the line as I adjust the values of the function. (main question)
New to google charts and not sure how easily you can do something like this.
To maybe clarify: I will be using a slider to control a value, as I move the slider the line will not change, but an "indicator" circle will change position to fit the new values; i.e. plotting a circle dynamically as the value changes.
Not sure if it helps, but my current graph looks simply like this:
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
drawChart();
function drawChart() {
var data = google.visualization.arrayToDataTable([
['somVar1', 'someVar2'],
['0.001' , 0.02],
['0.003' , 0.07],
['0.01' , 0.2],
['0.03' , 0.6 ],
['0.1' , 1.8],
['0.3' , 4.8],
['1' , 10],
['3' , 15.2 ],
['10' , 18.2 ],
['30' , 19.4],
['100' , 19.8],
['300' , 19.93],
['1000' , 19.98],
]);
//Graph styling and legend
var options = {
title: 'sumVar2 (%)',
curveType: 'function',
legend: { position: 'bottom' },
vAxis: { title: 'someVar2 %'},
hAxis: { title: 'someVar1'}
};
var chart = new google.visualization.LineChart(document.getElementById('lineGraph'));
chart.draw(data, options);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="lineGraph" style="width: 900px; height: 500px"></div>
*(unfortunately with google charts, it looks like I have to do this by finding the range of values and spitting them out into an array - rather than being able to plot straight from a mathematical function)
the DataView Class can be used to provide a function as the value for a series
use the setColumns method to set the function
you can pass a column index for an existing DataTable column or
a custom object with the calculation function
here, a DataView is created from a DataTable,
it uses the first column from the DataTable,
the next column is a function
var dataView = new google.visualization.DataView(dataTable);
dataView.setColumns([0, {
calc: function (data, row) {
return (2 * data.getValue(row, 0)) + 7;
},
type: 'number',
label: 'Y1'
}]);
you can set multiple column functions,
but you cannot use the values from one function in another,
in the same DataView
to get around, reference the previous DataView in the current function
otherwise, you would have to dump the values to a new table,
then use the new table in another view to set the next function
you can modify the series options to create points rather than a line, i.e.
series: {
1: {
lineWidth: 0,
pointSize: 8
}
}
the following working snippet demonstrates how to save a reference to the first function, and use it later, such as when the chart's 'ready' event fires
google.charts.load('current', {
callback: function () {
// DataTable X only
var dataTable = google.visualization.arrayToDataTable([
['X'],
[0.001],
[0.003],
[0.01],
[0.03],
[0.1],
[0.3],
[1],
[3],
[10],
[30],
[100],
[300],
[1000],
]);
// use DataView to provide function for Y
var dataView = new google.visualization.DataView(dataTable);
// y1=2x+7
var yCol1 = {
calc: function (data, row) {
return (2 * data.getValue(row, 0)) + 7;
},
type: 'number',
label: 'Y1'
};
// use above object as Y1
dataView.setColumns([0, yCol1]);
var container = document.getElementById('chart_div');
var chart = new google.visualization.LineChart(container);
// draw Y2 when chart finishes drawing
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
// add Y2 column
dataView.setColumns([0, yCol1, {
// y2=y1+(2x-1)
calc: function (data, row) {
//use reference to previous dataView
return dataView.getValue(row, 1) + ((2 * data.getValue(row, 0)) - 1);
},
type: 'number',
label: 'Y2'
}]);
chart.draw(dataView, {
series: {
1: {
lineWidth: 0,
pointSize: 8
}
}
});
});
chart.draw(dataView);
},
packages: ['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
EDIT
the same concept can be used to avoid having an array for the X values as well
just need to set an initial number of rows on a DataTable
see following working snippet...
google.charts.load('current', {
callback: function () {
// create blank table for the view
var dataTable = new google.visualization.DataTable();
dataTable.addColumn('number', 'X');
dataTable.addRows(20);
// use DataView to provide function for X
var dataView = new google.visualization.DataView(dataTable);
var xCol = {
calc: function (data, row) {
return (row + 1) * 0.3;
},
type: 'number',
label: 'X'
};
dataView.setColumns([xCol]);
// function for Y --> y1=2x+7
var yCol1 = {
calc: function (data, row) {
return (2 * dataView.getValue(row, 0)) + 7;
},
type: 'number',
label: 'Y1'
};
dataView.setColumns([xCol, yCol1]);
var container = document.getElementById('chart_div');
var chart = new google.visualization.LineChart(container);
// draw Y2 when chart finishes drawing
google.visualization.events.addOneTimeListener(chart, 'ready', function () {
// add Y2 column
dataView.setColumns([xCol, yCol1, {
// y2=y1+(2x-1)
calc: function (data, row) {
//use reference to previous dataView
return dataView.getValue(row, 1) + ((2 * data.getValue(row, 0)) - 1);
},
type: 'number',
label: 'Y2'
}]);
chart.draw(dataView, {
series: {
1: {
lineWidth: 0,
pointSize: 8
}
}
});
});
chart.draw(dataView);
},
packages: ['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
I have an line/area chart, I want to set a minimum range on the y-axis.
Let's say my points are [0,300],[1,270],[2,230],[3,260] (those are retrieved through ajax, so they're not static).
I want the y-axis range to be at least 100, but by default google will set maximum as maximum value (300 in this case), and minimum at minimum value (230 in this case), so range in this case would be (and it is actually) 70, I want it to be at least 100, so the chart maximum should be (300+230)/2+50 and minimum (300+230)/2-50, so that I have a 100 range and the chart i vertically center aligned.
I want the range to have a minimum but not a maximum, if my points are [0,100],[1,240],[5,160] then range should match the data range (140 in this case) also if the optimum is smaller (100).
Basically I don't want the chart to show a big difference when the actual difference in data is small. I know how to set fixed maximum and minimum on axis, but that doesn't solve my problem.
This is my actual code:
$.fn.createChart = function(url,options){
var obj = $(this);
console.log('CREATING CHART: '+url);
// Load the Visualization API and the linechart package.
if(!$.canAccessGoogleVisualization()){
google.charts.load('current', {packages: ['corechart', 'line']});
}
// Set a callback to run when the Google Visualization API is loaded.
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var jsonData = $.ajax({
url: url ,
dataType: "json",
async: false
}).responseText;
// Create our data table out of JSON data loaded from server.
var data = new google.visualization.DataTable(jsonData);
//Default options
var def = {
width: obj.width(),
height: obj.height(),
curveType: 'function',
legend: { position: 'bottom' },
hAxis: {
format: 'dd/MM'
},
animation:{
"startup": true,
duration: 1000,
easing: 'out',
}
};
//Overwrite default options with passed options
var options = typeof options !== 'undefined' ? $.mergeObjects(def,options) : def;
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.AreaChart(obj.get(0));
chart.draw(data, options);
}
}
$.mergeObjects = function(obj1,obj2){
for (var attrname in obj2) { obj1[attrname] = obj2[attrname]; }
return obj1;
}
$.canAccessGoogleVisualization = function()
{
if ((typeof google === 'undefined') || (typeof google.visualization === 'undefined')) {
return false;
}
else{
return true;
}
}
you can use the getColumnRange method on the DataTable to find the min and max
then apply you're logic to set the viewWindow on the vAxis
see following working snippet...
google.charts.load('current', {
callback: function () {
var data = google.visualization.arrayToDataTable([
['X', 'Y'],
[0, 300],
[1, 270],
[2, 230],
[3, 260]
]);
var yMin;
var yMax;
var columnRange = data.getColumnRange(1);
if ((columnRange.max - columnRange.min) < 100) {
yMin = ((columnRange.max + columnRange.min) / 2) - 50;
yMax = ((columnRange.max + columnRange.min) / 2) + 50;
} else {
yMin = columnRange.min;
yMax = columnRange.max;
}
var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
chart.draw(data, {
vAxis: {
viewWindow: {
min: yMin,
max: yMax
}
}
});
},
packages: ['corechart']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
I'm using the google elevation API to get elevation data along a path on google maps which I am then plotting on a google visualization column chart.
My code to get the data is as follows:
function createUpdateProfile(){
var path=[];
for (var i = 0; i < markers.length; i++) {
path.push(markers[i].getPosition());
};
var elevator = new google.maps.ElevationService;
// Draw the path, using the Visualization API and the Elevation service.
displayPathElevation(path, elevator, map);
}
function displayPathElevation(path, elevator, map) {
elevator.getElevationAlongPath({
'path': path,
'samples': 256
}, plotElevation);
}
function plotElevation(elevations, status) {
var chartDiv = document.getElementById('elevation_chart');
if (status !== google.maps.ElevationStatus.OK) {
// Show the error code inside the chartDiv.
chartDiv.innerHTML = 'Cannot show elevation: request failed because ' +
status;
return;
}
// Create a new chart in the elevation_chart DIV.
var chart = new google.visualization.ColumnChart(chartDiv);
// Extract the data from which to populate the chart.
// Because the samples are equidistant, the 'Sample'
// column here does double duty as distance along the
// X axis.
var data = new google.visualization.DataTable();
data.addColumn('string', 'Sample');
data.addColumn('number', 'Elevation');
for (var i = 0; i < elevations.length; i++) {
data.addRow(['', elevations[i].elevation]);
}
// Draw the chart using the data within its DIV.
chart.draw(data, {
height: 200,
legend: 'none',
titleY: 'Elevation (m)'
});
The data returned is in Metric (Meters), how can I convert the returned elevation data to feet and then send that to the chart?
To convert meters to feet, multiply by 3.28084 feet/meter
for (var i = 0; i < elevations.length; i++) {
data.addRow(['', elevations[i].elevation*3.28084]);
}
I'm working with Nvd3 charts from the examples from their official website. Now I want a line chart to update periodically based on data sent from server but I couldn't found any useful Example for this on internet.
I have created a function which re-draws the chart when new data is arrived but i want to append every new point to the existing chart (like we can do in highcharts) but i'm stuck.
Here is the code I'm using for Updating the chart.
var data = [{
"key" : "Long",
"values" : getData()
}];
var chart;
function redraw() {
nv.addGraph(function() {
var chart = nv.models.lineChart().margin({
left : 100
})
//Adjust chart margins to give the x-axis some breathing room.
.useInteractiveGuideline(true) //We want nice looking tooltips and a guideline!
.transitionDuration(350) //how fast do you want the lines to transition?
.showLegend(true) //Show the legend, allowing users to turn on/off line series.
.showYAxis(true) //Show the y-axis
.showXAxis(true);
//Show the x-axis
chart.xAxis.tickFormat(function(d) {
return d3.time.format('%x')(new Date(d))
});
chart.yAxis.tickFormat(d3.format(',.1%'));
d3.select('#chart svg').datum(data)
//.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
}
function getData() {
var arr = [];
var theDate = new Date(2012, 01, 01, 0, 0, 0, 0);
for (var x = 0; x < 30; x++) {
arr.push({
x : new Date(theDate.getTime()),
y : Math.random() * 100
});
theDate.setDate(theDate.getDate() + 1);
}
return arr;
}
setInterval(function() {
var long = data[0].values;
var next = new Date(long[long.length - 1].x);
next.setDate(next.getDate() + 1)
long.shift();
long.push({
x : next.getTime(),
y : Math.random() * 100
});
redraw();
}, 1500);