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>
Related
I have a Google LineChart where I need to draw the vertical axis lines based on a max value. Here is the scenario:
var options = {
vAxis: {
viewWindow: {
min: 0,
max: verticalAxisMaxValue // for my case it is 789. but could be anything.
},
gridlines: {
count: 10 // or something else
}
}
}
The value of verticalAxisMaxValue is determined before options is declared.
What I need is to draw the vertical axis lines to be drawn up to verticalAxisMaxValue (it could be anything like 789, 858, 560, ...) The problem I am having is the axis lines are being drawn but the line with the highest value never goes up to the verticalAxisMaxValue.
Please see the screenshot.
Here the highest value is 700, but I need to draw a line at 789. And the similar should happen for any verticalAxisMaxValue.
How can I do this?
viewWindow controls the visible range of the axis,
not necessarily the labels displayed on the axis.
to control the labels, you need to supply the ticks option.
the ticks option is an array of values, of the same type as on the axis.
it could be date, number, etc.
in this case, we can use the max value to build our ticks.
you will need to determine how much each label should increment by,
such as 100
here, we set the max, then add a tick for each 100 under the max,
then add the max as well to the ticks.
var verticalAxisMaxValue = 789;
var ticks = [];
for (var i = 0; i < verticalAxisMaxValue; i = i + 100) {
ticks.push(i);
}
ticks.push(verticalAxisMaxValue);
see following working snippet...
google.charts.load('current', {
packages: ['corechart']
}).then(function () {
var data = google.visualization.arrayToDataTable([
['Month', 'GROWTH TARGET'],
['Apr', 145],
['May', 169],
['Jun', 201],
['Jul', 231],
['Aug', 281],
['Sep', 325],
['Oct', 369],
['Nov', 444],
['Dec', 478]
]);
var verticalAxisMaxValue = 789;
var ticks = [];
for (var i = 0; i < verticalAxisMaxValue; i = i + 100) {
ticks.push(i);
}
ticks.push(verticalAxisMaxValue);
var options = {
vAxis: {
ticks: ticks,
viewWindow: {
min: 0,
max: verticalAxisMaxValue
}
}
};
var chart = new google.visualization.LineChart(document.getElementById('chart'));
chart.draw(data, options);
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>
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>
I am using Google Charts - Line Chart to show a weeks trend(x-axis) to Cost(y-axis).
Now if i span the number of weeks in past 9 months, then most of the x-axis labels are hidden because of the space constraint.
I am trying to show an axis label only for the first week of a month and have set blank to the rest.
Is there a way to show all of the 9 labels(first week of each month) for 9 months
to show specific axis labels, use config option --> hAxis.ticks
ticks takes an array of values, each value will be shown as a label.
the value should be the same type as the x-axis values in the data table.
if you are using dates, then the array should be filled with date values.
hAxis: {
ticks: [new Date(2018, 0, 1), new Date(2018, 1, 1), new Date(2018, 2, 1), ...]
}
you can also use object notation to fill the array,
using object notation, you can provide the value (v:) and the formatted value (f:).
hAxis: {
ticks: [{v: new Date(2018, 0, 1), f: '01/01/2018'}, {v: new Date(2018, 1, 1), f: '02/01/2018'}, ...]
}
see following working snippet, the data and ticks are built dynamically...
google.charts.load('current', {
callback: function () {
drawChart();
window.addEventListener('resize', drawChart, false);
},
packages:['corechart']
});
function drawChart() {
var datePattern = 'MM/dd/yyyy';
var formatDate = new google.visualization.DateFormat({
pattern: datePattern
});
var dataTable = new google.visualization.DataTable();
dataTable.addColumn('date', 'X');
dataTable.addColumn('number', 'Value');
var oneDay = (1000 * 60 * 60 * 24);
var startDate = new Date(2018, 0, 1);
var endDate = new Date(2018, 9, 0);
var ticksAxisH = [];
for (var i = startDate.getTime(); i <= endDate.getTime(); i = i + oneDay) {
// set x value
var rowDate = new Date(i);
var xValue = {
v: rowDate,
f: formatDate.formatValue(rowDate)
};
// add tick at beginning of each month
if (rowDate.getDate() === 1) {
ticksAxisH.push(xValue);
}
// set y value (y = 2x + 8)
var yValue = (2 * ((i - startDate.getTime()) / oneDay) + 8);
// add data row
dataTable.addRow([
xValue,
yValue
]);
}
var container = document.getElementById('chart_div');
var chart = new google.visualization.LineChart(container);
chart.draw(dataTable, {
chartArea: {
height: '100%',
width: '100%',
top: 32,
left: 48,
right: 18,
bottom: 32
},
hAxis: {
ticks: ticksAxisH
},
height: 288,
legend: {
position: 'top'
},
width: '100%'
});
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
I would like to know if it's possible to have line chart series names divided in two rows, with a label in addition (Group1 and Group2 from the photo). On the picture below you can see what I want to create.
If not, is it at least possible to divide series names in two rows (without labels)?
Here is the photo:
This is an example from a kendo ui homepage, here you can find the source code: link
I think you would need to play around with the legend width and the legend item visual:
DEMO
legend: {
position: "bottom",
width: 360,
item: {
visual: function (e) {
console.log(e);
var group = "";
var rect = new kendo.geometry.Rect([0, 0], [140, 50]);
if (e.series.name == "1. GOOG (close)"){
group = "Group 1: ";
rect = new kendo.geometry.Rect([0, 0], [200, 50]);
} else if (e.series.name == "3. AMZN (close)"){
group = "Group 2: ";
rect = new kendo.geometry.Rect([0, 0], [200, 50]);
}
var color = e.options.markers.background;
var labelColor = e.options.labels.color;
var layout = new kendo.drawing.Layout(rect, {
spacing: 5,
alignItems: "center"
});
var grplabel = new kendo.drawing.Text(group, [0, 0], {
fill: {
color: "#000"
}
});
var marker = new kendo.drawing.Path({
fill: {
color: color
},
stroke : {
color: color
}
}).moveTo(10, 0).lineTo(10, 10).lineTo(0, 10).lineTo(0, 0).close();
var label = new kendo.drawing.Text(e.series.name, [0, 0], {
fill: {
color: labelColor
}
});
layout.append(grplabel, marker, label);
layout.reflow()
return layout;
}
}
},
In the demo, I am checking the series name and adding the group label to the first and third series. The the legend width is set so that it wraps to a second line. NOTE: there is some trial and error to make this work with your data...
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