How to dynamically create ticks (month) in google chart? - charts

Given an aggregated data table that is defined as:
aggData: [Date: date][Team: string][Score: number]
I want to plot the aggregated data with the ability to filter by year. I am using dynamic ticks on the hAxis to avoid the repeating labels problem. However, the label for the custom ticks does not appear.
I want the hAxis to display the months. My hunch is I'm not creating the ticks properly
See images below
var hAxisTicks = [];
var dateRange = aggData.getColumnRange(0);
for (var y = dateRange.min.getFullYear(); y <= dateRange.max.getFullYear(); y = y + 1) {
for(var m = dateRange.min.getMonth(); m <= dateRange.max.getMonth(); m = m + 1){
hAxisTicks.push(new Date(y,m));
}
}
var yearPicker = new google.visualization.ControlWrapper({
controlType: 'CategoryFilter',
containerId: 'categoryFilter_div',
options: {
filterColumnIndex: 0,
ui: {
allowTyping: false,
allowMultiple: false,
label: 'Year:',
labelStacking: 'vertical'
},
useFormattedValue: true
}
});
var lineChart = new google.visualization.ChartWrapper({
chartType: 'LineChart',
containerId: 'chart_div',
options: {
width: 900,
height: 500,
hAxis: {
format: 'MMM',
ticks: hAxisTicks
}
}
});
aggData.sort([{ column: 0 }]);
// draw chart
var dashboard = new google.visualization.Dashboard(document.getElementById('dashboard_div'));
dashboard.bind(yearPicker, lineChart);
dashboard.draw(aggData);
<div id="dashboard_div">
<div id="categoryFilter_div"></div>
<div id="chart_div"></div>
</div>
When specifying the hAxisTicks value, the chart comes out without labels on the hAxis
Without specifying hAxisTicks the chart looks like:
i've logged the data to console using
google.visualization.dataTableToCsv(aggData)
the output is:
"Oct 1, 2019",128,0,0,0
"Nov 1, 2019",152,75,0,0
"Dec 1, 2019",0,0,23,0
"Jan 1, 2020",225,0,0,84

the issue with the for loop is in the month portion.
given the data you provided, the month for the min date = 9 (Oct)
however, the month for the max date = 0 (Jan)
so the month for loop does not run, because 9 > 0
instead, let's use a while loop.
var dateTick = dateRange.min;
while (dateTick.getTime() <= dateRange.max.getTime()) {
hAxisTicks.push(dateTick);
dateTick = new Date(dateTick.getFullYear(), dateTick.getMonth() + 1);
}

Related

Google LineChart - How to draw vertical axis line based on a max value. Lines stops before the max value

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>

Google charts, column chart - how to center column on x-axis label?

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>

Grid line only on data point for scratter points

I need to draw x & y intercepts for all data points in a scratter chart. I went through major and minor grid lines. But it could not be my perfect solution.
Like the image below:
The sample image with x and y intercepts only on data points
You can use the render function of the chart to draw the horizontal and vertical lines on the chart surface. In the following demo, I name the x and y axes so that in the render function I can use the getAxis() method along with slot and range. See DOCS.
DEMO
var data = [[0.67, 5.4], [2.2, 2], [3.1, 3]];
$("#chart").kendoChart({
series: [{
type: "scatter",
data: data,
markers: {size: 16},
}],
yAxis: { name: "value", majorGridLines: {visible: false } },
xAxis: { name: "category", majorGridLines: {visible: false } },
render: function(e){
var chart = e.sender;
var yAxis = chart.getAxis("value");
var xAxis = chart.getAxis("category");
//iterate each point on the chart
for (var i=0; i<data.length; i++){
//vertical line
var valRange = yAxis.range();
var valSlot = yAxis.slot(valRange.min, valRange.max);
var point = data[i];
var catSlot = xAxis.slot(point[0]);
var path = new kendo.drawing.Path({
stroke: {color: "#B3BDBD", width: 1}
}).moveTo(catSlot.origin.x + catSlot.size.width/2, valSlot.origin.y)
.lineTo(catSlot.origin.x + catSlot.size.width/2, valSlot.bottomRight().y);
chart.surface.draw(path);
//horizontal line
var ySlot = yAxis.slot(point[1]);
var xRange = xAxis.range();
var xSlot = xAxis.slot(xRange.min, xRange.max);
var pathH = new kendo.drawing.Path({
stroke: {color: "#B3BDBD", width: 1}
}).moveTo(xSlot.origin.x, ySlot.origin.y + ySlot.size.width/2)
.lineTo(xSlot.bottomRight().x, ySlot.origin.y + ySlot.size.width/2);
chart.surface.draw(pathH);
}
}
});

Google Charts haxis label render

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>

Google Charts: Horizontal Reference Line on Barchart

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>