I'm having an issue with Google bar chart. It doesn't put y-axis values in order (Figure A). The other issue is that it shows bars even if the values are zeros (Figure B).
Please see my code below :
success: function (r) {
var data = new google.visualization.DataTable();
var newData = r.d;
// determine the number of rows and columns.
var numRows = newData.length;
if (numRows > 0) {
var numCols = newData[0].length;
// in this case the first column is of type 'string'.
data.addColumn('string', newData[0][0]);
// all other columns are of type 'number'.
for (var i = 1; i < numCols; i++)
data.addColumn('string', newData[0][i]);
// now add the rows.
for (var i = 1; i < numRows; i++)
data.addRow(newData[i]);
}
else {
data.addColumn('string', "Category");
data.addColumn('number', "No Data");
data.addRow(null);
}
var options = {
bars: 'vertical',
height: 350,
bar: { groupWidth: "40" },
legend: { position: 'top', maxLines: 3 },
chartArea: { 'width': '50%', 'height': '100%' }
};
var chart = new google.charts.Bar(document.getElementById('chart'));
chart.draw(data, google.charts.Bar.convertOptions(options));
}
Figure A
Figure B
How can I fix this issue?
this happens when string values are used, instead of numbers...
here, the comment mentions using number, but the code has string
// all other columns are of type 'number'.
for (var i = 1; i < numCols; i++)
data.addColumn('string', newData[0][i]);
try changing to the following...
// all other columns are of type 'number'.
for (var i = 1; i < numCols; i++)
data.addColumn('number', parseFloat(newData[0][i]));
Related
I want to display the total for each stacked column on the end of the column.
I cant quite work out how to total the columns within the setColumns view code i have below. Can anyone help finalise this?
I am looking to achieve the total on the end of the stacked column like this:
[![enter image description here][1]][1]
Here is my code so far that works for charting but not quite for displaying total labels
myDrawFunc(){
var data = new google.visualization.DataTable();
data.addColumn('string', 'Scenario');
for (var i = 0; i < rows.length; i++) {
ptype = rows[i].cells[2].querySelector('.part').value;
if (ptype.length > 0) {
console.log(ptype);
// Declare columns
data.addColumn('number', ptype);
}
}
for (var i = 0; i < tcd.length; i++) {
var dn = 1 + i;
designLabel = "Design " + dn;
tcd[i].unshift(designLabel);
}
data.addRows(tcd);
//var view = new google.visualization.DataView(data);
var view = getDataView(data);
console.log(data);
chart = new google.visualization.BarChart(document.getElementById('chart_div'));
chart.draw(view, globalOptions);
}
//get Data view
function getDataView(dataTable) {
var dataView;
var viewColumns = [];
var columnsTotal = dataTable.getNumberOfColumns();
for (var i = 0; i < columnsTotal; i++) {
addViewColumn(viewColumns, i);
//add extra columns
if (i == columnsTotal) {
pushExtraCols(viewColumns);
}
}
// set series for displaying total columns
createSeries(columnsTotal) ;
dataView = new google.visualization.DataView(dataTable);
dataView.setColumns(viewColumns);
return dataView;
}
function addViewColumn(viewColumns, index) {
viewColumns.push(index);
if (index > 0) {
viewColumns.push({
calc: function (dt, row) {
console.log(row, index);
return dt.getValue(row, index);
},
role: 'annotation',
type: 'number'
});
}
}
function createSeries(columnsTotal) {
var seriesT = columnsTotal - 1;
console.log(seriesT);
seriesObj = {
[seriesT]: {
annotations: {
stem: {
color: "transparent",
length: 128
},
textStyle: {
color: "red",
}
},
enableInteractivity: false,
tooltip: "none",
visibleInLegend: false
}
}
globalOptions.series = seriesObj;
}
function pushExtraCols(viewColumns) {
viewColumns.push(
{
calc: function (dt, row) {
return 0;
},
label: "Total",
type: "number",
});
viewColumns.push({
calc: function (dt, row) {
return getTotal(dt, row);
},
type: "number",
role: "annotation"
});
}
//add up row total per column
function getTotal(dt, row) {
total = 0;
for (var i = 0; i < dt.getNumberOfColumns()-1; i++) {
total += dt.getValue(row, i);
}
console.log("total=" + total);
return total;
}
Try with parseInt
total = parseInt(total) + dt.getValue(row, i)
There is a similar thread here but the answer isn't clear and there is no clear example to follow.
I need to dynamically add series to a Google Charts graph. Suppose each successive click of a button should add a new series from my array. Right now it just replaces it. What should I do?
// Init
google.charts.load('current', {'packages':['corechart']});
var currentclick = 0;
// My Data
var titles = ['Year1','Year2','Year3','Year4'];
var nums = [ [44,12,33,22], [33,11,7,8], [2,1,65,44] ];
$('#addSeries').click(function() {
if (currentclick < 3) {
draw(nums, currentclick);
}
currentclick++;
});
function draw(arr, seriesnum) {
var chartData = new google.visualization.DataTable();
chartData.addColumn('string', 'Year');
chartData.addColumn('number', 'Value');
var chartRowArray = $.makeArray();
for (var i = 0; i < 4; i++) {
chartRowArray.push( [ titles[i], arr[seriesnum][i] ] );
}
chartData.addRows(chartRowArray);
var chart = new google.visualization.LineChart(document.getElementById('chartarea'));
chart.draw(chartData, null);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="addSeries">Add Next Series</button>
<div id="chartarea">
</div>
You want to add a line chart from the data of nums every time when you clicked "Add Next Series" button.
If my understanding is correct, how about this modification? I think that there are several answers for your situation. So please think of this as just one of several answers.
Modification point:
In this modification, chartRowArray is declared as a global variable. When the button is clicked, the data is added to chartRowArray. By this, the line chars are added to the existing chart.
Modified script:
// Init
google.charts.load('current', {'packages':['corechart']});
var currentclick = 0;
// My Data
var titles = ['Year1','Year2','Year3','Year4'];
var nums = [ [44,12,33,22], [33,11,7,8], [2,1,65,44] ];
$('#addSeries').click(function() {
if (currentclick < 3) {
draw(nums, currentclick);
}
currentclick++;
});
// Below script was modified.
var chartRowArray = $.makeArray(); // Added
function draw(arr, seriesnum) {
var chartData = new google.visualization.DataTable();
chartData.addColumn('string', 'Year');
for (var i = 0; i < seriesnum + 1; i++) { // Added
chartData.addColumn('number', 'Value');
}
if (seriesnum === 0) { // Added
for (var i = 0; i < 4; i++) {
chartRowArray.push( [ titles[i], arr[seriesnum][i] ] );
}
} else { // Added
for (var i = 0; i < 4; i++) {
chartRowArray[i].push(arr[seriesnum][i]);
}
}
chartData.addRows(chartRowArray);
var chart = new google.visualization.LineChart(document.getElementById('chartarea'));
chart.draw(chartData, null);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="addSeries">Add Next Series</button>
<div id="chartarea"></div>
If I misunderstood your question and this was not the result you want, I apologize.
The key thing is the final structure of the data.
The Column Array should be
0: Year
1: Value
2: Value
etc.
The Column array is added 1-by-1 as chartData.addColumn(type, name).
The data's Row Array should be
0:
0: Year1
1: 44
2: 33
1:
0: Year2
1: 12
2: 11
etc.
The Row Array is added in one shot as chartData.addRows(rowArray).
Knowing this, I made both RowArray / ColArray global variables that get modified on the fly (thanks for the idea Tainake) and for the initial conditions when the arrays are empty, I construct or initialize them for the first time.
Working example below. Thanks again for the help!
// Init
google.charts.load('current', {'packages':['corechart']});
var currentclick = 0;
// My Data
var titles = ['Year1','Year2','Year3','Year4'];
var nums = [ [44,12,33,22], [33,11,7,8], [2,1,65,44] ];
var chartColArray = $.makeArray(); // Global var
var chartRowArray = $.makeArray(); // Global var
$('#addSeries').click(function() {
if (currentclick < 3) {
draw(nums, currentclick);
}
currentclick++;
});
function draw(arr, seriesnum) {
var chartData = new google.visualization.DataTable();
// For initial Column, push 'Year'; otherwise, push 'Value'
if (chartColArray.length == 0) {
chartColArray.push('Year');
}
chartColArray.push('Value');
// addColumn() has to be 1-by-1-by-1, there is no addColumns(colarray)
$.each(chartColArray, function(index, item) {
(index == 0 ? chartData.addColumn('string', item) : chartData.addColumn('number', item));
});
for (var i = 0; i < 4; i++) {
// For initial Row subarray, create subarray and push 'Year Series';
// otherwise, push actual 'Series' value
if (chartRowArray[i] == undefined) {
chartRowArray[i] = $.makeArray();
chartRowArray[i].push(titles[seriesnum]);
}
chartRowArray[i].push(nums[seriesnum][i]);
}
chartData.addRows(chartRowArray);
// Instantiate and draw the chart.
var chart = new google.visualization.LineChart(document.getElementById('chartarea'));
chart.draw(chartData, null);
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="addSeries">Add Next Series</button>
<div id="chartarea">
</div>
I have a Scatter Series with a set of points, like the one shown here. https://developers.google.com/chart/interactive/docs/gallery/scatterchart
The points are grouped and each group is shown in different color. I would like to draw a polygon around each group (convex hull). Looks like there is not a straightforward way to add polygons each with n boundary-points to the chart.
if you have an algorithm to find the boundary points,
you can use a ComboChart to draw both the scatter and line series...
use option seriesType to set the default type
use option series to customize the type for a particular series
in the following working snippet,
the algorithm used was pulled from --> Convex Hull | Set 1 (Jarvis’s Algorithm or Wrapping)
(converted from the Java version)
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var groupA = [
[0,3],[2,3],[1,1],[2,1],[3,0],[0,0],[3,3],[2,2]
];
var groupB = [
[11,11],[12,12],[12,10],[12,14],[13,13],[14,12],[15,12],[16,12]
];
var data = new google.visualization.DataTable();
data.addColumn('number', 'x');
data.addColumn('number', 'y');
data.addRows(groupA);
data.addRows(groupB);
addGroup('A', data, groupA)
addGroup('B', data, groupB)
var options = {
chartArea: {
bottom: 48,
height: '100%',
left: 36,
right: 24,
top: 36,
width: '100%'
},
height: '100%',
seriesType: 'line',
series: {
0: {
type: 'scatter'
}
},
width: '100%'
};
var chart = new google.visualization.ComboChart(document.getElementById('chart_div'));
drawChart();
window.addEventListener('resize', drawChart, false);
function drawChart() {
chart.draw(data, options);
}
function addGroup(group, dataTable, points) {
var polygon = convexHull(points);
var colIndex = dataTable.addColumn('number', group);
for (var i = 0; i < polygon.length; i++) {
var rowIndex = dataTable.addRow();
dataTable.setValue(rowIndex, 0, polygon[i][0]);
dataTable.setValue(rowIndex, colIndex, polygon[i][1]);
}
}
function orientation(p, q, r) {
var val = (q[1] - p[1]) * (r[0] - q[0]) -
(q[0] - p[0]) * (r[1] - q[1]);
if (val == 0) {
return 0; // collinear
} else if (val > 0) {
return 1; // clock wise
} else {
return 2; // counterclock wise
}
}
function convexHull(points) {
// must be at least 3 rows
if (points.length < 3) {
return;
}
// init
var l = 0;
var p = l;
var q;
var hull = [];
// find leftmost point
for (var i = 1; i < points.length; i++) {
if (points[i][0] < points[l][0]) {
l = i;
}
}
// move counterclockwise until start is reached
do {
// add current point to result
hull.push(points[p]);
// check orientation (p, x, q) of each point
q = (p + 1) % points.length;
for (var i = 0; i < points.length; i++) {
if (orientation(points[p], points[i], points[q]) === 2) {
q = i;
}
}
// set p as q for next iteration
p = q;
} while (p !== l);
// add back first hull point to complete line
hull.push(hull[0]);
// set return value
return hull;
}
});
html, body, #chart_div {
height: 100%;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
Based on googleAPI documentation:
https://developers.google.com/chart/interactive/docs/gallery/columnchart?hl=en
As you can see in "labeling columns" section each column is labeled with a static value. I want to know if it is possible to label a column with a specific value resulting of the sum of all.
// Set chart options
var options = {
width: 400,
height: 300,
calc:????
};
Should i set this "calc" field with a specific function?
JSFIDDLE: Total Labeling Column
I can't figure out how can i customize a label with the sum values of each stacked column.
You can use getNumberOfRows(), getNumberOfColumns() and getValue() functions to calculate total and set that value instead of string total:
function drawChart() {
// Create the data table.
var data = new google.visualization.arrayToDataTable(array);
for (var i = 0; i < data.getNumberOfRows(); i++) {
var total = 0;
for (var j = 1; j < data.getNumberOfColumns() - 2; j++) {
// console.log(data.getValue(i, j));
total += data.getValue(i, j);
}
data.setValue(i, data.getNumberOfColumns() - 1, '' + total);
}
...
You will have to change or size of chart or size of fonts to get values properly displayed on columns. Labels are displayed correctly.
This is my answer.. i hope it helps someone with the same issue.
function getValueAt(column, dataTable, row) {
return dataTable.getFormattedValue(row, column);
}
function setLabelTotal(dataTable) {
//dataTable must have role: annotation
var SumOfRows = 0;
for (var row = 0; row < dataTable.getNumberOfRows(); row++) {
SumOfRows = 0;
for (var col = 0; col < dataTable.getNumberOfColumns(); col++) {
if (dataTable.getColumnType(col) == 'number') {
SumOfRows += dataTable.getValue(row, col);
}
if(dataTable.getColumnRole(col) == 'annotation')
{dataTable.setValue(row, col, SumOfRows.toString());}
}
}
}
Notice that you must call these methods on main function (e.g. "drawChart").
When I use an Area Chart on Google Drive, I can select an option to "Switch Rows / Columns".
Now that I am playing with the Javascript API, I'd like to do the same but couldn't find a way to do it in the documentation.
Here's the code I am using successfully. All I need is to switch row/column on the API.
<script type="text/javascript">
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = google.visualization.arrayToDataTable([
['data',0,1,2,3,4,5,6]
,['2013-04-14 (336)',064,04,03,02,06,02,02]
,['2013-04-21 (169)',0,028,03,02,04,02,02]
,['2013-04-28 (121)',0,0,027,02,01,02,02]
,['2013-05-05 (101)',0,0,0,020,0,01,0]
,['2013-05-12 (688)',0,0,0,0,0143,017,07]
,['2013-05-19 (3226)',0,0,0,0,0,0642,022]
,['2013-05-26 (321)',0,0,0,0,0,0,082]
]);
var options = {
title: 'Company Performance', isStacked:true,
hAxis: {title: 'Year', titleTextStyle: {color: 'red'}}
};
var chart = new google.visualization.AreaChart(document.getElementById('chart_div'));
chart.draw(data, options);
}
</script>
Can anyone help?
Unfortunately you have to transpose the DataTable. The following code will do the job. If anyone can improve it, please share improved version.
This function would work passing a DataView as well.
In your case: var transposedData = transposeDataTable(data);
function transposeDataTable(dataTable) {
//step 1: let us get what the columns would be
var rows = [];//the row tip becomes the column header and the rest become
for (var rowIdx=0; rowIdx < dataTable.getNumberOfRows(); rowIdx++) {
var rowData = [];
for( var colIdx = 0; colIdx < dataTable.getNumberOfColumns(); colIdx++) {
rowData.push(dataTable.getValue(rowIdx, colIdx));
}
rows.push( rowData);
}
var newTB = new google.visualization.DataTable();
newTB.addColumn('string', dataTable.getColumnLabel(0));
newTB.addRows(dataTable.getNumberOfColumns()-1);
var colIdx = 1;
for(var idx=0; idx < (dataTable.getNumberOfColumns() -1);idx++) {
var colLabel = dataTable.getColumnLabel(colIdx);
newTB.setValue(idx, 0, colLabel);
colIdx++;
}
for (var i=0; i< rows.length; i++) {
var rowData = rows[i];
console.log(rowData[0]);
newTB.addColumn('number',rowData[0]); //assuming the first one is always a header
var localRowIdx = 0;
for(var j=1; j< rowData.length; j++) {
newTB.setValue(localRowIdx, (i+1), rowData[j]);
localRowIdx++;
}
}
return newTB;
}
Source and credit:
http://captaindanko.blogspot.sg/2013/05/transpose-of-google-visualization-data.html
Example:
https://bitbucket.org/cptdanko/blog-code/src/0666cdce533db48cd89a4e2f02ef7e87a891c857/transpose.html?at=default
A neater and more efficient version with use of the getDate function on the first column label.
Here's a nice and verbose edition commented to explain what's going on -
function transposeDateDataTable (dataTable) {
// Create new datatable
var newDataTable = new google.visualization.DataTable ();
// Add first column from original datatable
newDataTable.addColumn ('string', dataTable.getColumnLabel (0));
// Convert column labels to row labels
for (var x=1; x < dataTable.getNumberOfColumns (); x++) {
var label = dataTable.getColumnLabel (x);
newDataTable.addRow ([label]);
}
// Convert row labels and data to columns
for (var x=0; x < dataTable.getNumberOfRows (); x++) {
newDataTable.addColumn ('number', dataTable.getValue (x, 0).getDate ()); // Use first column date as label
for (var y=1; y < dataTable.getNumberOfColumns (); y++) {
newDataTable.setValue (y-1, x+1, dataTable.getValue (x, y));
}
}
return newDataTable;
}
Or the nice and compact version...
function transposeDateDataTable (dt) {
var ndt = new google.visualization.DataTable;
ndt.addColumn ('string',dt.getColumnLabel(0));
for (var x=1; x<dt.getNumberOfColumns(); x++)
ndt.addRow ([dt.getColumnLabel(x)]);
for (var x=0; x<dt.getNumberOfRows(); x++) {
ndt.addColumn ('number', dt.getValue(x,0).getDate());
for (var y=1; y<dt.getNumberOfColumns(); y++)
ndt.setValue (y-1, x+1, dt.getValue (x,y));
}
return ndt;
}