Google Charts: manually set focus on column of data in Column Chart - google-visualization

I am making a custom legend for a Google Charts ColumnChart. I would like it to have the same behavior as the native legend. The native legend has behavior on click and mouseover. When a legend key is clicked, the column of values is selected. I can do this in my custom legend by calling
myChartWrapper.getChart().setSelection([{column: 4}]);
When a legend key is moused over, the column of values gets an outline. I would like to trigger that same outline when mousing over the key in my custom legend.
Is there a way to set that focussed column similar to setting the selection?
I thought I might be able to do it by calling events.trigger(), but I can't get anything to happen at all with that. For example, these don't seem to do anything.
// did nothing:
google.visualization.events.trigger(myChartWrapper, 'select', [{column: 4}]);
// did nothing:
google.visualization.events.trigger(myChartWrapper.getChart(), 'onmouseover', [{column: 4}]);

attempt to cause focus appearance via event trigger
google.charts.load('current', {
callback: function () {
var dataTable = new google.visualization.DataTable({
cols: [
{label: 'Month', type: 'string'},
{label: 'Amount', type: 'number'}
],
rows: [
{c:[{v: 'April'}, {v: 279899811.34}]},
{c:[{v: 'May'}, {v: 205855811}]},
{c:[{v: 'June'}, {v: 10009811}]},
{c:[{v: 'July'}, {v: 79979811}]},
{c:[{v: 'August'}, {v: 175789911}]},
{c:[{v: 'September'}, {v: 99899811}]},
{c:[{v: 'October'}, {v: 149899811}]},
{c:[{v: 'November'}, {v: 80899811}]},
{c:[{v: 'December'}, {v: 60899811}]},
{c:[{v: 'January'}, {v: 225899811}]},
{c:[{v: 'February'}, {v: 148899811}]},
{c:[{v: 'March'}, {v: 150899811}]}
]
});
var chartWrapper = new google.visualization.ChartWrapper({
chartType: 'ColumnChart',
containerId: 'chart_div',
dataTable: dataTable,
options: {
legend: {
position: 'bottom'
},
vAxis: {
format: 'short'
}
}
});
google.visualization.events.addOneTimeListener(chartWrapper, 'ready', function () {
// mouseover for custom div
document.getElementById('hover_div').addEventListener('mouseover', function () {
// trigger onmouseover for chart, pass props
google.visualization.events.trigger(chartWrapper.getChart(), 'onmouseover', {
'column': 1,
'row': null,
'test': 'over'
});
}, false);
// mouseout
document.getElementById('hover_div').addEventListener('mouseout', function () {
google.visualization.events.trigger(chartWrapper.getChart(), 'onmouseout', {
'column': 1,
'row': null,
'test': 'out'
});
}, false);
// chart event listeners
google.visualization.events.addListener(chartWrapper.getChart(), 'onmouseover', function (props) {
document.getElementById('msg_div').innerHTML = JSON.stringify(props);
});
google.visualization.events.addListener(chartWrapper.getChart(), 'onmouseout', function (props) {
document.getElementById('msg_div').innerHTML = JSON.stringify(props);
});
});
chartWrapper.draw();
},
packages:['controls', 'corechart']
});
#hover_div {
background-color: magenta;
border: 1px solid lime;
color: cyan;
height: 200px;
text-align: center;
width: 200px;
}
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart_div"></div>
<div id="hover_div">HOVER THIS DIV</div>
<div id="msg_div"></div>

Here's a hacky way to highlight bars in a bar chart. It's not a proper solution because it doesn't trigger the chart's native highlight. Instead, it's a way to create your own highlight by manipulating the stroke of the rects with jQuery. I approximated the highlight by setting the stroke to be 2px and grey. The actual highlight appears to be a blur effect with svg. To enhance the highlighting, I applied a slight transparency to the rects themselves.
On mouseover, I get the set of rects for all the bars, and then the subset for the particular column. It turned out to be tricky to get the right set of rects on mouseover, because the user might have selected a bar. When you select a bar with the mouse (i.e., click on it), the rect for it moves around, so you have to select the rects anew each time you mouseover. Plus selecting adds another rect for the white outline, which needs to be filtered out.
For the custom legend, I used colors from the Google Charts palette, which you get here.
I didn't add the part that would trigger a selection on clicking the legend. For that, I will follow this method.
The solution also only works with bar charts. You'd have to do something else with line charts, or other types. So the value of this solution may be limited.
google.charts.load('current', {packages: ['corechart']});
google.charts.setOnLoadCallback(draw_chart);
function draw_chart() {
// Create DataTable object from DataTable constructor or arrayToDatable()
var data = new google.visualization.DataTable({"rows":[{"c":[{"v":"Sacramento"},{"v":97},{"v":79},{"v":67},{"v":100}]},{"c":[{"v":"Montpelier"},{"v":96},{"v":74},{"v":32},{"v":96}]},{"c":[{"v":"Juneau"},{"v":24},{"v":44},{"v":54},{"v":64}]},{"c":[{"v":"Montgomery"},{"v":26},{"v":69},{"v":51},{"v":56}]},{"c":[{"v":"Little Rock"},{"v":87},{"v":69},{"v":78},{"v":41}]}],"cols":[{"type":"string","id":"cities","label":"cities"},{"type":"number","id":"A","label":"A"},{"type":"number","id":"B","label":"B"},{"type":"number","id":"C","label":"C"},{"type":"number","id":"D","label":"D"}]});
// Add formatters, if any
// Create ChartWrapper
var my_chart = new google.visualization.ChartWrapper({
"containerId": "chart_id",
"dataTable": data,
"chartType": "ColumnChart",
"options": {"bar": {"groupWidth": 67}, "chartArea": {"width": 440, "top": 20, "height": 295, "left": 60}, "height": 375, "width": 500, "fontSize": 12, "legend": "none"}
});
var bar_rect_set;
var num_rows = 5;
var num_cols = 4;
var num_series = num_rows * num_cols;
var parent_g;
function get_bar_rect_set() {
// get all the rects in the parent except for the white outline rects
// on selected bars, if any.
bar_rect_set = parent_g.find('rect[fill!="#ffffff"]');
}
google.visualization.events.addOneTimeListener(my_chart, 'ready', function () {
// Get an initial collection of the bar rects, along with their parent.
// Hereafter, get the bar rects with the method above.
// get all rects three layers down, including gridlines and axis
var g_set_1 = $("svg g g g rect");
// slice out the gridlines at the beginning and the axis line at the end
bar_rect_set = g_set_1.slice(g_set_1.length - num_series - 1, g_set_1.length - 1);
parent_g = $(bar_rect_set[0]).parent();
});
my_chart.draw();
function highlight_bars(series_num) {
if (series_num > num_cols - 1) {
return false;
}
get_bar_rect_set();
var start_index = series_num * num_rows;
var end_index = start_index + num_rows;
var series_g_set = bar_rect_set.slice(start_index, end_index)
var styles = {'stroke-width': "1.5px", "stroke": "#AAAAAA", "opacity": .8};
series_g_set.css(styles);
}
function remove_highlight() {
var styles = {'stroke-width': "0", "opacity": 1};
bar_rect_set.css(styles);
}
$("#legend tr").each(function(index, row) {
$(row).mouseover(function() {
highlight_bars(index);
}).mouseout(function() {
remove_highlight();
});
});
}
.color_bar {
width:24px;
height:12px;
margin-right:5px;
vertical-align:middle;
}
#legend td {
font-size:12;
height:19px;
font-family:"Arial";
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://www.gstatic.com/charts/loader.js"></script>
<table id="legend">
<tr>
<td><div class='color_bar' style="background-color:#3366cc"></div></td>
<td>A</td>
</tr>
<tr>
<td><div class='color_bar' style="background-color:#dc3912"></div></td>
<td>B</td>
</tr>
<tr>
<td><div class='color_bar' style="background-color:#ff9900"></div></td>
<td>C</td>
</tr>
<tr>
<td><div class='color_bar' style="background-color:#109618"></div></td>
<td>D</td>
</tr>
</table>
<div id="chart_id" ></div>

Related

NumberRangeFilter behaves strangely with decimals in Google Charts

I'm having trouble getting the numberRangeFilter control for google charts to play nicely with decimals. To illustrate the problem, I've modified the example from the docs to have five data points, some with decimals. I've also set the step property of the numberRangeFilter to move by increments of 0.1.
var control = new google.visualization.ControlWrapper({
'controlType': 'NumberRangeFilter',
'containerId': 'numberRangeFilter_control_div',
'options': {
'filterColumnIndex': 1,
'ui':{
'showRangeValues':true,
'step':0.1
}
}
});
var data = google.visualization.arrayToDataTable([
['Name', 'Age'],
['Robert', 7.0],
['John', 7.5],
['Aaron', 8.0],
['Aaron', 8.3],
['Jessica', 8.7]
]);
I would expect the slider to start with all data included, move in increments of 0.1, and filter out datapoints as the range is adjusted.
However I'm seeing two unexpected behaviors instead:
1) The filter range labels don't update properly as I move the max range slider. The max range label reads 8.7 even after I slide it three steps to the left. It filters out data as expected, but the labels don't update, so it continues to read 8.7.image of slider label behavior
2)The label on the minimum of the range does not include the lowest data point, despite displaying that bar on the graph:
image of nonsensical range
Has anyone seen this before dealing with decimals in google charts? Am I doing something wrong?
Full script below:
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<div id="numberRangeFilter_dashboard_div" style="border: 1px solid #ccc">
<p> </p>
<table class="columns">
<tr>
<td>
<div id="numberRangeFilter_control_div" style="padding-left: 2em"></div>
</td>
<td>
<div id="numberRangeFilter_chart_div"></div>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
google.charts.load('current', {packages:['corechart', 'table', 'gauge', 'controls']});
google.charts.setOnLoadCallback(drawNumberRangeFilter);
function drawNumberRangeFilter() {
var dashboard = new google.visualization.Dashboard(
document.getElementById('numberRangeFilter_dashboard_div'));
var control = new google.visualization.ControlWrapper({
'controlType': 'NumberRangeFilter',
'containerId': 'numberRangeFilter_control_div',
'options': {
'filterColumnIndex': 1,
'ui':{
'showRangeValues':true,
'step':0.1
}
}
});
var data = google.visualization.arrayToDataTable([
['Name', 'Age'],
['Robert', 7.0],
['John', 7.5],
['Aaron', 8.0],
['Aaron', 8.3],
['Jessica', 8.7]
]);
var chart = new google.visualization.ChartWrapper({
'chartType': 'BarChart',
'containerId': 'numberRangeFilter_chart_div',
'options': {
'width': 400,
'height': 300,
'hAxis': {'minValue': 0, 'maxValue': 60},
'chartArea': {top: 0, right: 0, bottom: 0}
}
});
dashboard.bind(control, chart);
dashboard.draw(data);
}
</script>
Workaround: display the true selected range of the numberRangeFilter controller on the page using statechange events, and modifying html on page each time the event fires. Use these new text divs instead of the standard numberRangeFilter labels.
Clumsy, but it works for me.
google.visualization.events.addListener(control, 'statechange', selectHandler);
function selectHandler(e) {
var range_selected = control.getState()
document.getElementById("lower_range_label").innerHTML = "Lower Range:"+range_selected.lowValue
document.getElementById("upper_range_label").innerHTML = "Upper Range:"+range_selected.highValue
}

Chartjs Graph is not showing in colorbox

I am trying to display a Doughnut Chart in the popup(colorbox) with the dummy values but it is giving error, but if i simply call it in browser(via url) it display the graph.
IndexSizeError: Index or size is negative or greater than the allowed amount.
var DoughnutChart = [{
value: 60,
color: "#fcc79e"
}, {
value: 30,
color: "#beefd2"
}, {
value: 50,
color: "#ffddfb"
}, {
value: 20,
color: "#cdecff"
}, {
value: 90,
color: "#fff5bc"
}];
var myDoughnutChart = new Chart(document.getElementById("canvas").getContext("2d")).Doughnut(DoughnutChart);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<canvas id="canvas" height="450" width="600"></canvas>
Chart.js needs the canvas to have a rendered size before it can be rendered. So you need to have your chart initialization inside the cbox_complete event hook instead of doing it first and then opening the colorbox
Something like
$(document).bind('cbox_complete', function () {
var DoughnutChart = [{
...
...
var myDoughnutChart = new Chart(document.getElementById("canvas").getContext("2d")).Doughnut(DoughnutChart);
});

How to Suppress Horizontal Scrollbar on Table

I would like a Google Visualization Table with a fixed width and td cells that wrap. When I specify a width in the draw method a horizontal scroll bar appears. Rather than this, I want wrapping of cells.
This jsfiddle is a working example. In this example I would like for the first column to wrap. I have tried adding a class with word-wrap: break-word;. But this does not work.
Here is relevant js:
function drawChart() {
var cssClassNames = {
'tableCell': 'googlecell'
};
var datatable = new google.visualization.DataTable();
datatable.addColumn('string', 'Keyword', 'col1');
datatable.addColumn('number', 'Pageviews');
datatable.addColumn('number', 'Secs.');
datatable.addRows(3);
datatable.setCell(0, 0, '(not set)');
datatable.setCell(0, 1, 20308);
datatable.setCell(0, 2, 68.74);
datatable.setCell(1, 0, '(not provided)');
datatable.setCell(1, 1, 14410);
datatable.setCell(1, 2, 49.99);
datatable.setCell(2, 0, 'product_type_l1==sdsssijven&+product_type_l2==*');
datatable.setCell(2, 1, 3838);
datatable.setCell(2, 2, 48.35);
var table = new google.visualization.Table(document.getElementById('table_div'));
table.draw(datatable, {
showRowNumber: false,
'allowHtml': true,
'cssClassNames': cssClassNames,
width: 400
});
}
google.load("visualization", "1", {
packages: ["corechart", "table"]
});
google.setOnLoadCallback(drawChart);
And relevant css:
.googlecell {
word-wrap: break-word;
}
What about:
.googlecell {
word-break: break-all;
}
Is that what you are after? (demo)

Drawing visual Lines in Google Charts

I'm writing a Google Chart. It has stacked columns. On top of that I want to draw 2 lines, which indicate min and max allowed value.
The only solution I came up with, was modifying the first example of ComboCharts. My result looks like this:
Which isn't sufficient. The graph is variable, so if there's only 1 Quartal shown, the line will solely be a dot. My Questions are:
Is there a way to draw the line further, so it hits the left and right boundary of the Graph?
Can I draw markup lines into the graph, without pretending it's another datapoint?
You can fiddle with a ComboChart here if you want.
You can't get the lines to go edge-to-edge with a discrete (string-based) x-axis. If you switch to a continuous (number, date, datetime, timeofday) axis, then you can add one row before your real data and one row after that contain the goal lines (and nulls for the other data series):
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Quarter');
data.addColumn('number', 'Value 1');
data.addColumn('number', 'Value 2');
data.addColumn('number', 'Value 3');
data.addColumn('number', 'Goal 1');
data.addColumn('number', 'Goal 2');
data.addRows([
[0, null, null, null, 10, 14],
[1, 5, 4, 7, null, null],
[2, 6, 9, 6, null, null],
[3, 2, 6, 4, null, null],
[4, 3, 6, 4, null, null],
[5, null, null, null, 10, 14]
]);
var chart = new google.visualization.ComboChart(document.querySelector('#chart_div'));
chart.draw(data, {
height: 400,
width: 600,
isStacked: true,
legend: {
position: 'top'
},
seriesType: 'bars',
interpolateNulls: true,
series: {
3: {
type: 'line'
},
4: {
type: 'line'
}
},
hAxis: {
format: 'Q#',
ticks: [1, 2, 3, 4],
viewWindow: {
min: 0.5,
max: 4.5
}
},
chartArea: {
left: '10%',
width: '80%'
}
});
}
google.load('visualization', '1', {packages:['corechart'], callback: drawChart});
See working example: http://jsfiddle.net/asgallant/W67qU/
Here is some explanation of what is going on (edit on Nov 24, 2022 by Jorr.it):
At the top and bottom of the DataTable there are extra rows added with the goals only. With the hAxis.viewWindow option the two new goal dots are just cut off the chart, but resulting in a full line over the whole width of the chart. Finally option "interpolateNulls" needs to be set to connect the two invisible dots "over" the null values in the bar rows.
Maybe a bit late but I faced the same issue. I was trying to set max and min lines into a line chart with a lot of data points in the serie and I wanted to avoid adding new series with a lot of repeated points so I used overlays ( https://developers.google.com/chart/interactive/docs/overlays#javascript2 ).
Here are an example, It's just a draft in which I'm working now but maybe can help:
<html>
<head>
<script
type="text/javascript"
src="https://www.gstatic.com/charts/loader.js"
></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<style>
#container {
position: relative;
width: 900px;
height: 500px;
}
.min-bar {
height: 1px;
background-color: red;
position: absolute;
left: 0;
right: 0;
}
</style>
<script type="text/javascript">
$(function() {
$.get(
"https://firebasestorage.googleapis.com/v0/b/manasav-pricetracker.appspot.com/o/products%2F-L6O-CtBKZAc2NTCFq7Z.data?alt=media&token=60e06bb6-59b7-41a9-8fd0-f82f4ddc75f2",
function(data) {
google.charts.load("current", { packages: ["corechart"] });
google.charts.setOnLoadCallback(drawChart);
var downloadedData = JSON.parse("[" + data);
function drawChart() {
var dataTable = [["Time", "New"]];
let min = Number.MAX_VALUE;
let rowMin;
for (var i in downloadedData) {
var d = downloadedData[i];
if (d.new < min) {
rowMin = i;
min = d.new;
}
dataTable.push([new Date(d.date), d.new]);
}
var data = google.visualization.arrayToDataTable(dataTable);
var options = {
title: "Price evolution",
legend: { position: "bottom" },
trendlines: { 0: {} }
};
var chart = new google.visualization.LineChart(
document.getElementById("curve_chart")
);
function placeMarker(dataTable) {
var cli = this.getChartLayoutInterface();
var chartArea = cli.getChartAreaBoundingBox();
document.querySelector(".min-bar").style.top =
Math.floor(cli.getYLocation(min)) + "px";
document.querySelector(".min-bar").style.left =
Math.floor(cli.getXLocation(dataTable.getValue(0,0))) - 25 + "px";
document.querySelector(".min-bar").style.right =
(document.querySelector("#container").offsetWidth - Math.floor(cli.getXLocation(dataTable.getValue(dataTable.getNumberOfRows()-1,0)))) - 25 + "px";
// document.querySelector(".min-bar").style.top =
// Math.floor(cli.getXLocation(dataTable.getValue(rowMin, 1))) +
// "px";
}
google.visualization.events.addListener(
chart,
"ready",
placeMarker.bind(chart, data)
);
chart.draw(data, options);
}
}
);
});
</script>
</head>
<body>
<div id="container">
<div id="curve_chart" style="width: 900px; height: 500px"></div>
<div class="min-bar"></div>
</div>
</body>
</html>
Jsfiddle demo => https://jsfiddle.net/jRubia/8z7ao1nh/

ChartWrapper.getChart() returns null

I'm trying to implement clickable chart in dashboard. The user will be redirected to a different webpage based on the segment of pie chart clicked. However, the chartObject is NULL when i use getChart() in the ChartWrapper object. I have no problem pulling out DataTable from ChartWrapper.
I've tried to use 'ready' event before 'select' event in my code but still get NULL. Any advice?
You can try out the code here
// Define a Pie chart 3
pie3 = new google.visualization.ChartWrapper({
'chartType': 'PieChart',
'containerId': 'chart3',
'options': {
//'width': 400,
'height': 300,
'legend': {'position':'right', 'textStyle': {fontSize: 9}},
'title': 'Audit Type',
'chartArea': {'left': 80, 'top': 30, 'right': 0, 'bottom': 0, 'height':300, 'widith':200},
'pieSliceText': 'value',
'slices': {0: {color: '#9fbfdf'}, 1: {color: '#6699cc'}, 2: {color: '#3973ac'}, 3: {color: '#738b28'}, 4: {color: '#a4c639'}, 5: {color: '#bfd774'}},
'fontSize': 11
},
// from the 'data' DataTable.
'view': {'columns': [6]}
});
google.visualization.events.addListener(pie3, 'ready', onReady);
// Create a dashboard
new google.visualization.Dashboard(document.getElementById('dashboard')).
// Establish bindings, declaring the both the slider and the category
// picker will drive both charts.
bind([yearPicker, slider2, categoryPicker], [pie, pie2, pie3]).
// Draw the entire dashboard.
draw(data);
function onReady() {
google.visualization.events.addListener(pie3.getChart(), 'select', handler);
}
function handler() {
chartObject = pie3.getChart().getSelection();
alert(chartObject);
}
}
google.setOnLoadCallback(drawVisualization);
Your primary problem is that you are only passing 1 column of data to each PieChart. The PieCharts expect to have two columns of data: 1 string column for pie slice labels, and 1 number column for pie slice values. You need to add a second column of data to each PieChart.
Aside from that, you need to move your chart code outside the $(document).ready(function() {...}); handler and load version 1 of the API instead of 1.1 (unless you have a specific reason to load the release candidate version). Generally, your code should be organized like this:
$(document).ready(function () {
// do stuff on document ready
});
function drawVisualization () {
// draw dashboard
}
google.load('visualization', '1', {packages: ['controls'], callback: drawVisualization});