i'm trying to implement a bar chart with "zebra" background instead of having the default grid lines using google charts.
is there a way to achieve so? so far couldn't figure out how.
here's what i'm trying to achieve:
and here's what i've got so far:
there are no configuration options you can use to change the width of the gridlines.
however, you can manually change, on the chart's 'ready' event.
see following working snippet...
here, the minor gridlines are moved to align with the axis labels.
and the width is increased to the position of the next axis label.
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
['X', 'Y'],
['school_score', 80],
['salary_score', 72],
['benefits_score', 50],
['work_environment', 42],
['security_score', 35]
]);
var container = document.getElementById('chart');
var chart = new google.visualization.BarChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
// find gridlines
var gridlines = container.getElementsByTagName('rect');
var minor = [];
Array.prototype.forEach.call(gridlines, function(gridline) {
if ((gridline.getAttribute('width') === '1') && (gridline.getAttribute('fill') === '#ebebeb')) {
minor.push(gridline);
}
});
// increase width of every other minor gridline, make the rest transparent
var index = 0;
var labelBounds;
var labelBoundsNext;
var chartLayout = chart.getChartLayoutInterface();
while ((labelBounds !== null) && (index < minor.length)) {
if (index % 2 === 0) {
// use axis label bounds to determine width
labelBounds = chartLayout.getBoundingBox('hAxis#0#label#' + index);
labelBoundsNext = chartLayout.getBoundingBox('hAxis#0#label#' + (index + 1));
minor[index].setAttribute('x', labelBounds.left);
minor[index].setAttribute('width', (labelBoundsNext.left - labelBounds.left + labelBounds.width));
} else {
minor[index].setAttribute('fill', 'transparent');
}
index++;
}
});
chart.draw(data);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>
Related
In a grouped column chart of two groups, I would like to center the column when the other column has height 0.
So for example,
The bars for the years 2013 to 2016 should be centered on the year label. This is because the second value in the group is 0, so the height of the bar is 0, so no bar displays:
data = [
["2012", 900, 950],
["2013", 1000, 0],
["2014", 1170, 0],
["2015", 1250, 0],
["2016", 1530, 0]
];
How can I do this with google charts?
see following working snippet...
the bars are centered where their counterpart is blank.
however, it breaks once the user hovers a bar.
the bars are represented by <rect> elements,
which are used to draw the chart itself, the gridlines, the legend bars, etc.
3 <rect> elements are used to highlight the hovered bar.
this is what breaks the code below, it throws off the routine to find the bars.
here's how it works now...
there will be the same number of bars / <rect> elements as there are rows and series,
even if a bar is not visible.
they will be next to last in the list of elements.
the last <rect> element is the x-axis.
the code below works backwards, skipping the last element,
and counts the number of rows / series to gather the bars that may need to be moved.
when the users hovers, there are 3 elements inserted, so the routine will need to change to accommodate.
and they will also need to be moved in order to highlight properly.
otherwise, you can just turn off interactivity and be done...
enableInteractivity: false
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
["Year", "Asia", "Mama"],
["2012", 900, 950],
["2013", 1000, 0],
["2014", 1170, 0],
["2015", 1250, 0],
["2016", 1530, 0]
]);
var options = {
chartArea: {
height: '100%',
width: '100%',
top: 32,
left: 48,
right: 128,
bottom: 48
},
height: 400,
width: '100%'
};
var container = document.getElementById('chart');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'ready', function () {
// get chart layout
var chartLayout = chart.getChartLayoutInterface();
// create mutation observer
var observer = new MutationObserver(function () {
// get bar elements
var rects = container.getElementsByTagName('rect');
var barLength = data.getNumberOfRows() * (data.getNumberOfColumns() - 1);
var bars = [];
for (var i = rects.length - 1; i > ((rects.length - 1) - (barLength + 1)); i--) {
if (i < (rects.length - 1)) {
bars.unshift(rects[i]);
}
}
// process each row
for (var r = 0; r < data.getNumberOfRows(); r++) {
// process each series
for (var s = 1; s < data.getNumberOfColumns(); s++) {
// get chart element bounds
var boundsBar = chartLayout.getBoundingBox('bar#' + (s - 1) + '#' + r);
var boundsLabel = chartLayout.getBoundingBox('hAxis#0#label#' + r);
// determine if bar is hidden
if (boundsBar.height < 1) {
// determine series shown, new x coordinate
var seriesShown = (s === 1) ? 1 : 0;
var xCoord = boundsLabel.left + (boundsLabel.width / 2);
// move bar
bars[r + (data.getNumberOfRows() * seriesShown)].setAttribute('x', (xCoord - (boundsBar.width / 2)));
}
}
}
});
observer.observe(container, {
childList: true,
subtree: true
});
});
chart.draw(data, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>
The y asis labels of my google charts are aligned with respect to the vertical bar while I want them to be aligned with the margin of the chart. I looked everywhere. Can anyone help. The left to right and right to left uses thechart margin as reference not the chart area left margin.
no options for label alignment...
but you can move them manually when the chart's 'ready' event fires
you can use the following chart method to get the bounds of each label
getChartLayoutInterface().getBoundingBox(id)
then set the labels's x attribute to the same value as the width
if the labels need more room, use option --> chartArea.left
see following working snippet...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = new google.visualization.DataTable();
data.addColumn('string', 'x');
data.addColumn('number', 'y0');
data.addRows([
['Brain Tree', 200],
['One Touch', 220],
['PayPal Email', 240],
['PayPal Here', 260],
['PayPal Invoice', 280],
['PayPal Mas', 300]
]);
var container = document.getElementById('chart_div');
var chart = new google.visualization.BarChart(container);
google.visualization.events.addListener(chart, 'click', function (gglClick) {
console.log(JSON.stringify(gglClick));
});
google.visualization.events.addListener(chart, 'ready', function () {
var chartLayout = chart.getChartLayoutInterface();
var labelIndex = -1;
var labelWidth;
var axisLabels = container.getElementsByTagName('text');
Array.prototype.forEach.call(axisLabels, function(label) {
if (label.getAttribute('text-anchor') === 'end') {
labelIndex++;
labelWidth = chartLayout.getBoundingBox('vAxis#0#label#' + labelIndex).width;
label.setAttribute('x', labelWidth);
}
});
});
chart.draw(data, {
chartArea: {
left: 128
},
height: 600
});
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
Just provide is-stacked as true in option if you want to align Y-axis names with bar
options={ isStacked: true}
I'm trying to draw a Google Chart whose x-axis represents the week numbers. As we're crossing a new year, the axis goes 50, 51, 52, 1, 2, 3, ....
I'm properly ordering my data, but Google Charts insists on reordering my x-axis, and I end up with a weird graph:
var chartData = [
["Week","Revenue"],
[40,227],
[41,317],
[42,320],
[43,482],
[44,418],
[45,345],
[46,313],
[47,316],
[48,380],
[49,467],
[50,349],
[51,256],
[52,393],
[1,276],
[2,349],
[3,312]
];
google.load("visualization", "1", {
packages:["corechart"],
callback: function() {
var div = document.getElementById('chart');
var chartDataTable = google.visualization.arrayToDataTable(chartData);
var chart = new google.visualization['LineChart'](div);
chart.draw(chartDataTable);
}});
<div id="chart" style="height: 400px;">test</div>
<script src="//www.google.com/jsapi"></script>
How can I prevent it from reordering my data?
google's object notation allows you to provide a value (v:) and a formatted value (f:)
thus, you can use a value of 1 with a format of '40'
e.g. --> {v: 1, f: '40'}
in a row --> [{v: 1, f: '40'},227]
the following working snippet uses object notation to re-format the values for the x-axis,
and re-use those values for the x-axis labels (hAxis.ticks)
var chartData = [
["Week","Revenue"],
[40,227],
[41,317],
[42,320],
[43,482],
[44,418],
[45,345],
[46,313],
[47,316],
[48,380],
[49,467],
[50,349],
[51,256],
[52,393],
[1,276],
[2,349],
[3,312]
];
var hAxisTicks = [];
chartData.forEach(function (row, index) {
if (index === 0) {
return;
}
row[0] = {
v: index,
f: row[0].toString()
};
hAxisTicks.push(row[0]);
});
google.charts.load('current', {
callback: function () {
var div = document.getElementById('chart');
var chartDataTable = google.visualization.arrayToDataTable(chartData);
var chart = new google.visualization['LineChart'](div);
chart.draw(chartDataTable, {
hAxis: {
ticks: hAxisTicks
}
});
},
packages:['corechart']
});
<div id="chart"></div>
<script src="https://www.gstatic.com/charts/loader.js"></script>
note:
recommend using loader.js to load the the library, 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 only changes the load statement, see snippet above...
EDIT:
there are more options available for continuous axis
which must be sorted, or in reverse sort order ('number', 'date' values)
but the chart will respect the original sort order for a discrete axis ('string' values)
see following snippet for 'string' values
and discrete vs. continuous for more...
var chartData = [
["Week","Revenue"],
[40,227],
[41,317],
[42,320],
[43,482],
[44,418],
[45,345],
[46,313],
[47,316],
[48,380],
[49,467],
[50,349],
[51,256],
[52,393],
[1,276],
[2,349],
[3,312]
];
chartData.forEach(function (row, index) {
if (index === 0) {
return;
}
row[0] = row[0].toString();
});
google.charts.load('current', {
callback: function () {
var div = document.getElementById('chart');
var chartDataTable = google.visualization.arrayToDataTable(chartData);
var chart = new google.visualization['LineChart'](div);
chart.draw(chartDataTable);
},
packages:['corechart']
});
<div id="chart"></div>
<script src="https://www.gstatic.com/charts/loader.js"></script>
Having a Barchart like the following
I want to be able to draw an horizontal reference line (For example at 80%). However this doesn't seem to be possible on Google Charts.
I've tried several things, including combo charts with multiple series.
However it won't look very nice since the hAxis is discrete :(
Your help would be very appreciated.
add another series for the Reference Line
use the same value for all rows and change the series type to 'line'
see following working snippet...
google.charts.load('current', {
callback: drawChart,
packages: ['corechart']
});
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Category', 'Value', 'Reference'],
['Quant', 0.10, 0.80],
['Verbal', 0.30, 0.80],
['Total', 0.20, 0.80]
]);
var chartDiv = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(chartDiv);
chart.draw(data, {
colors: ['lime', 'magenta'],
legend: 'none',
series: {
1: {
type: 'line'
}
},
title: 'Percentile Score',
vAxis: {
format: 'percent',
viewWindow: {
min: 0,
max: 1
}
}
});
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
EDIT
in the above snippet, the reference line stops at the center of the first and last columns
extend the line to the edges of the columns by changing the coordinates of the reference line,
use the 'ready' listener to know when the chart has been drawn
the key is finding the specific chart elements you need to work with,
in the following snippet, the series color is used to find the columns and reference line
google.charts.load('current', {
callback: drawChart,
packages: ['corechart']
});
function drawChart() {
var data = google.visualization.arrayToDataTable([
['Category', 'Value', 'Reference'],
['Quant', 0.10, 0.80],
['Verbal', 0.30, 0.80],
['Total', 0.20, 0.80]
]);
var chartDiv = document.getElementById('chart_div');
var chart = new google.visualization.ColumnChart(chartDiv);
// use colors to find chart elements
var colorMagenta = '#ff00ff';
var colorLime = '#00ff00';
var xBeg; // save first x coord
var xWidth; // save width of column
var rowIndex = -1; // find first column
google.visualization.events.addListener(chart, 'ready', function () {
// columns
Array.prototype.forEach.call(chartDiv.getElementsByTagName('rect'), function(rect, index) {
if (rect.getAttribute('fill') === colorLime) {
rowIndex++;
xWidth = parseFloat(rect.getAttribute('width')) / 2;
if (rowIndex === 0) {
xBeg = parseFloat(rect.getAttribute('x'));
}
}
});
// reference line
Array.prototype.forEach.call(chartDiv.getElementsByTagName('path'), function(path, index) {
if (path.getAttribute('stroke') === colorMagenta) {
// change line coords
var refCoords = path.getAttribute('d').split(',');
refCoords[0] = 'M' + xBeg;
var refWidth = refCoords[2].split('L');
refWidth[1] = parseFloat(refWidth[1]) + xWidth;
refCoords[2] = refWidth.join('L');
path.setAttribute('d', refCoords.join(','));
}
});
});
chart.draw(data, {
colors: [colorLime, colorMagenta],
legend: 'none',
series: {
1: {
type: 'line'
}
},
title: 'Percentile Score',
vAxis: {
format: 'percent',
viewWindow: {
min: 0,
max: 1
}
}
});
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
Is it possible to display a vertical Line marker showing the current x-axis value on LineChart, and moving when mouse moves ?
Thanks in advance.
While this was difficult before, a recent update to the API makes it much easier! You need to use a mouseover event handler to get the mouse coordinates and the new ChartLayoutInterface to translate the coordinates into chart values. Here's some example code:
[edit - fixed cross-browser compatibility issue]
*[edit - updated to get the value of points near the annotation line]*
function drawChart() {
// Create and populate the data table.
var data = new google.visualization.DataTable();
data.addColumn('number', 'x');
// add an "annotation" role column to the domain column
data.addColumn({type: 'string', role: 'annotation'});
data.addColumn('number', 'y');
// add 100 rows of pseudorandom data
var y = 50;
for (var i = 0; i < 100; i++) {
y += Math.floor(Math.random() * 5) * Math.pow(-1, Math.floor(Math.random() * 2));
data.addRow([i, null, y]);
}
// add a blank row to the end of the DataTable to hold the annotations
data.addRow([null, null, null]);
// get the index of the row used for the annotations
var annotationRowIndex = data.getNumberOfRows() - 1;
var options = {
annotation: {
1: {
// set the style of the domain column annotations to "line"
style: 'line'
}
},
height: 400,
width: 600
};
// create the chart
var chart = new google.visualization.LineChart(document.getElementById('chart_div'));
// create 'ready' event listener to add mousemove event listener to the chart
var runOnce = google.visualization.events.addListener(chart, 'ready', function () {
google.visualization.events.removeListener(runOnce);
// create mousemove event listener in the chart's container
// I use jQuery, but you can use whatever works best for you
$('#chart_div').mousemove(function (e) {
var xPos = e.pageX - container.offsetLeft;
var yPos = e.pageY - container.offsetTop;
var cli = chart.getChartLayoutInterface();
var xBounds = cli.getBoundingBox('hAxis#0#gridline');
var yBounds = cli.getBoundingBox('vAxis#0#gridline');
// is the mouse inside the chart area?
if (
(xPos >= xBounds.left || xPos <= xBounds.left + xBounds.width) &&
(yPos >= yBounds.top || yPos <= yBounds.top + yBounds.height)
) {
// if so, draw the vertical line here
// get the x-axis value at these coordinates
var xVal = cli.getHAxisValue(xPos);
// set the x-axis value of the annotation
data.setValue(annotationRowIndex, 0, xVal);
// set the value to display on the line, this could be any value you want
data.setValue(annotationRowIndex, 1, xVal.toFixed(2));
// get the data value (if any) at the line
// truncating xVal to one decimal place,
// since it is unlikely to find an annotation like that aligns precisely with the data
var rows = data.getFilteredRows([{column: 0, value: parseFloat(xVal.toFixed(1))}]);
if (rows.length) {
var value = data.getValue(rows[0], 2);
// do something with value
}
// draw the chart with the new annotation
chart.draw(data, options);
}
});
});
// draw the chart
chart.draw(data, options);
}
See it working here: http://jsfiddle.net/asgallant/tVCv9/12