I am using the Google Column Chart to visualize data.
Problem:
Unfortunately with a certain input the heights of the column bars are not in proportion.
Error Reproduction:
I reconstructed this in a simple JsFiddle.
Here is an other example, which contains a working and a not working version.
Question:
How can the issue be fixed, so that the height is proportional to the value differences of my columns.
If for example one column has the value 20.000 and an other 40.000, the height of the first one should be half as high as for the second column.
Thank you so much.
Code examples & images:
Here is my jsFiddleCode:
Html:
<body>
<h1>
Weird height of Google Column Chart Bars.
</h1>
<div id="columnchart_values"></div>
<h1>
Correct height of Google Column Chart Bars.
</h1>
<div id="columnchart_values2"></div>
</body>
Javascript:
function numberWithCommas(x) {
var parts = x.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ".");
if( typeof( parts[1] ) != 'undefined' ){
parts[1] = parts[1].substr(0,2);
}
return parts.join(",");
}
function drawChart1() {
var data = google.visualization.arrayToDataTable([
['Quelle', 'Geldbetrag', { role: 'style' }, { role: 'annotation' } ],
['test1', 40000, '#11368A', 'Cu' ],
['test2', 29400, '#000357', 'Ag' ],
['test3', 22193, '#40F020', 'Au' ],
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1,
{ calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation" },
2]);
var options = {
title: "Density of Precious Metals, in g/cm^3",
width: 600,
height: 400,
bar: {groupWidth: "95%"},
legend: { position: "none" },
};
var chart = new google.visualization.ColumnChart(document.getElementById("columnchart_values"));
chart.draw(view, options);
}
function drawChart2() {
/*only the data of test 3 changed*/
var data = google.visualization.arrayToDataTable([
['Quelle', 'Geldbetrag', { role: 'style' }, { role: 'annotation' } ],
['test1', 40000, '#11368A', 'Cu' ],
['test2', 29400, '#000357', 'Ag' ],
['test3', 19000, '#40F020', 'Au' ],
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1,
{ calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation" },
2]);
var options = {
title: "Density of Precious Metals, in g/cm^3",
width: 600,
height: 400,
bar: {groupWidth: "95%"},
legend: { position: "none" },
};
var chart = new google.visualization.ColumnChart(document.getElementById("columnchart_values2"));
chart.draw(view, options);
}
google.charts.load('current', {packages: ['corechart'], 'language': 'de'});
google.charts.setOnLoadCallback( function(){
drawChart1();
drawChart2();
} );
The current behavior is to remain backward compatible with the way it used to be years ago. Now it will include the baseline value in the chart if it is "close enough" to the data. But you can force the baseline value of the bars to be included in the chart by simply specifying the baseline value explicitly. For your example, just add:
vAxis: { baseline: 0 }
to your options. See https://jsfiddle.net/2betxw5u/2/
Adding: vAxis: {minValue: 0, format: '€ #,###'} to the options solves the problem.
Related
I hope this question can be understood as I find this topic rather tricky to explain. What I am looking for is a solution for a Google (Column) Chart, in which the chart shows both, X-Axis and legend with values, like a pie chart sometimes does. Most examples I found simply turned off the legend, though.
Here is what I got so far: First example of the fitting x axis:
The legend just shows 'Density', which makes sense, but I am looking for all the metals in the legend as well. So I switched the order of the dataset, to end up with this one:
And here the x axis shows density instead of the metals. So what I am looking for is something like this:
Either way, be it the manipulation of the legend or the manipulation of the axis works for me. Any ideas how to proceed?
Sources
First version
function drawChart() {
var data = google.visualization.arrayToDataTable([
["Element", "Density", { role: "style" } ],
["Copper", 8.94, "#b87333"],
["Silver", 10.49, "silver"],
["Gold", 19.30, "gold"],
["Platinum", 21.45, "color: #e5e4e2"]
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1,
{ calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation" },
2]);
var options = {
title: "Density of Precious Metals, in g/cm^3",
width: 600,
height: 400,
bar: {groupWidth: "95%"},
};
var chart = new google.visualization.ColumnChart(document.getElementById("columnchart_values"));
chart.draw(view, options);
}
My Fiddle: https://jsfiddle.net/927pjLvb/2/
Second version:
function drawChart() {
var data = google.visualization.arrayToDataTable([
["Element", "Copper", "Silver", "Gold", "Platinum"],
["Density", 8.94, 10.49, 19.30, 21.45],
]);
var view = new google.visualization.DataView(data);
view.setColumns([
0,
1,
{ calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation" },
2,
{ calc: "stringify",
sourceColumn: 2,
type: "string",
role: "annotation" },
3,
{ calc: "stringify",
sourceColumn: 3,
type: "string",
role: "annotation" },
4,
{ calc: "stringify",
sourceColumn: 4,
type: "string",
role: "annotation" }
]);
var options = {
title: "Density of Precious Metals, in g/cm^3",
width: 600,
height: 400,
colors: ['#b87333', 'silver', 'gold', '#e5e4e2'],
bar: {groupWidth: "95%"},
};
var chart = new google.visualization.ColumnChart(document.getElementById("columnchart_values"));
chart.draw(view, options);
}
My fiddle: https://jsfiddle.net/8jaL9kf3/
starting with the second version, leave the x-axis label blank.
var data = google.visualization.arrayToDataTable([
['Element', 'Copper', 'Silver', 'Gold', 'Platinum'],
['', 8.94, 10.49, 19.30, 21.45]
]);
on the chart's 'ready' event,
copy the labels from the legend,
and place them under the bars,
using chart method --> getChartLayoutInterface()
the layout interface has method --> getBoundingBox(id)
this method returns the coordinates of chart elements,
by passing the id of the element.
in this case, we want to know the coordinates of the bars.
the id of each bar is structured as follows.
'bar#0#0' // <-- first bar
where the first 0 is the series number, and the second row number.
once we know the coordinates of the bars,
we can place the copied labels under them.
see following working snippet...
google.charts.load('current', {
packages:['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
['Element', 'Copper', 'Silver', 'Gold', 'Platinum'],
['', 8.94, 10.49, 19.30, 21.45]
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, {
calc: 'stringify',
sourceColumn: 1,
type: 'string',
role: 'annotation'
}, 2, {
calc: 'stringify',
sourceColumn: 2,
type: 'string',
role: 'annotation'
}, 3, {
calc: 'stringify',
sourceColumn: 3,
type: 'string',
role: 'annotation'
}, 4, {
calc: 'stringify',
sourceColumn: 4,
type: 'string',
role: 'annotation'
}]);
var options = {
title: 'Density of Precious Metals, in g/cm^3',
width: 600,
height: 400,
colors: ['#b87333', 'silver', 'gold', '#e5e4e2'],
bar: {groupWidth: '95%'},
};
var container = document.getElementById('columnchart_values');
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'ready', function (gglClick) {
// chart svg
var svg = container.getElementsByTagName('svg')[0];
// chart layout
var chartLayout = chart.getChartLayoutInterface();
// get all chart labels, chart title will be first, legend labels next
var labels = container.getElementsByTagName('text');
// process each series in the data table
for (var series = 1; series < data.getNumberOfColumns(); series++) {
// get bar bounds
var barBounds = chartLayout.getBoundingBox('bar#' + (series - 1) + '#0');
// copy legend label
var barLabel = labels[series].cloneNode(true);
// padding above label
var labelPadding = 4;
// center align label
barLabel.setAttribute('text-anchor', 'middle');
// set label x,y coordinates
barLabel.setAttribute('x', barBounds.left + (barBounds.width / 2));
barLabel.setAttribute('y', barBounds.top + barBounds.height + parseFloat(barLabel.getAttribute('font-size')) + labelPadding);
// add label to chart svg
svg.appendChild(barLabel);
}
});
chart.draw(view, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="columnchart_values"></div>
note: the above example assumes the chart will have a title option.
I have chart configured like in working jsfiddle.
I have configured labels(basing on google doc documentation: https://developers.google.com/chart/interactive/docs/gallery/barchart#labeling-bars)
But they aren't visible. When I change chart type to google.visualization.BarChart, then labels appear but bars structure is destroyed. How to add labels to my configuration?
Replicated:
https://jsfiddle.net/41fmq37j/
JS:
google.charts.load('current', {'packages':['corechart', 'bar']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
[{label: 'Year', id: 'year', type: 'number'},
{label: 'Sales', id: 'Sales', type: 'number'},
{label: 'Expenses', id: 'Expenses', type: 'number'},
{ role: 'annotation' }],
[2014, 10, 400 ,'label1'],
[2014, 800, 100 ,'label2'],
[2015, 200, 460 ,'label3'],
[2015, 110, 660 ,'label4'],
[2016, 100, 300 ,'label5'],
[2016, 600, 120 ,'label6'],
[2017, 360, 540 ,'label7'],
[2017, 300, 500 ,'label8']
]);
var options = {
chart: {
title: 'Sales and Expenses',
subtitle: 'Some descr',
},
bars: 'horizontal',
height: 400,
isStacked: true,
};
var chart = new google.charts.Bar(document.getElementById('chart_div'));
chart.draw(data, google.charts.Bar.convertOptions(options));
}
HTML:
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
EDIT:
It is possible to configure yAxis like below? Because current format can be confusing.
I would like to create more, a little different graphs, for example which will group bars by string. So another question is: how we can archive grouping yAxis by string? Maybe we should create any comparator?
material charts do not support columns roles, such as 'annotation',
along with several other options
and, it's not possible to have multiple stacks per label in classic charts
as such, we can use a material chart,
and add our own annotations manually,
on the chart's 'ready' event
see following working snippet...
google.charts.load('current', {
packages:['bar']
}).then(function () {
var data = google.visualization.arrayToDataTable([
[
{label: 'Year', id: 'year', type: 'number'},
{label: 'Sales', id: 'Sales', type: 'number'},
{label: 'Expenses', id: 'Expenses', type: 'number'},
{role: 'annotation', type: 'string'}
],
[2014, 10, 400, 'label1'],
[2014, 800, 100, 'label2'],
[2015, 200, 460, 'label3'],
[2015, 110, 660, 'label4'],
[2016, 100, 300, 'label5'],
[2016, 600, 120, 'label6'],
[2017, 360, 540, 'label7'],
[2017, 300, 500, 'label8']
]);
var options = {
chart: {
title: 'Sales and Expenses',
subtitle: 'Some descr',
},
bars: 'horizontal',
height: 400,
isStacked: true,
vAxis: {
format: '0'
}
};
var container = document.getElementById('chart_div');
var chart = new google.charts.Bar(container);
// add annotations
google.visualization.events.addListener(chart, 'ready', function () {
var annotation;
var bars;
var copyLabel;
var coordsBar;
var coordsLabel;
var labels;
var svg;
// get svg
svg = container.getElementsByTagName('svg')[0];
// find label to clone
labels = svg.getElementsByTagName('text');
Array.prototype.forEach.call(labels, function(label) {
if (label.textContent === data.getValue(0, 0).toString()) {
copyLabel = label;
}
});
// find top bars, add labels
bars = svg.getElementsByTagName('path');
Array.prototype.forEach.call(bars, function(bar, index) {
coordsBar = bar.getBBox();
annotation = copyLabel.parentNode.insertBefore(copyLabel.cloneNode(true), copyLabel);
coordsLabel = annotation.getBBox();
annotation.textContent = data.getValue(index, 3);
annotation.setAttribute('fill', '#000000');
annotation.setAttribute('x', coordsBar.x + coordsBar.width - 16);
annotation.setAttribute('y', coordsBar.y + coordsBar.height - (coordsLabel.height / 2));
annotation.style.zIndex = -1;
});
});
chart.draw(data, google.charts.Bar.convertOptions(options));
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
EDIT
the annotation script finds the first y-axis label,
and uses it as a clone for the annotations.
if the values for the y-axis change,
then the script to find the label needs to change.
updated here...
// find label to clone
labels = svg.getElementsByTagName('text');
Array.prototype.forEach.call(labels, function(label) {
// find first y-axis label
if (label.textContent === formatDate.formatValue(data.getValue(0, 0))) {
annotation = label;
}
});
see following working snippet...
google.charts.load('current', {
packages:['bar']
}).then(function () {
var data = google.visualization.arrayToDataTable([
[
{label: 'Date', id: 'string', type:'date'},
{label: 'Sales', id: 'Sales', type: 'number'},
{label: 'Expenses', id: 'Expenses', type: 'number'},
{role: 'annotation', type: 'string'}
],
[new Date('2011-12-20'), 10, 400, 'User1'],
[new Date('2011-12-20'), 800, 100, 'User2'],
[new Date('2011-12-21'), 200, 460, 'User3'],
[new Date('2011-12-21'), 200, 460, 'User3'],
]);
var dateFormat = 'YYYY/MM/dd';
var options = {
chart: {
title: 'Sales and Expenses',
subtitle: 'Some descr',
},
bars: 'horizontal',
height: 400,
isStacked: true,
vAxis: {
format: dateFormat,
}
};
var container = document.getElementById('chart_div');
var chart = new google.charts.Bar(container);
var formatDate = new google.visualization.DateFormat({
pattern: dateFormat
});
// add annotations
google.visualization.events.addListener(chart, 'ready', function () {
var annotation;
var bars;
var copyLabel;
var coordsBar;
var coordsLabel;
var labels;
var svg;
// get svg
svg = container.getElementsByTagName('svg')[0];
// find label to clone
labels = svg.getElementsByTagName('text');
Array.prototype.forEach.call(labels, function(label) {
// find first y-axis label
if (label.textContent === formatDate.formatValue(data.getValue(0, 0))) {
copyLabel = label;
}
});
// find top bars, add labels
bars = svg.getElementsByTagName('path');
Array.prototype.forEach.call(bars, function(bar, index) {
coordsBar = bar.getBBox();
annotation = copyLabel.parentNode.insertBefore(copyLabel.cloneNode(true), copyLabel);
coordsLabel = annotation.getBBox();
annotation.textContent = data.getValue(index, 3);
annotation.setAttribute('fill', '#ffffff');
annotation.setAttribute('text-anchor', 'start');
annotation.setAttribute('x', coordsBar.x + coordsBar.width);
annotation.setAttribute('y', coordsBar.y + (coordsBar.height / 2) + (coordsLabel.height / 2));
annotation.style.zIndex = -1;
});
});
chart.draw(data, google.charts.Bar.convertOptions(options));
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
I am using Google Chart's bar graph chart. There are annotation for every bar at the top, inside of the bar and I want create more space (more than default) between bar top line and annotation text. I have not find any way to create top padding (or top margin) around annotation text.
Here is my code:
var data = google.visualization.arrayToDataTable([
[USER_AVERAGE, 'User Average', { role: 'style' }],
['SANDRA COOMBS', 8.6, '#008FBE'],
['STEVE ADAMS', 4.3, '#008FBE'],
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, 2,
{
calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation"
}]);
var options = {
legend: {
position: 'none'
},
chartArea: { width: width, height: height, right: right },
isStacked: true,
orientation: orientation.orientation,
fontSize: '12',
fontName: 'OpenSans-Regular',
hAxis: {
viewWindowMode: 'maximized',
},
vAxis: {
viewWindowMode: 'maximized',
},
animation: {
startup: true,
duration: 1500,
easing: 'out',
},
};
var chart = new google.visualization.ColumnChart(document.getElementById("averageDaysChart"));
chart.draw(view.toDataTable(), options);
there are no config options for annotation padding, position, or the like...
however, you can move them manually on the chart's 'animationfinish' event
but, a MutationObserver must be used,
because the chart will move them back on interactivity,
such as when you hover the column.
see following working snippet for an example of moving the annotations...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
['USER_AVERAGE', 'User Average', { role: 'style' }],
['SANDRA COOMBS', 8.6, '#008FBE'],
['STEVE ADAMS', 4.3, '#008FBE'],
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, 2,
{
calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation"
}]);
var options = {
legend: {
position: 'none'
},
//chartArea: { width: width, height: height, right: right },
isStacked: true,
//orientation: orientation.orientation,
fontSize: '12',
fontName: 'OpenSans-Regular',
hAxis: {
viewWindowMode: 'maximized',
},
vAxis: {
viewWindowMode: 'maximized',
},
animation: {
startup: true,
duration: 1500,
easing: 'out',
},
};
var container = document.getElementById("averageDaysChart");
var chart = new google.visualization.ColumnChart(container);
google.visualization.events.addListener(chart, 'animationfinish', function () {
moveAnnotations();
var observer = new MutationObserver(moveAnnotations);
observer.observe(container, {
childList: true,
subtree: true
});
});
var originalY = {};
function moveAnnotations() {
var labels = container.getElementsByTagName('text');
Array.prototype.forEach.call(labels, function(label) {
if ((!isNaN(parseFloat(label.textContent))) && (label.getAttribute('text-anchor') === 'middle')) {
if (!originalY.hasOwnProperty(label.textContent)) {
originalY[label.textContent] = parseFloat(label.getAttribute('y'));
}
label.setAttribute('y', originalY[label.textContent] + 20);
}
});
}
chart.draw(view.toDataTable(), options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="averageDaysChart"></div>
No need for an animation hook or MutationObserver, just use good old fashioned CSS for this.
With Column and Bar charts, you only want to apply the "padding" to the annotation if it's placed within the bar itself. It's only placed inside the bar if the annotation text fits in the bar. When placed inside, it's given a white text color, so let's target that.
#averageDaysChart text[fill="#ffffff"] {
transform: translateY(10px);
}
This question already has an answer here:
Google Charts show all labels
(1 answer)
Closed 4 years ago.
I'm working with Google Bar Charts. In the left side, I need to add the description of the bars.
Move within USA
Received in New Jersy
Handed over to International
Currier At Sri lanka port
But I can't show the whole sentence in the bar chart. Below I attached the bar chart and the code
google.charts.load("current", {packages: ["corechart"]});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
["Element", "Density", {role: "style"}],
["Move within USA",parseInt(orderObj.RNJ), "color :blue"],
["Received in New Jersy", parseInt(orderObj.HOC), "color :yellow"],
["Handed over to International Currier", parseInt(orderObj.ASP), "color :red"],
["At Sri lanka port", parseInt(orderObj.KWH), "color: green"]
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1,
{
calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation"
},
2]);
var options = {
title: "",
width: 600,
height: 300,
bar: {groupWidth: "65%"},
legend: {position: "none"}
};
var chart = new google.visualization.BarChart(document.getElementById("chart_divSea1"));
chart.draw(view, options);
How about adjusting it using left and width of chartArea as follows?
var options = {
title: "",
width: 600,
height: 300,
bar: {groupWidth: "65%"},
legend: {position: "none"},
chartArea: {left: 250, width: 300} // Added
};
google.charts.load("current", {packages: ["corechart"]});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var orderObj = {RNJ: 6, HOC: 26, ASP: 6, KWH: 14}; // sample values
var data = google.visualization.arrayToDataTable([
["Element", "Density", {role: "style"}],
["Move within USA",parseInt(orderObj.RNJ), "color :blue"],
["Received in New Jersy", parseInt(orderObj.HOC), "color :yellow"],
["Handed over to International Currier", parseInt(orderObj.ASP), "color :red"],
["At Sri lanka port", parseInt(orderObj.KWH), "color: green"]
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1, {
calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation"
}, 2]);
var options = {
title: "",
width: 600,
height: 300,
bar: {groupWidth: "65%"},
legend: {position: "none"},
chartArea: {left: 250, width: 300} // Added
};
var chart = new google.visualization.BarChart(document.getElementById("chart_divSea1"));
chart.draw(view, options);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_divSea1"></div>
Reference :
Configuration Options
If this was not what you want, I'm sorry.
i am using google chart api to show a chart on a bootstrap modal with framework Laravel, i'm having an issue because i'm not beein able to change the colour of the bar and put one blue and the other one green, another issue is that at the right it only put me the name of une of the bars(RX).
Here is my js code:
// Load the Visualization API and the corechart package.
google.charts.load('current', {'packages':['corechart']});
// Set a callback to run when the Google Visualization API is loaded.
// google.charts.setOnLoadCallback(drawChart);
// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.
function drawChart(nombre, unidad, tipo, valor, media) {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Name');
data.addColumn('number', 'RX');
data.addColumn({type: 'string', role: 'annotation'});
data.addRows([
['Tú', parseInt(valor), valor+' '+unidad],
['Media', parseFloat(media), parseInt(media)+' '+unidad],
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1,1,2]);
var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));
chart.draw(view, {
// height: 400,
// width: 600,
series: {
0: {
type: 'bars'
},
1: {
type: 'line',
color: 'grey',
lineWidth: 0,
pointSize: 0,
visibleInLegend: false
}
},
vAxis: {
maxValue: 100,
minValue: 0,
}
});
$("#myModalLabel").empty();
$("#myModalLabel").append(tipo+" - "+nombre);
$("#modalChart").modal();
}
Here is the chart that i get:
you can write your drawchart function like this
<script type="text/javascript">
google.charts.load("current", {packages:['corechart']});
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
["Element", "Density", { role: "style" } ],
["Copper", 8.94, "#b87333"],
["Silver", 10.49, "silver"],
["Gold", 19.30, "gold"],
["Platinum", 21.45, "color: #e5e4e2"]
]);
var view = new google.visualization.DataView(data);
view.setColumns([0, 1,
{ calc: "stringify",
sourceColumn: 1,
type: "string",
role: "annotation" },
2]);
var options = {
title: "Density of Precious Metals, in g/cm^3",
width: 600,
height: 400,
bar: {groupWidth: "95%"},
legend: { position: "none" },
};
var chart = new google.visualization.ColumnChart(document.getElementById("columnchart_values"));
chart.draw(view, options);
}
</script>
For more https://developers.google.com/chart/interactive/docs/gallery/columnchart#labeling-columns