Creating a stacked budget Vs Actual chart - javascript

I am trying to use chartJS to create a stacked budget with an actual vs budgeted amount.
So I am aiming to create something like this.
I have gotten this far with it.
new Chart(document.getElementById("chartjs-7"), {
"type": "bar",
"data": {
"labels": ["Petrol", "Food", "Kids Clubs", "Alcohol"],
"datasets": [{
"label": "Actual Spend",
"data": [70, 100, 20, 29],
"borderColor": "rgb(63, 191, 63)",
"backgroundColor": "rgba(63, 191, 63)"
}, {
"label": "Budgeted amount",
"data": [60, 150, 20, 30],
"type": "bar",
"fill": false,
"borderColor": "rgb(63, 65, 191)",
"backgroundColor": "rgba(63, 65, 191)"
}]
},
"options": {
"scales": {
"yAxes": [{
"ticks": {
"beginAtZero": true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"></script>
<body>
<canvas id="chartjs-7" class="chartjs" width="770" height="385" style="display: block; width: 770px; height: 385px;"></canvas>
</body>
However mine currently appears like this.
Any ideas how to do this or if it is even possible with graphJS I can't seem to find a way to stack these against each other

You must add stacked: true to xAxes:
"options": {
"scales": {
"xAxes": [{
"stacked": true,
}],
"yAxes": [{
"ticks": {
"beginAtZero": true
}
}]
}
}
Demo:
new Chart(document.getElementById("chartjs-7"), {
"type": "bar",
"data": {
"labels": ["Petrol", "Food", "Kids Clubs", "Alcohol"],
"datasets": [{
"label": "Actual Spend",
"data": [70, 100, 20, 29],
"borderColor": "rgb(63, 191, 63)",
"backgroundColor": "rgba(63, 191, 63)"
}, {
"label": "Budgeted amount",
"data": [60, 150, 20, 30],
"type": "bar",
"fill": false,
"borderColor": "rgb(63, 65, 191)",
"backgroundColor": "rgba(63, 65, 191)"
}]
},
"options": {
"scales": {
"xAxes": [{
"stacked": true,
}],
"yAxes": [{
//"stacked": true,
"ticks": {
"beginAtZero": true
}
}]
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.3.0/Chart.min.js"></script>
<body>
<canvas id="chartjs-7" class="chartjs" width="770" height="385" style="display: block; width: 770px; height: 385px;"></canvas>
</body>

Related

How to change the entire color of the selected stack bar on a click event in highchart

I am using a combination of stack bar and line chart at the same with the help of HighChart, I want to be able to select a bar(or in my case the entire stack bar as well as the point in the line graph) and change its color when I click on it,
Right now I am able to select a bar and change its color but not able to change the color of both bars and the point of the line which was clicked
Below is the click of the code which I tried from my end, I want to be able to select the entire bar and line when clicked, but I am only able to select one of them
SandBox Link
I tried this sorry that the sandbox link was broken
Highcharts.chart('container', {
"chart": {
"zoomType": "xy",
"height": 320
},
"credits": {
"enabled": false
},
"title": {
"text": "",
"align": "left",
"style": {
"fontFamily": "Poppins, Helvetica, \"sans-serif\"",
"fontWeight": "600"
}
},
"xAxis": {
"title": {
"text": ""
},
"categories": [
"0.01-0.05",
"0.06-0.10",
"0.11-0.15",
"0.16-0.20",
"0.21-0.25",
"0.26-0.30",
"0.31-0.35",
"0.36-0.40",
"0.41-0.45",
"0.46-0.50",
"0.51-0.55",
"0.56-0.60",
"0.61-0.65",
"0.66-0.70",
"0.71-0.75",
"0.76-0.80",
"0.81-0.85",
"0.86-0.90",
"0.91-0.95",
"0.96-1.00"
],
"crosshair": true,
"plotLines": [{
"color": "#A9A9A9",
"width": 3,
"value": 9.5,
"dashStyle": "longdashdot"
}]
},
"yAxis": [{
"title": {
"text": "Count"
},
"min": 0
},
{
"title": {
"text": "y1 count"
},
"opposite": true,
"min": 0
}
],
"tooltip": {
"shared": true
},
plotOptions: {
series: {
stacking: 'normal',
point: {
events: {
click() {
var indexP = this.x,
series = this.series.chart.series;
if (series[0].data[indexP].selected) {
//unselect
series[0].data[indexP].select(false, true);
series[1].data[indexP].select(false, true);
} else {
var i = 0;
var len = series[1].data.length;
for (i = 0; i < len; i++) { //clear all selection
series[0].data[i].select(false, true);
series[1].data[i].select(false, true);
series[2].data[i].select(false, true);
}
//select
series[0].data[indexP].select(true, true);
series[1].data[indexP].select(true, true);
series[2].data[indexP].select(true, true);
}
return false;
}
}
}
}
},
"legend": {
"layout": "horizontal",
"align": "right",
"verticalAlign": "top"
},
"exporting": {
"enabled": false
},
"series": [{
"name": "y1",
"type": "column",
"stack": 1,
"yAxis": 1,
"color": "rgb(217, 141, 39)",
"data": [
1,
13,
24,
25,
34,
74,
104,
66,
104,
121,
111,
89,
87,
97,
93,
92,
100,
147,
250,
567
],
"tooltip": {
"valueSuffix": " "
}
},
{
"name": "y3",
"type": "column",
"stack": 1,
"yAxis": 1,
"color": "rgb(44, 99, 143)",
"data": [
463,
287,
208,
201,
209,
197,
171,
150,
134,
102,
65,
50,
36,
24,
33,
20,
13,
13,
10,
9
],
"tooltip": {
"valueSuffix": ""
}
},
{
"name": "y3",
"type": "line",
"color": "#F1416C",
"data": [
1,
14,
38,
63,
97,
171,
275,
341,
445,
566,
677,
766,
853,
950,
1043,
1135,
1235,
1382,
1632,
2199
],
"tooltip": {
"valueSuffix": " "
}
}
]
})
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>
<div id="container"></div>

Override the default format for some legend items in ChartJS

Wanting to use ChartJS to generate a complex combination graph. All working well with the exception of being able to control the formatting of the legend.
Trying to achieve something like this:
desired chart display
The code I have for this is below. Have tried using the generateLabels function to override but with no success. I'd settle for being able to set the background color of the boxes in the legend to be clear so that the dashed lines are more obvious.
# quickchart-python https://github.com/typpo/quickchart-python
from quickchart import QuickChart
qc = QuickChart()
qc.width = 600
qc.height = 400
# Config can be set as a string or as a nested dict
qc.config = """{
"type": "line",
"data": {
"labels": [
"2015-16",
"2016-17",
"2017-18",
"2018-19",
"2019-20",
"2020-21",
"2021-22",
"2022-23",
"2023-24",
"2024-25",
"2025-26",
"2026-27",
"2027-28",
"2028-29",
"2029-30",
"2030-31",
"2031-32",
"2032-33",
"2033-34",
"2034-35",
"2035-36",
"2036-37",
"2037-38",
"2038-39",
"2039-40",
"2040-41",
"2041-42",
"2042-43",
"2043-44",
"2044-45",
"2045-46",
"2046-47",
"2047-48",
"2048-49"
],
"datasets": [
{
"label": "Emissions (actual)",
"borderColor": "rgb(192,0,0)",
"backgroundColor": "rgb(192,0,0)",
"pointBorderWidth": 0,
"pointBackgroundColor": "rgb(192,0,0)",
"pointStyle":"line",
"pointRadius": 0,
"fill": false,
"data": [
2250,2230,2195,2185,2170,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
],
"yAxisID": "y"
},
{
"label": "Emissions (projected)",
"borderColor": "rgb(192,0,0)",
"borderDash": [4,4],
"backgroundColor": "rgb(0,0,0)",
"pointBorderWidth": 0,
"pointBackgroundColor": "rgb(192,0,0)",
"pointStyle":"line",
"pointRadius": 0,
"fill": false,
"data": [
,,,,2170,2170,2170,2170,2170,2190,2200,2210,2210,2210,2210,2210,2210,2210,
2210,2210,2210,2210,2210,2210,2210,2210,2210,2210,2210,2210,2210,2210,2210,2210
],
"yAxisID": "y"
},
{
"label": "Sequestration (estimated)",
"borderColor": "rgb(0,112,192)",
"backgroundColor": "rgb(0,112,192)",
"pointBorderWidth": 2 ,
"pointBackgroundColor": "rgb(0,112,192)",
"pointStyle":"line",
"pointRadius": 0,
"fill": false,
"data": [
3197,3159,3121,3083,3008,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
],
"yAxisID": "y"
},
{
"label": "Sequestration (projected)",
"borderColor": "rgb(0,112,192)",
"borderDash": [4,4],
"backgroundColor": "rgb(0,112,192)",
"pointBorderWidth": 2,
"pointBackgroundColor": "rgba(0,112,192,0.1)",
"pointStyle":"line",
"pointRadius": 0,
"fill": false,
"data": [
,,,,3008,2970,2932,2894,2856,2818,2780,2742,2704,2666,2628,2590,2552,
2514,2476,2438,2400,2362,2324,2286,2248,2210,2172,2134,2096,2058,2020,
1982,1944,1906
],
"yAxisID": "y"
},
{
"label": "Cumulative balance (actual)",
"borderColor": "rgb(112,173,71)",
"backgroundColor": "rgb(112,173,71)",
"pointBorderWidth": 0,
"pointBackgroundColor": "rgb(112,173,71)",
"pointStyle":"line",
"pointRadius": 0,
"fill": true,
"data": [
947,1876,2802,3700,4538,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
],
"yAxisID": "y1"
},
{
"label": "Cumulative balance (projected)",
"borderColor": "rgba(112,173,71,0.4)",
"backgroundColor": "rgba(112,173,71,0.6)",
"pointBorderWidth": 0,
"pointBackgroundColor": "rgba(112,173,71,0.6)",
"pointStyle":"line",
"pointRadius": 0,
"fill": true,
"data": [
,,,,4538,5338,6100,6824,7510,8138,8718,9250,9744,10200,10618,10998,11340,11644,
11910,12138,12328,12480,12594,12670,12708,12708,12670,12594,12480,12328,12138,
11910,11644,11340
],
"yAxisID": "y1"
}
]
},
"options": {
"stacked": false,
"legend" : {
display: true,
position: 'bottom',
generateLabels(chart) {
return {
fillStyle: "rgb(255,255,255)"
}
}
},
"title": {
"display": false,
"text": "Chart.js Line Chart - Multi Axis"
},
"scales": {
"xAxes":[{
"ticks": {"fontFamily": "Calibri",
"fontSize": 9}
}],
"yAxes": [
{
"id": "y",
"type": "linear",
"display": true,
"position": "left",
"ticks": {
"min": 1000,
"max": 3500,
"stepSize": 500,
"fontFamily": "Calibri",
"fontSize": 9
},
"scaleLabel": {
"display": true,
"labelString": "Emissions and Sequestration - tCO2e per year",
"fontFamily": "Calibri",
"fontSize": 11
}
},
{
"id": "y1",
"type": "linear",
"display": true,
"position": "right",
"gridLines": {
"drawOnChartArea": false
},
"ticks": {
"min": 0,
"max": 14000,
"stepSize": 2000,
"fontFamily": "Calibri",
"fontSize": 9
},
"scaleLabel": {
"display": true,
"labelString": "Cumulative carbon balance - tCO2e",
"fontFamily": "Calibri",
"fontSize": 11
}
}
]
}
}
}"""
# You can get the chart URL...
print(qc.get_url())
# Get the image as a variable...
image = qc.get_image()
# Or write the chart to a file
qc.to_file('mychart.png')
This is because you putted the generateLabels function in the root of the legend options while it needs to be nested within the labels key like so:
options: {
legend: {
labels: {
generateLabels: (chart) => {
// Implementation
}
}
}
}
Documentation: https://www.chartjs.org/docs/2.9.4/configuration/legend.html#legend-label-configuration

Chart.js bar graph not centered when there is only 1 item

When there is only one item in a bar graph the bar isn't centered:
https://codepen.io/anon/pen/rKmrpX?editors=1010
HTML:
<canvas id="myChart" width="400" height="400"></canvas>
JS:
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'bar',
data: {
"labels": ["2018-06-01T04:00:00.000Z"],
"datasets": [{
"type": "bar",
"label": "Earnings",
"yAxisID": "left-y-axis",
"borderColor": "rgba(107, 165, 57, 1)",
"backgroundColor": "rgba(107, 165, 57, 0.4)",
"data": [25.77]
}, {
"type": "line",
"label": "Pageviews",
"yAxisID": "right-y-axis",
"backgroundColor": "rgba(70, 152, 203, 0.4)",
"borderColor": "rgba(70, 152, 203, 1)",
"pointBackgroundColor": "rgba(70, 152, 203, 1)",
"pointRadius": 0,
"data": [3426]
}]
},
options: {
"scales": {
"xAxes": [{
"maxBarThickness": 100,
"type": "time",
"offset": true,
"time": {
"unit": "day",
"tooltipFormat": "MMM-D",
"displayFormats": {
"day": "MMM-D",
"month": "MMM"
}
},
"ticks": {
"maxRotation": 0,
"maxTicksLimit": 7,
"autoSkip": true
},
"gridLines": {
"display": false,
"offsetGridLines": true
}
}],
"yAxes": [{
"id": "left-y-axis",
"type": "linear",
"position": "left",
"ticks": {
"beginAtZero": true
},
"scaleLabel": {
"display": true,
"labelString": "Earnings"
}
}, {
"id": "right-y-axis",
"type": "linear",
"position": "right",
"ticks": {
"beginAtZero": true
},
"gridLines": false,
"scaleLabel": {
"display": true,
"labelString": "Pageviews"
}
}]
},
"tooltips": {
"callbacks": {}
}
}
});
Is there a fix for this? Perhaps I'm missing an option that should be passed in?
Using the latest version of Chart.js (2.7.2) here.
Removing most of the xAxes config seams to give the result you are looking for
https://codepen.io/tryggvigy/pen/rKmQpX?editors=1010
"scales": {
"xAxes": [{
"maxBarThickness": 100,
}],

AmCharts serial chart date ordering issue

I have 4 different data sets data1, data2, data3, data4, each data set contain different dates, so date is not in order, hence graph is not displaying according to date
Here is my code
AmCharts.makeChart("chartdiv", {
"type": "serial",
"addClassNames": true,
"startDuration": 0.4,
"theme": "light",
"dataDateFormat": "YYYY-MM-DD",
"trendLines": [],
"applyGapValue": 0,
"graphs": [{
"bullet": "round",
"type": "smoothedLine",
"valueField": "data2",
}, {
"bullet": "round",
"type": "smoothedLine",
"valueField": "data1",
}, {
"bullet": "round",
"type": "smoothedLine",
"valueField": "data3",
}, {
"bullet": "round",
"type": "smoothedLine",
"valueField": "data4",
}],
"guides": [],
"valueAxes": [{
"id": "ValueAxis-1",
}],
"categoryField": "date",
"categoryAxis": {
"parseDates": true,
"equalSpacing": true,
"minorGridEnabled": true,
"gridPosition": "start",
},
"allLabels": [],
"balloon": {
"borderThickness": 3,
"horizontalPadding": 17,
"offsetX": 50,
"offsetY": 8
},
"chartCursor": {
"cursorAlpha": 0,
"cursorPosition": "mouse",
"graphBulletSize": 1,
"zoomable": false
},
"legend": {
"enabled": true,
"useGraphSettings": true,
"position": "top",
},
"dataProvider": [
{
"date": "2017-06-02",
"data1": 202,
}, {
"date": "2017-06-04",
"data1": 420,
}, {
"date": "2017-06-05",
"data1": 910,
}, {
"date": "2017-06-07",
"data1": 60,
},
{
"date": "2017-06-02",
"data2": 110,
}, {
"date": "2017-06-04",
"data2": 920,
}, {
"date": "2017-06-05",
"data2": 320,
}, {
"date": "2017-06-07",
"data2": 355,
},
{
"date": "2017-06-02",
"data3": 80,
}, {
"date": "2017-06-04",
"data3": 350,
}, {
"date": "2017-06-05",
"data3": 710,
}, {
"date": "2017-06-07",
"data3": 710,
},
{
"date": "2017-06-02",
"data4": 580,
}, {
"date": "2017-06-04",
"data4": 510,
}, {
"date": "2017-06-05",
"data4": 702,
}, {
"date": "2017-05-07",
"data4": 940,
}, {
"date": "2017-06-09",
"data4": 940,
},
]
});
html, body {
width: 100%;
height: 100%;
margin: 0px;
}
#chartdiv {
width: 100%;
height: 100%;
}
<script src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script src="//www.amcharts.com/lib/3/serial.js"></script>
<script src="//www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv"></div>
in the output I want chart to be displayed according to ordered date, thank you
You can sort the data like this:
chartData.sort(function(a, b) {
return new Date(b.date) - new Date(a.date);
});
Let's say you do this before passing it to the dataProvider property.
While you do have to sort the data by dates as previously mentioned, it's not enough for AmCharts. You also need to group the data by date as well or your chart will not render or behave correctly, i.e. the array element with the date "2017-06-02" needs to have the "data1", "data2", "data3", "data4" properties in the same element.
Here's one way to group it (assuming your data is in a variable called chartData):
var dataMap = {}; //map to group data by date
var newChartData = []; //new chart data array
chartData.forEach(function(dataItem) {
if (!dataMap[dataItem.date]) {
dataMap[dataItem.date] = {};
}
Object.keys(dataItem).forEach(function(key) { //assign keys to map
if (key !== "date") {
dataMap[dataItem.date][key] = dataItem[key];
dataMap[dataItem.date].date = dataItem.date;
}
});
});
//sort the dates and add the grouped objects to the new array.
Object.keys(dataMap).sort(function(lhs, rhs) {
return new Date(lhs) - new Date(rhs);
}).forEach(function(date) {
newChartData.push(dataMap[date]);
});
Demo:
var chartData = [
{
"date": "2017-06-02",
"data1": 202,
}, {
"date": "2017-06-04",
"data1": 420,
}, {
"date": "2017-06-05",
"data1": 910,
}, {
"date": "2017-06-07",
"data1": 60,
},
{
"date": "2017-06-02",
"data2": 110,
}, {
"date": "2017-06-04",
"data2": 920,
}, {
"date": "2017-06-05",
"data2": 320,
}, {
"date": "2017-06-07",
"data2": 355,
},
{
"date": "2017-06-02",
"data3": 80,
}, {
"date": "2017-06-04",
"data3": 350,
}, {
"date": "2017-06-05",
"data3": 710,
}, {
"date": "2017-06-07",
"data3": 710,
},
{
"date": "2017-06-02",
"data4": 580,
}, {
"date": "2017-06-04",
"data4": 510,
}, {
"date": "2017-06-05",
"data4": 702,
}, {
"date": "2017-05-07",
"data4": 940,
}, {
"date": "2017-06-09",
"data4": 940,
},
];
var dataMap = {}; //map to group data by date
var newChartData = []; //new chart data array
chartData.forEach(function(dataItem) {
if (!dataMap[dataItem.date]) {
dataMap[dataItem.date] = {};
}
Object.keys(dataItem).forEach(function(key) { //assign keys to map
if (key !== "date") {
dataMap[dataItem.date][key] = dataItem[key];
dataMap[dataItem.date].date = dataItem.date;
}
});
});
//sort the dates and add the grouped objects to the new array.
Object.keys(dataMap).sort(function(lhs, rhs) {
return new Date(lhs) - new Date(rhs);
}).forEach(function(date) {
newChartData.push(dataMap[date]);
});
AmCharts.makeChart("chartdiv", {
"type": "serial",
"addClassNames": true,
"startDuration": 0.4,
"theme": "light",
"dataDateFormat": "YYYY-MM-DD",
"trendLines": [],
"applyGapValue": 0,
"graphs": [{
"bullet": "round",
"type": "smoothedLine",
"valueField": "data2",
}, {
"bullet": "round",
"type": "smoothedLine",
"valueField": "data1",
}, {
"bullet": "round",
"type": "smoothedLine",
"valueField": "data3",
}, {
"bullet": "round",
"type": "smoothedLine",
"valueField": "data4",
}],
"guides": [],
"valueAxes": [{
"id": "ValueAxis-1",
}],
"categoryField": "date",
"categoryAxis": {
"parseDates": true,
"equalSpacing": true,
"minorGridEnabled": true,
"gridPosition": "start",
},
"allLabels": [],
"balloon": {
"borderThickness": 3,
"horizontalPadding": 17,
"offsetX": 50,
"offsetY": 8
},
"chartCursor": {
"cursorAlpha": 0,
"cursorPosition": "mouse",
"graphBulletSize": 1,
"zoomable": false
},
"legend": {
"enabled": true,
"useGraphSettings": true,
"position": "top",
},
"dataProvider": newChartData
});
html,
body {
width: 100%;
height: 100%;
margin: 0px;
}
#chartdiv {
width: 100%;
height: 100%;
}
<script src="//www.amcharts.com/lib/3/amcharts.js"></script>
<script src="//www.amcharts.com/lib/3/serial.js"></script>
<script src="//www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv"></div>

How to overcome overlapping series lines in highcharts spline

I have a trend chart implemented using highcharts, the data is loaded via json dynamically. The chart renders properly with the data but however the series lines, data labels and symbols overlap each other at given data points. How do I overcome this?
Find the link to Fiddle - https://jsfiddle.net/ashanwijenayake/ft0ke201/
Screenshot
function renderTrendChart(){
$("#trendChartContainer").highcharts({
title: {
text: 'Indicator Vs Action Trend Chart',
x: -20
},
xAxis: {
categories: trendChartCategoies
},
yAxis: {
minRange: 1,
allowDecimals: false,
min: 0,
title: {
text: 'Cases Identified'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
plotOptions: {
series: {
pointPadding: 10,
groupPadding: 10,
cursor: 'pointer',
}
},
series: [{"name": "Pneumothorax", "data": [ { "y": 1, "marker": { "symbol": "text:" }, "dataLabels": { "x": -10, "y": -2, "enabled": true, "format": "2", "style": { "fontWeight": "bold", "fontSize": "12px" } } },{ "y": 2, "marker": { "symbol": "text:" }, "dataLabels": { "x": -10, "y": -2, "enabled": true, "format": "2", "style": { "fontWeight": "bold", "fontSize": "12px" } } } ], "id": "Pneumothorax" },{"name": "Respiratory", "data": [ 0,{ "y": 1, "marker": { "symbol": "text:" }, "dataLabels": { "x": -10, "y": -2, "enabled": true, "format": "1", "style": { "fontWeight": "bold", "fontSize": "12px" } } } ], "id": "Respiratory" },{"name": "Exsanguination", "data": [ 0,{ "y": 2, "marker": { "symbol": "text:" }, "dataLabels": { "x": -10, "y": -2, "enabled": true, "format": "3", "style": { "fontWeight": "bold", "fontSize": "12px" } } } ], "id": "Exsanguination" }]
});
}

Categories