Create date histogram chart with google histogram - javascript

How to create Google Histogram Chart [1] that works with dates?
I've placed sample code (with working number and non-working date examples): http://jsfiddle.net/Qquse/417/ and code below [2]
[1] https://developers.google.com/chart/interactive/docs/gallery/histogram
[2]
google.load("visualization", "1", {
packages: ["corechart"]
});
google.setOnLoadCallback(drawChart);
function str_to_utcdate(d) {
return new Date(d.substr(0, 4), d.substr(5, 2) - 1, d.substr(9, 2));
}
function drawChart() {
var data = google.visualization.arrayToDataTable([
['item', 'date'],
['a', str_to_utcdate('2001-07-01')],
['b', str_to_utcdate('2001-07-01')],
['c', str_to_utcdate('2001-07-01')], ]);
var chart = new google.visualization.Histogram(document.getElementById('chart_div1'));
chart.draw(data);
var data = google.visualization.arrayToDataTable([
['item', 'date'],
['a', 10],
['b', 20],
['c', 30], ]);
var chart = new google.visualization.Histogram(document.getElementById('chart_div2'));
chart.draw(data);
}

5 years now and Histograms still don't support dates. Since I ain't got time and I need my stuff done, I made a workaround (so ugly it hurts) but works.
First, I defined a format just to put a keyword at the start of each label. In my case, since I have only one chart in my page, I just used the keyword 'date'.
const keyword = 'date';
const options = {
// ...
hAxis: {
format: `'${keyword}'#`
},
}
If you have multiple charts in your page and you want them to behave differently, you might want to define different keyword to each one.
Now, using query selector, I selected all the label elements that are of my interest
let labels = Array.from(document.querySelectorAll('#chart svg g:nth-of-type(3) g:nth-of-type(3) g text'));
In my case, my chart was set in a DIV with id 'chart'. If you're using any other id, just replace #chart with your id;
Then I will filter only the labels which start with my keyword 'date'
labels = labels.filter(g => g.innerHTML.startsWith(keyword));
With that, I replace the innerHTML of each element to the date format I wish.
labels.forEach(g => {
const date = new Date(+g.substring(keyword.length));
// you can apply any format method you wish
// .toLocaleDateString()
g.innerHTML = date.toLocaleDateString();
// moment.js
g.innerHTML = moment(date).format('DD/MM/YYYY HH:MM');
});
Also, I'm using interactive charts, so the labels refresh on each user interaction. Therefore, I put everything inside an interval, and the final result is as follows:
const keyword = 'date';
const options = {
// ...
hAxis: {
format: `'${keyword}'#`
},
}
// ... chart setup
setInterval(() => {
let labels = Array.from(document.querySelectorAll('#chart svg g:nth-of-type(3) g:nth-of-type(3) g text'));
labels = labels.filter(g => g.innerHTML.startsWith(keyword));
labels.forEach(g => {
const date = new Date(+g.substring(keyword.length));
g.innerHTML = date.toLocaleDateString();
});
}, 100);
Use this answer with caution. Using query selectors to modify third party generated content is very unsafe because it relies on the hope that the third-party developers won't modify the way the content is generated.

Try out this column chart:
public histogramChart: GoogleChartInterface = {
chartType: 'ColumnChart',
dataTable: [
['Date', 'Number'],
[new Date(2015, 1, 1), 5],
[new Date(2015, 1, 2), 5.1],
[new Date(2015, 1, 3), 6.2],
[new Date(2015, 1, 4), 7]
];,
//opt_firstRowIsData: true,
options: {
title: 'Shooting history',
legend: { position: 'none' },
colors: ['#4285F4'],
},
};

Related

Set dynamic regions for each y axis group

I would like to implement an alternate region background to the chart, sort of like a striped table look.
Is there a way for me to dynamically set the regions for each y-axis block? Once I can do this, I can just basically use css to set alternating background colors.
The regions can be set statically like this:
http://c3js.org/samples/region.html
To do it dynamically, you'd just need to use the API:
http://c3js.org/reference.html#api-regions e.g.
chart.regions([
{axis: 'x', start: 5, class: 'regionX'},
{axis: 'y', end: 50, class: 'regionY'}
]);
So to get alternating stripes build up a regions array as so after generating your chart object:
// find range of all data
var allData = d3.merge (chart.data().map(function(d) {
return d.values.map (function(dd) { return dd.value; });
}));
var dataRange = d3.extent(allData);
dataRange.min = Math.min (dataRange[0], 0);
dataRange.extent = dataRange[1] - dataRange.min;
// set number of pairs of stripes
var stripeCount = 5;
var step = dataRange.extent / stripeCount;
var newRegions = [];
// then divide the data range neatly up into those pairs of stripes
d3.range(0,stripeCount).forEach (function(d) {
newRegions.push ({axis: 'y', start: dataRange.min+(step*d), end: dataRange.min+(step*(d+0.5)), class: 'stripe1'});
newRegions.push ({axis: 'y', start: dataRange.min+(step*(d+0.5)), end: dataRange.min+(step*(d+1)), class: 'stripe2'});
});
// set the new regions on the chart object
chart.regions(newRegions);
css:
.c3-region.stripe1 {
fill: #f00;
}
.c3-region.stripe2 {
fill: #0f0;
}
(If, alternatively, you wanted to make a new stripe pair every 10 units on the y scale you would just make step=10; and change the d3.range to d3.range(0,dataRange.extent/step))
I forked off someone's bar chart on jsfiddle and added the striping --> http://jsfiddle.net/k9c0peax/

google chart with multiple dates

I have multiple collections of data I want to display in a single google chart. Each series is a collection of CPM's with a date:
for instance:
series A[
'2016-09-01' => 2.08023,
'2016-09-04' => 2.01123
];
series B[
'2016-09-01' => 1.99324,
'2016-09-02' => 2.00112
];
Note that the dates in the two series are not the same. I want to draw these two graphs in a single google graph. How should I format the data rows (if this is possible at all).
I have made a JSFiddle with what I thought would be a way to go, but adding a null in the data table doesn't work, I get two dots for the first series where I'm expecting a line. How can I make google chart draw the line instead of the dots?
in the fiddle, the data is setup correctly
however, since there is a null value is Series A due to the different dates
need to use configuration option interpolateNulls: true
from the documentation...
interpolateNulls
Whether to guess the value of missing points. If true, it will guess the value of any missing data based on neighboring points. If false, it will leave a break in the line at the unknown point.
see following working snippet...
google.charts.load('current', {'packages':['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('date', 'X');
data.addColumn('number', 'series a');
data.addColumn('number', 'series b')
data.addRows([
[new Date( 1472688000000 ), 2.08023, 1.99324],
[new Date( 1472774400000 ), null, 2.00112],
[new Date( 1472947200000 ), 2.01123, null]
]);
var options = {
interpolateNulls: true
};
var container = document.getElementById('chart_div');
var chart = new google.visualization.LineChart(container);
chart.draw(data, options);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>

Need to access data in JSON.stringify object

The background for this question is I'm trying to access the selected cell from addListener in google visualization calendar. I could print the selected cell using JSON.stringify(chart.getSelection());. It prints as [{"date":1468195200000,"row":0}].
My problem is to access this date object. I tried to access it as
var obj=JSON.stringify(chart.getSelection());
console.log('selected value '+obj.date);
But it displays as Undefined. When I print the obj it displays as [object, object]
If you need more information please let me know. Any suggestion would be appreciated.
JSON.stringify() serializes a data object into a string. Just access the object directly.
var data = chart.getSelection()[0]; // assuming there is only ever one item in the selection array.
console.log(data.date)
Try this:
var data = [{"date":1468195200000,"row":0}]
var rw = data[0].row;
var dt = new Date(data[0].date);
console.log(rw);
console.log(dt);
careful when accessing the first array element directly when listening for the 'select' event
chart.getSelection()[0]
the 'select' event is fired, both when a selection is made, AND removed
so the above line will fail when the selection is removed
check the length before accessing...
selection = chart.getSelection();
// check selection length
if (selection.length > 0) {
console.log(selection[0].date);
} else {
console.log('selection removed');
}
see following working snippet, select a date, then select again to remove the selection...
google.charts.load('current', {
callback: function () {
var dataTable = new google.visualization.DataTable();
dataTable.addColumn({ type: 'date', id: 'Date' });
dataTable.addColumn({ type: 'number', id :'Score' });
dataTable.addRows([
[ new Date(2016, 3, 13), 101 ],
[ new Date(2016, 3, 14), 102 ],
[ new Date(2016, 3, 15), 103 ],
[ new Date(2016, 3, 16), 104 ],
[ new Date(2016, 3, 17), 105 ]
]);
var chart = new google.visualization.Calendar(document.getElementById('chart_div'));
google.visualization.events.addListener(chart, 'select', function () {
var selection = chart.getSelection();
// check selection length
if (selection.length > 0) {
console.log(selection[0].date);
} else {
console.log('selection removed');
}
});
chart.draw(dataTable);
},
packages:["calendar"]
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
This is an array of objects however there is only one object in this array (or so I think) so what you should do is console.log(obj[0].date); instead!

enable lables for each cell in bar chart

I am trying to set each bar chart cell with lables of A, B and C for all my data to be shown on chart. I tried with data.addColumn('string', 'Alphabets'); but it is not working out.
It shall be easy but i am missing something.
// Load the Visualization API and the piechart package.
google.load('visualization', '1.0', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart() {
// Create the data table.
var data = new google.visualization.DataTable();
var raw_data = [
['A', 40],
['B', 17],
['C', 7]
];
data.addColumn('string', 'Columns');
for (var i = 0; i < raw_data.length; ++i) {
data.addColumn('number', raw_data[i][0]);
}
data.addRows(1);
data.setValue(0, 0, 'row');
for (var i = 0; i < raw_data.length; ++i) {
data.setValue(0, i + 1, raw_data[i][1]);
}
// Set chart options
var options = {'title':'Megafon 27/10 2011',
'width':1300,
'height':600,
'colors' : ['red', 'blue', 'green']
};
// Instantiate and draw our chart, passing in some options.
var chart = new google.visualization.ColumnChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
You are not formatting your data correctly.
Series names are in separate columns (each column will add a new color to the chart, and a new item to the legend).
Value labels (the names of the X-axis categories) are in the first column of each row. You are doing them backwards, therefore there are no axis labels.
For instance, this code:
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['Label', 'Data'],
['A', 10],
['B', 20],
['C', 40],
['D', 80]
]);
// Create and draw the visualization.
new google.visualization.ColumnChart(document.getElementById('visualization')).
draw(data,{});
}
Creates this graph:
However, you want to have a different color for each series (which is bad visualization practice). To do that you need to have 4 different columns to get 4 different colors.
To do this you need to reformat your data as follows:
function drawVisualization() {
// Create and populate the data table.
var data = google.visualization.arrayToDataTable([
['Label', 'Color 1', 'Color 2', 'Color 3', 'Color 4'],
['A', 10, null, null, null],
['B', null, 20, null, null],
['C', null, null, 40, null],
['D', null, null, null, 80]
]);
// Create and draw the visualization.
new google.visualization.ColumnChart(document.getElementById('visualization')).
draw(data,{series: [{color: 'red'},{color: 'orange'},{color: 'yellow'},{color: 'green'}]});
}
This comes out like this:
The issue then is that you have 4 different series in the legend, although they only represent one type of data (and this is why it is bad visualization practice!).
You also get a bit of an ugly looking graph, but that can be fixed with setting the options to isStacked: true.
At any rate, I'd just keep the data as it is in your array, and not fiddle with color. But your mileage may vary.

Annotated Timeline Flickers and Re-Zooms

I have an Annotated Timeline that I would like to update in realtime (similar to how Google Finance does it). However, I am having trouble getting the chart to not flicker and not re-zoom when data is added.
I couldn't get the code to work in JSFiddle (I think because annotated timeline is flash based?) but here is some basic code that you can plug in to Google's Visualization Playground. You can see a saved example here but unfortunately can't modify the code, you'll have to go to the playground to do that.
function drawVisualization() {
var data = new google.visualization.DataTable();
data.addColumn('date', 'Date');
data.addColumn('number', 'My Data');
data.addRows([
[new Date(2009, 1 ,1), 30000],
[new Date(2009, 1 ,2), 14045],
[new Date(2009, 1 ,3), 55022],
[new Date(2009, 1 ,4), 75284],
[new Date(2009, 1 ,5), 41476],
[new Date(2009, 1 ,6), 33322]
]);
var annotatedtimeline = new google.visualization.AnnotatedTimeLine(
document.getElementById('visualization'));
var options = {
'allowRedraw' : true ,
'displayAnnotations' : true,
'zoomStartTime': new Date(2009, 1 ,2),
'zoomEndTime': new Date(2009, 1 ,10)
};
annotatedtimeline.draw(data, options);
// let's add some more data in 3 seconds
setTimeout(function() {
again(annotatedtimeline, data, options);
}, 3000);
}
function again(timeline, data, options) {
data.addRow([new Date(2009, 1 ,7), 30000]);
timeline.draw(data, options);
}
google.setOnLoadCallback(drawVisualization);​
What's weird is that I get pretty different flicker behavior depending on how I set displayAnnotations
displayAnnotations = true : Chart flickers but does not re-zoom
displayAnnotations = false : No chart flicker but it re-zooms to the new data (try it on the playground to see)
Setting allowRedraw = false causes the chart to flicker regardless. Any idea how to get no flicker and no re-zoom?
From the docs:
allowRedraw must be true
displayAnnotations must be false (that is, you cannot show annotations)
That's your problem.

Categories