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)
Related
To show all bars I have set the horizontal scrolling to chartjs in Ionic angular project , and i have used the DataLabelsPlugin constant for bars label. and while scrolling the datalabels is overlapping with y-axis its not hiding before y-axis like bars.
and also horizontal scroll is not happening smoothly.
graph working fine as a expected output
marked with issue about - after scrolling the datalabels went over the y-axis not hide below y-axis like bars
I have tried to add and used the custom datalabels but same issue i am getting and i didnt find any css or attribute on 'https://www.chartjs.org/docs/latest/' official site or not on any other sites -> to hide the datalabels from over the y-axis.
ts file code:
createBarChart() {
const footer = (tooltipItems) => {
let sum = 0;
tooltipItems.forEach(function(tooltipItem) {
sum += tooltipItem.parsed.y;
});
return this.util.getFormatValue(sum)+'%';
};
const toolLabel = (tooltipItems) => {
return "";
};
const toolTitle = (tooltipItems) => {
var string_to_array = function (str) {
return str.trim().split("#$#$");
};
var ss;
tooltipItems.forEach(function(tooltipItem) {
ss = string_to_array(tooltipItem.label.replace(/(.{40})/g, "$1#$#$"))
});
return ss;
};
let graphSize = Math.max(...this.daywise_occupancy);
if(graphSize == 0){
graphSize =1;
}
const plugin = {
id: 'customCanvasBackgroundColor',
beforeDraw: (chart, args, options) => {
const {ctx} = chart;
ctx.save();
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
};
this.bars = new Chart(this.barchart6.nativeElement, {
type: 'bar',
data: {
labels: this.daywise_date,
datasets: [{
data: this.daywise_occupancy,
backgroundColor: function(context) {
var value = context.dataset.data[context.dataIndex];
return value <= 15 ? '#f95959'
: value > 15 && value <=60 ? '#F5A623'
: '#00ADB5'
},
borderColor: function(context) {
var value = context.dataset.data[context.dataIndex];
return value <= 15 ? '#f95959'
: value > 15 && value <=60 ? '#F5A623'
: '#00ADB5'
},
borderWidth: 1,
barThickness:30,
}]
},
plugins: [DataLabelsPlugin,plugin],
options: {
animations: {
tension: {
duration: 1000,
easing: 'linear',
from: 1,
to: 0,
loop: true
}
},
scales: {
x: {
min:0,
max:5,
ticks : {
maxRotation: 70,
minRotation: 70,
font:{
size:10,
},
callback: function(value : any, index, ticks_array) {
let characterLimit = 12;
let label = this.getLabelForValue(value);
if ( label.length >= characterLimit) {
return label.slice(0, label.length).substring(0, characterLimit -1).trim() + '..';
}
return label;
}
}
},
y: { // defining min and max so hiding the dataset does not change scale range
min: 0,
max: this.loader.getGraphsizeRound(graphSize),
title: { display: true, text: (this.titleSet)? '% of Branches Contribution' : '% of Seat Occupancy' },
beginAtZero: true,
display: true,
position: 'left',
// ticks: {
// stepSize: 6,
// },
}
},
plugins: {
legend: {
display: false
},
datalabels:{
anchor: 'end',
align: 'end',labels: {
value: {
color: '#2C3A45;',
formatter: function (value) {
// return Math.round(value) + '%';
return value + '%';
},
font:{
weight:700,
size:14
}
}
}
},
tooltip: {
callbacks: {
footer:footer,
label: toolLabel,
title:toolTitle
},
displayColors:false
}
}
}
});
this.bars.canvas.addEventListener('touchmove',(eve) => {
this.touchmove(eve,this.bars)
});
this.bars.canvas.addEventListener('touchstart',(eve) => {
this.touchstart(eve)
});
}
touchstart(e)
{
this.startX = e.touches[0].clientX;
this.startY = e.touches[0].clientY;
}
touchmove(e,chart)
{
var deltaX = e.touches[0].clientX - this.startX,
deltaY = e.touches[0].clientY - this.startY;
const dataLength = chart.data.labels.length;
let min = chart.options.scales.x.min;
if(deltaX < 0){
if( chart.options.scales.x.max >= dataLength ){
chart.options.scales.x.min = dataLength - 5;
chart.options.scales.x.max = dataLength;
}else{
chart.options.scales.x.min += 1;
chart.options.scales.x.max += 1;
}
// console.log( chart.options.scales.x.min);
// chart1line.options.scales.y.max = graphSize
}else if(deltaX > 0){
if( chart.options.scales.x.min <= 0 ){
chart.options.scales.x.min = 0;
chart.options.scales.x.max = 4;
}else{
chart.options.scales.x.min -= 1;
chart.options.scales.x.max -= 1;
}
}else{
}
chart.update();
}
HTML code:
<div class="chartWrapper">
<div class="chartAreaWrapper">
<canvas #barchart6 height="190" max-height="190" width="0"></canvas>
</div>
</div>
My expected output
horizontal scroll work smoothly.
after scrolling label should not overlap on y-axis.
I am trying to plot the following sample data using a LineChart with a CategoryFilter to filter on year.
Data table is defined as:
aggData: [Date: date][Team: string][Score: number]
From the aggData table I dynamically calculate the default hAxis ticks as
var hAxisTicks = [];
var dateRange = aggData.getColumnRange(0);
for (var date = dateRange.min; date <= dateRange.max; date = new Date(date.getFullYear(), date.getMonth() + 1)) {
hAxisTicks.push(date);
}
The year picker and the line chart are configured as:
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
}
}
});
I added the following event listener
google.visualization.events.addListener(yearPicker, 'statechange', function () {
google.visualization.events.addOneTimeListener(lineChart, 'ready', getTicks);
});
I need to create/recreate the hAxis ticks everytime the yearPicker changes by calling getTicks
function getTicks() {
var ticks = [];
if (yearPicker.getState().selectedValues.length > 0) {
for (var i = 0; i <= hAxisTicks.length; i = i + 1) {
var date = new Date(hAxisTicks[i]);
if (date.getFullYear() == yearPicker.getState().selectedValues[0]) {
ticks.push(date)
}
}
}
else {
for (var i = 0; i <= hAxisTicks.length; i = i + 1) {
var date = new Date(hAxisTicks[i]);
ticks.push(date);
}
lineChart.setOption('hAxis.ticks', ticks);
lineChart.draw();
}
lineChart.setOption('hAxis.ticks', ticks);
lineChart.draw();
}
Here's what happens at different stages
1- When page first loads the graph looks like (the getTicks function is NOT called) which is correct:
2- When the year is changed to 2019, for example, the hAxis ticks get recalculated (the getTicks function is does get called) and the graph appears to be correct
3- Attempting to go back to the default chart to display all years, an a.getTime is not a function error message appears under the CategoryFilter
4- Any subsequent attempts to change the CategoryFilter to any value throws a ```One or more participants failed to draw()
How can I rectify this behavior?
I realized my iteration over the hAxisTics array was incorrect. I should stop at < hAxisTics.length instead of <= hAxisTics.length and I should recalculate inside the event handler
google.visualization.events.addListener(yearPicker, 'statechange', function () {
var ticks = [];
if (yearPicker.getState().selectedValues.length > 0) {
for (var i = 0; i < hAxisTicks.length; i = i + 1) {
var date = new Date(hAxisTicks[i]);
if (date.getFullYear() == yearPicker.getState().selectedValues[0]) {
ticks.push(date)
}
}
}
else {
for (var i = 0; i < hAxisTicks.length; i = i + 1) {
var date = new Date(hAxisTicks[i]);
ticks.push(date);
}
lineChart.setOption('hAxis.ticks', ticks);
lineChart.draw();
}
lineChart.setOption('hAxis.ticks', ticks);
lineChart.draw();
});
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>
I have a bunch of stock charts rendered with Highstock charting API.
In an attempt to center the scrollbar handle for each chart, I use the following piece of code:
/* ============ Position chart scroll BEGIN ============ */
$(".highcharts-container").each(function () {
var scrollBar = $(this).find('.highcharts-scrollbar');
var scrollBarElms = scrollBar.find('rect');
var scrollBarTrackWidth = $(scrollBarElms[0]).attr("width");
var scrollBarHandleWidth = $(scrollBarElms[1]).attr("width");
var xPos = (scrollBarTrackWidth / 2) - (scrollBarHandleWidth / 2);
$(scrollBarElms[1]).attr("x", xPos);
});
/* ============ Position chart scroll END ============ */
The problem is that the handle and the 3 vertical lines that should 'decorate' it are separated. (You can see the entire thing HERE.)
Any suggestions on how to keep them together?
function getData() {
// generate an array of random data
var data = [],
time = (new Date()).getTime(),
i;
for (i = -999; i <= 0; i = i + 1) {
data.push([
time + i * 1000,
Math.round(Math.random() * 100)]);
}
return data;
}
function getRange(data) {
var l = data.length,
range = l * 0.1, // number of points -> 10%
min = data[Math.round(l / 2 - range / 2)][0],
max = data[Math.round(l / 2 + range / 2)][0];
return {
min: min,
max: max
};
}
/* ============ CHARTS OPTIONS BEGIN ============ */
var options = {
chart: {
zoomType: 'x',
events: {
load: function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime();
var y = Math.round(Math.random() * 100);
series.addPoint([x, y]);
}, 1000);
}
}
},
xAxis: {
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 0
},
title: {
text: null
},
exporting: {
enabled: false
},
// Disable navigator
navigator: {
enabled: false
},
series: [{
name: ''
}]
}
/* ============ CHARTS OPTIONS END ============ */
/* ============ DRAW CHARTS BEGIN ============ */
function renderCharts() {
$('div.chart-container').each(function () {
var chartId = $(this).attr('id');
var chartIdParts = chartId.split('-');
var chartIdentifier = chartIdParts[1];
//Set chart options dinamically
var chartId = "chart" + chartIdentifier;
var chart = $('#' + chartId);
var renderTo = "chartcontainer-" + chartIdentifier;
//Render Charts for each aech container
options.chart.renderTo = renderTo;
options.chart.type = 'line';
options.series[0].data = getData();
var range = getRange(options.series[0].data);
options.xAxis.min = range.min;
options.xAxis.max = range.max;
var chart = new Highcharts.StockChart($.extend(true, {}, options));
});
}
function setChatType() {
// Show types list (piker)
$('.current-type').on('click', function () {
$(this).parents('div.chart-options').find('ul.type ul').addClass('clicked');
});
$('.chart-options ul ul li a').on('click', function () {
//Get piked chart type
var type = $(this).parent('li').attr('data-chart-type');
// For text and Title Capitalization
var textAndTitle = type.replace(/^[a-z]/, function (m) {
return m.toUpperCase()
});
// Show piked type in picker
var currSetClass = 'current-type ' + type;
$(this).parents('.chart-options').find('.current-type')
.text(textAndTitle)
.attr({
class: currSetClass,
title: textAndTitle
});
// Then Hide the types list
$('.chart-options ul ul').removeClass('clicked');
//Identify current chart container by ID
var chartCtnId = $(this).parents('div.chart').find('.chart-container').attr('id');
// Render chart again with new type
options.chart.renderTo = chartCtnId;
options.chart.type = type;
var chart = new Highcharts.StockChart($.extend(true, {}, options));
});
}
/* ============ DRAW CHARTS END ============ */
$(document).ready(function () {
$("article.grid:even").addClass('left')
$("article.grid:odd").addClass('right');
// Draw charts
renderCharts();
// Set/change chart type
setChatType();
});
The entire solution can be found HERE.
Thanks to Paweł Fus for help!
I use Ajax to get data, when I debug with firebug, the result shows highcharts option's data has data. But the chart can't render correctly. The charts background is rended correctely, but there is no chart.
here is my code.
// # author:wang
var chart;
var element;
var chart_type_element;
var y_title_1;
var y_title_2;
var y_title_3;
var date = new Date();
var y = date.getUTCFullYear();
var m = date.getUTCMonth();
var d = date.getUTCDate()-1;
var h = date.getUTCHours();
var minute = date.getUTCMinutes();
/**
* 返回图表的类型
*
*/
function chart_type(element){
var type;
var wind = '风向风速';
var t_h = '温湿度';
if ( element== 'wind' ){
type = wind;
} else if ( element == 't_h') {
type = t_h;
}
return type;
}
/**
*
*return y-axis title
*
*/
function y_title(element, serie){
var title;
if ( element== 'wind' ){
switch (serie){
case 1: title = '风速'; break;
case 2: title = '阵风'; break;
case 3: title = '风向'; break;
}
} else if ( element == 't_h') {
switch (serie){
case 1: title = '温度'; break;
case 2: title = '湿度'; break;
default: title = '';
}
}
return title;
}
function getLocTime(nS) {
return new Date(parseInt(nS)).toLocaleString().replace(/年|月/g, "-").replace(/日/g, " ");
}
/**
* 气压配置选项
*/
var option_p = {
chart: {
renderTo: 'station_curve',
zoomType: 'x'
},
title:{
text:'气压序列图'
},
subtitle: {
text: '信科气象台'
},
xAxis: {
type: 'datetime',
maxZoom: 3600000, // one hour
title: {
text: null
}
},
yAxis: {
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}],
min:980,
max:1040
},
tooltip: {
formatter: function() {
return getLocTime(this.x) +': '+ this.y;
}
},
legend: {
layout: 'vertical',
align: 'left',
x: 220,
verticalAlign: 'top',
y: 30,
floating: true,
backgroundColor: '#FFFFFF'
},
series: [{
name: '海平面气压',
color: '#4572A7',
type: 'line',
pointInterval: 60 * 1000,
pointStart: Date.UTC(y,m,d,h,minute),
marker: {
enabled: false
}
}, {
name: '甲板气压',
type: 'line',
color: '#AA4643',
pointInterval: 60 * 1000,
pointStart: Date.UTC(y,m,d,h,minute),
marker: {
enabled: false
}
}/*, {
name: '3',
color: '#89A54E',
pointInterval: 60 * 1000,
pointStart: Date.UTC(y,m,d,h,minute),
type: 'spline',
marker: {
enabled: false
}
}*/]
};
function draw_curve(platformID,element){
option.series[0].data = [];
option.series[1].data = [];
option_th.series[0].data = [];
option_th.series[1].data = [];
jQuery.getJSON('get_last_3d.php',{platformID:platformID,element:element}, function(data) {
var serie=[];
var serie1=[];
if (element == 'wind_dir'){
$.each(data,function(i,value){
serie[i]=parseInt(value.wd);
});
option.series[0].data = serie.reverse();
} else if (element == 'wind_speed'){
$.each(data,function(i,value){
serie[i]=parseInt(value.ws);
serie1[i]=parseInt(value.ws_max);
});
option_wind_speed.series[0].data = serie.reverse();
option_wind_speed.series[1].data = serie1.reverse();
} else if (element == 't_h') {
$.each(data,function(i,value){
serie[i]=parseInt(value.t);
serie1[i]=parseInt(value.h);
});
option_th.series[0].data = serie.reverse();
option_th.series[1].data = serie1.reverse();
} else if (element == 'p') {
$.each(data,function(i,value){
serie[i]=parseInt(value.sea_p);
serie1[i]=parseInt(value.deck_p);
});
option_p.series[0] = serie.reverse();
option_p.series[1] = serie1.reverse();
} else if (element == 'wave_height') {
$.each(data,function(i,value){
serie[i]=parseInt(value.wave_height);
});
option.series[0].data = serie.reverse();
} else if (element == 'visibility') {
$.each(data,function(i,value){
serie[i]=parseInt(value.visibility);
});
option.series[0].data = serie.reverse();
} else if (element == 'cloudheight') {
$.each(data,function(i,value){
serie[i]=parseInt(value.cloud_height);
});
option.series[0].data = serie.reverse();
}
switch(element){
case 'p' :
chart = new Highcharts.Chart(option_p);
break;
case 't_h':
chart = new Highcharts.Chart(option_th);
break;
case 'wind_speed':
chart = new Highcharts.Chart(option_wind_speed);
break;
default:
chart = new Highcharts.Chart(option);
}
/* old code, will be replaced with switch
if (element == 'p')
chart = new Highcharts.Chart(option_p);
else {
chart = new Highcharts.Chart(option);
}
*/
});
}
$( function(){
draw_curve(105,'t_h');
})//end of jquery function
![the chart][1]
thank you advance
The reason it doesn't work is because you didn't provide the values for y,m,d,h,minute for the Date.UTC(y,m,d,h,minute) in the pointStart property for your series. See working: http://jsfiddle.net/LzfM3/