Basically my JavaScript receives data like this: year = {month1, ...., month12}. Each month stores data for 3 graphs. month1 = {graph1_data,..., graph3_data}
I wish to draw charts for all of them using Google Charts. So that makes it 12 * 3 = 36 charts. I have written the following code logic. But it fails to work.
for ( month in year )
{
google.charts.setOnLoadCallback(function(){
// draw graph1
var data = new google.visualization.DataTable();
// code
// code
// data.addColumn()
// data.addColumn()
data.addRows(data_table);
var options = {title: month, height:100};
var chart = new google.visualization.ColumnChart(
document.querySelector('#'+month+' .graph1'));
chart.draw(data, options);
});
google.charts.setOnLoadCallback(function(){
// draw graph2
google.charts.setOnLoadCallback(function(){
var data = new google.visualization.DataTable();
// code
// code
// data.addColumn()
// data.addColumn()
data.addRows(data_table);
var options = {title: month, height:100};
var chart = new google.visualization.ColumnChart(
document.querySelector('#'+month+' .graph2'));
chart.draw(data, options);
});
google.charts.setOnLoadCallback(function(){
// draw graph3
google.charts.setOnLoadCallback(function(){
var data = new google.visualization.DataTable();
// code
// code
// data.addColumn()
// data.addColumn()
data.addRows(data_table);
var options = {title: month, height:100};
var chart = new google.visualization.ColumnChart(
document.querySelector('#'+month+' .graph3));
chart.draw(data, options);
});
}
It draws 3 charts for month12. Now why does this happen? Does this mean I must call setOnLoadCallback 36 times? Isn't there any alternative way?
Edit
It looks like once Google Chart API is loaded, the callbacks in all the setOnLoadCallbacks is called once. Which probably explains why I get only one graph, since at the time of execution of the function, the loop would have stopped at month12.
So I finally figured out what is going wrong here.
You are only registering a callback with the Google Charts API, not actually calling it in your for(month in year) loop. The callback is called (as you can guess) when relevant resources have finished loading and the Charts API can start drawing. Unfortunately your globally defined var month is now equal to month12. This is the state after your for loop completes:
month = year.month12;
callback_for_graph_1 = <anonymous function 1>
callback_for_graph_2 = <anonymous function 2>
callback_for_graph_3 = <anonymous function 3>
As you can imagine, now when the callbacks, they are each called only once, with month = year.month12. Hence the three graphs, all with month12 data.
So how do you fix this? Here's how:
Initialise three callbacks only once. No need to keep them anonymous.
google.charts.setOnLoadCallback(callbackForGraph1);
google.charts.setOnLoadCallback(callbackForGraph2);
google.charts.setOnLoadCallback(callbackForGraph3);
function callbackForGraph1 {
// do your thing
}
function callbackForGraph2 {
// do your thing
}
function callbackForGraph3 {
// do your thing
}
Iterate over all the months inside each callback, and create a new Chart instance for each month. Also draw it.
function callbackForGraph1() {
for(month in year) {
// draw graph1
var data = new google.visualization.DataTable();
// code
// code
// data.addColumn()
// data.addColumn()
data.addRows(data_table);
var options = {title: month, height:100};
// this is your Chart instance ->
var chart = new google.visualization.ColumnChart(document.querySelector('#'+month+' .graph1'));
// draw it!
chart.draw(data, options);
}
}
Profit?
You might also want to store each Chart instance in a array or dictionary style object. That way you can later make changes to your plots, instead of having to regenerate and replace them.
OLD ANSWER: This happens because you draw the chart for graph_1 in the same HTML element for each month. So the chart for graph_1[month2] overwrites the chart for graph_1[month1]. Since graph_1[month12] is the last data to be processed, you get the chart for month12. Likewise occurs for graph_2 and graph_3.
What you can try is appending a new svg or vml element to your HTML document every time the plotting callback is called. This can be done from within the callback itself. That way you get a new chart for every month for every graph.
Related
Please I need help here. I am trying to generate a time-series NDVI chart using the code below, but I keep getting an error that reads "image.normalizedDifference is not a function"
I need help with this please. I am new to the GEE platform.
var finni = ee.FeatureCollection("users/Time-series-NDVI/Finney_shapefile"),
landsat7 = ee.ImageCollection("LANDSAT/LE07/C02/T1_RT");
//define start and end dates
var startDate = '2001-01-01'
var endDate = '2020-12-31'
// filter images to cover only the dates required
var images = landsat7.filter(ee.Filter.date(startDate, endDate));
//print(images);
//crop images to my study area
var imgextent = images.filter(ee.Filter.bounds(finni));
print(imgextent);
// remove cloud cover
var image = imgextent.filter(ee.Filter.eq('CLOUD_COVER', 0));
print(image);
// Select on Red and NIR bands from the collection
var image = image.select(['B4', 'B3'])
print(image)
// Compute the Normalized Difference Vegetation Index (NDVI).
var addNDVI = function(image){
var ndvi = image.normalizedDifference(['B4', 'B3']).rename('NDVI');
return image.addBands(ndvi);
};
//Add the calculated NDVI band to my "image" already containing Red and NIR band
var withNDVI = image.map(addNDVI);
//Export a chart of only NDVI layer
var chart = ui.Chart.image.series({
imageCollection: withNDVI.select('NDVI'),
region: finni,
reducer: ee.Reducer.mean(),
scale: 30
}).setOptions({title: 'NDVI over time'});
// Display the chart in the console.
print(chart);
Use the addBands function with .clip() instead of the select function.
Use this line return image.addBands(ndvi).clip(finni); instead of this "withNDVI.select('NDVI').clip(finni);"
That should work.
I am trying to push data points to a data series array for HighCharts. I have started with the standard pie template. I have then added additional $.get statements to calculate the number of lines in the files.
The alert statement on each additional $.get returns the correct number of lines, but the data point is not pushed to the series.
Can anyone help me, I am new to JQuery. Thanks.
$.get('piechart.csv', function(data) {
// Split the lines
var buttons
var lines = data.split('\n');
var series = {
data: []
};
// Iterate over the lines and add categories or series
$.each(lines, function(lineNo,line) {
var items = line.split(',');
series.data.push({
name: items[0],
y:parseFloat(items[1])
});
});
$.get('button0.txt', function(data) {
// Split the lines
var lines = data.split('\n');
series.data.push({
name: 'Power off',
y: (lines.length-1)
});
alert (lines.length-1);
});
$.get('button1.txt', function(data) {
// Split the lines
var lines = data.split('\n');
series.data.push({
name: 'Power on',
y: (lines.length-1)
});
alert (lines.length-1);
});
options.series.push(series);
// Create the chart
var chart = new Highcharts.Chart(options);
});
The data point is, I would assume, being pushed to the series, but the problem lies in the fact that this doesn't happen until after you have already created and rendered your chart instance.
Leaving aside the fact that I don't see a declaration of the options variable in your code, which would suggest that there might be another bug lurking (but I'm assuming that's just a typo?), the problem lies in the fact that HighCharts doesn't do anything to track the initial series that you pass it, so modifying that series isn't going to affect the chart.
You'll need to update the chart yourself - using, for example, the addPoint method of the HighCharts series object. So, in the success callback for your Ajax calls, you'll need to do something along the lines of:
$.get('button0.txt', function(data) {
// Split the lines
var lines = data.split('\n'),
chart = $('#container').highcharts();
chart.series[0].addPoint({
name: 'Power off',
y: lines.length-1
});
});
I'm trying to draw an area chart using dc.js, and the end date (i.e. far right) of the chart is based on the current date, not the last date in the dataset. In cases where there's a date gap between data points, I want the area to extend from one point to the next, not draw at 0.
Given this data:
var data = [
{domain: "foo.com", project: "pdp", repo: "myrepo", commit_date: "6/1/2014", lines_added: 100, lines_deleted: 50},
{domain: "foo.com", project: "pdp", repo: "myrepo", commit_date: "7/1/2014", lines_added: 100, lines_deleted: 50}
];
var ndx = crossfilter(data);
The chart's line/area currently ends at the "7/1/2014" data point, but I want it to stretch the entire length of the chart.
The relevant code for drawing the chart is:
var dateDim = ndx.dimension(function(d) {return d.commit_date;});
var minDate = dateDim.bottom(1)[0].commit_date;
var maxDate = new Date();
var domainGroup = dateDim.group().reduceSum(function(d) {return d.cumulative_lines;});
unshippedlineChart
.width(500).height(200)
.dimension(dateDim)
.group(domainGroup)
.renderArea(true)
.x(d3.time.scale().domain([minDate,maxDate]))
.brushOn(false)
.interpolate('step-after')
.yAxisLabel("Unshipped Value");
Full example is at http://jsfiddle.net/xayhkcvn/1/
You didn't actually ask a question :-), but I think you may be looking for ways to prefilter your data so that it gets extended to today, and to remove any zeros.
This stuff isn't built into dc.js, but there is some example code in the FAQ which may help. Specifically, there is a function remove_empty_bins which adapts a group to remove any zeros.
You could similarly define a function to add a final point (untested):
function duplicate_final_bin(source_group, key) {
return {
all:function () {
var ret = Array.prototype.slice.call(source_group.all()); // copy array
if(!ret.length) return ret;
ret.push({key: key, value: ret[ret.length-1].value});
return ret;
}
};
}
You can compose this with remove_empty_bins:
var super_group = duplicate_final_bin(remove_empty_bins(domainGroup), maxDate);
The idea is to create a wrapper object which dynamically adds or remove stuff from the (always changing) source_group.all() on demand. dc.js will call group.all() whenever it is redrawing, and these wrappers intercept that call and adapt the data the crossfilter group returns.
I am using Highcharts and I would like this chart to update each second. This is what I have now: JSFiddle
I have timer window.setInterval(updateChart, 1000); and it works properly actualizing data each second.
But I have no idea how to actualize the view. It is important that I don't want to draw chart again and again each second. I only want to shift points and add new ones. Do anyone know how to do that?
Look at the the series.addPoint method.
Your updateChart function becomes:
function updateChart()
{
for (var source = 1; source <=3; source++)
{
var point = [
23,
Math.floor((Math.random() * 10*source) + 5+source*2),
source
];
Highcharts.charts[0].series[0].addPoint(point, false, true); // add the point, don't redraw and shift off a point
}
Highcharts.charts[0].redraw(); // 3 points added, now redraw
}
Update fiddle.
I have a csv file that looks like that:
week,value1,value2
1,2,3
2,7,9
I would like to plot a stacked graph of it using google chart (week being my x (horizontal) values and values1 and values2 being the two set of y's). Unfortunately, I didn't find any easy way to do so. That probably relates to me being a complete noob in js.
Is there any simple way to do that?
The jquery-csv library provides the ability to translate a string of csv into an array to be used by google.visualization.arrayToDataTable() (their example here). To make this work, add jquery.csv.js to your server (in the example below I assume it is in the same folder as your HTML) and link to it in your <head>. The following is a simple script you can add to your <head> to get started. I assume a scatter chart, but this process works for any of the charts here. You will also need a <div> with id="chart" for this to work.
// load the visualization library from Google and set a listener
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
// this has to be a global function
function drawChart() {
// grab the CSV
$.get("example.csv", function(csvString) {
// transform the CSV string into a 2-dimensional array
var arrayData = $.csv.toArrays(csvString, {onParseValue: $.csv.hooks.castToScalar});
// this new DataTable object holds all the data
var data = new google.visualization.arrayToDataTable(arrayData);
// this view can select a subset of the data at a time
var view = new google.visualization.DataView(data);
view.setColumns([0,1]);
// set chart options
var options = {
title: "A Chart from a CSV!",
hAxis: {title: data.getColumnLabel(0), minValue: data.getColumnRange(0).min, maxValue: data.getColumnRange(0).max},
vAxis: {title: data.getColumnLabel(1), minValue: data.getColumnRange(1).min, maxValue: data.getColumnRange(1).max},
legend: 'none'
};
// create the chart object and draw it
var chart = new google.visualization.ScatterChart(document.getElementById('chart'));
chart.draw(view, options);
});
}
I have been searching for a while, and found the solution on a Google group discussion.
https://groups.google.com/forum/#!topic/google-visualization-api/cnXYDr411tQ
I have tried it, and it works!
In this case, we have to specify the header types of our csv file.
var queryOptions = {
csvColumns: ['number', 'number', 'number' /* Or whatever the columns in the CSV file are */],
csvHasHeader: true /* This should be false if your CSV file doesn't have a header */
}
/* csvUrl is the path to your csv */
var query = new google.visualization.Query(csvUrl, queryOptions);
query.send(handleQueryResponse);
function handleQueryResponse(response) {
if (response.isError()) {
alert('Error in query: ' + response.getMessage() + ' ' + response.getDetailedMessage());
return;
}
var data = response.getDataTable();
var chart = new google.visualization.ColumnChart(document.getElementById('your div'));
// Draw your chart with the data table here.
// chart.draw(view, queryOptions);
}
What server side scripting language are you working in (php, asp)?
One option could be to import the data from a spreadsheet saved in Google Drive, see here for a PHP based example of saving and extracting data from Google Docs. This would then enable you to update the spreadsheet and the chart would automatically plot the new data.