chartjs push array to label not working - javascript

Dynamically updating a chartjs chart and creating the labels in an array format (["A","B","C"]). However chartjs doesn't accept a push of the label array unless it is in the format "A","B","C" (without brackets). Anyone else experience this or have I misunderstood? Se code below. Produces this
Instead of this (ok when adding labels as chart.data.labels.push("A","B","C","D")
var chart = new Chart(document.getElementById("element"), {
type: 'bar',
options: {
legend: {
display: false
}
}
});
//PUSH DATA TO GRAPH.
var verserier = [];
var veromslperserie = [];
var stat = seriestat(); //function to retrieve data for labels
$.each(stat, function(i, item) {
verserier.push(i);
veromslperserie.push(item["omsl"]);
});
chart.data.labels.push(verserier); //error occurs here
chart.data.datasets.push({
label: "Omsl",
data: veromslperserie,
backgroundColor: colorarray,
});
chart.update();

When you push outside of the loop you are actually pushing verserier into position [n], which in this case is 0.
If you do not add values again you can do
chart.data.labels = verserier

I don't have the explanation of the "why", but here's a workaround :
var data_array = new Array();
// This doesn't work (but with no error on my side, appart from visually wrong labels)
myChart.data.labels.push(data_array);
// But this works
for(i=0;i<data_array.length;i++)
{
myChart.data.labels.push(data_array[i]);
}

Related

Chart.js scatter chart plot data joins start and finish points together

I'm currently trying to write a short program that captures data on mouse movements, scrolling and keyboard clicks from my laptop. This data is then saved in a firebase real time database and deployed to a simple webpage.
I'm using chart.js to plot this data on the page, but when I plot the data, the first adn last points get connected together (see image). I'm having to use the scatter plot as opposed to the line plot as I want the values to be distributed relatively along the x axis. The line chart.js plot places them with equal spacing (which looks weird for a time series bit of data!).
As this is a time series data set this is clearly quite annoying! Anyone have any ideas how I can stop this from happening??
Any help much appreciated!
//This code is taken from a larger script, I'm fairly sure these are all the relevant parts
//This is a js file which manages 3 chart.js charts on a webpage.
//This function generates the chart template
function chartTemplateCreation(InputLabel)
{
const chartTemplate = {
type: 'scatter',
data: {
datasets: [{
label: InputLabel,
data: [],
//backgroundColor: 'rgba(255, 205, 210, 0.5)'
}]
},
options: {
legend: {
display: false
},
responsive: true,
maintainAspectRatio: false,
scales: {
xAxes: [{
ticks: {
suggestedMin: 7,
suggestedMax: 22
}
}]
}
}
}
return chartTemplate
}
//These values create links to the html positioning of the charts
const blankMouseChart = document.getElementById('mouse-chart').getContext('2d')
const blankKeysChart = document.getElementById('keys-chart').getContext('2d')
const blankScrollChart = document.getElementById('scroll-chart').getContext('2d')
//Charts are created
const mouseChart = new Chart(blankMouseChart,chartTemplateCreation('mouse'))
const keysChart = new Chart(blankKeysChart, chartTemplateCreation('keys'))
const scrollChart = new Chart(blankScrollChart, chartTemplateCreation('scroll'))
//This function is used to add to new data to the chart
function addDataScatter(chart, time, value)
{
//chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) =>
{
dataset.data.push({x:time,y:value})
})
chart.update()
}
//collect the firebase reference
const dataRefTenMin = firebase.database().ref(date + "/10min_culmalative/")
//initially add all the historic values from today
dataRefTenMin.once("value").then(function(snapshot)
{
snapshot.forEach(function(childSnapshot)
{
var childData = childSnapshot.val()
var decTime = childData.decTime
addDataScatter(mouseChart, decTime, childData.mouse)
addDataScatter(scrollChart, decTime, childData.scroll)
addDataScatter(keysChart, decTime, childData.keys)
})
})
//then live update the graph by adding data points every time a child is added to the firebase realtime database
dataRefTenMin.on("child_added", function(data)
{
var newData = data.val()
var decTime = newData.decTime
addDataScatter(mouseChart,newData.decTime, newData.mouse)
addDataScatter(scrollChart,newData.decTime, newData.scroll)
addDataScatter(keysChart,newData.decTime, newData.keys)
})
Ah! Figured out the answer by mistake! Accidently commented out a section of code and it now works. All you have to do is remove the section which was meant to add the historic data. As it turns out, both:
dataRefTenMin.once("value").then(function(snapshot)
{
snapshot.forEach(function(childSnapshot)
{
var childData = childSnapshot.val()
var decTime = childData.decTime
addDataScatter(mouseChart, decTime, childData.mouse)
addDataScatter(scrollChart, decTime, childData.scroll)
addDataScatter(keysChart, decTime, childData.keys)
})
})
and
dataRefTenMin.on("child_added", function(data)
{
var newData = data.val()
var decTime = newData.decTime
addDataScatter(mouseChart,newData.decTime, newData.mouse)
addDataScatter(scrollChart,newData.decTime, newData.scroll)
addDataScatter(keysChart,newData.decTime, newData.keys)
})
add historic data to the chart. Hence the error comes as they plot the data over each other (I think).
If any one else has a similar problem, remove the first one of the two portions of code above! Then the chart will both plot the historic data and update live.

Angular nvD3 Stacked Area Chart Data Series Format

I am trying to use my own data in a nvD3 stacked area chart. The sample data format from the Angular nvD3 site has a format like this:
[{
"key":"Series 1",
"values":[[1025409600000,0],[1028088000000,-6.3382185140371]]
},
{
"key":"Series 2",
"values":[[1025409600000,0],[1028088000000,0]]
}]
I have data coming from my database in this format:
[{
"Loc_Cnt":6,"Num_Cars":552,"Num_Employees":34,"active_month":"2017-10-01T00:00:00"
},
{
"Loc_Cnt":4,"Num_Cars":252,"Num_Employees":14,"active_month":"2017-11-01T00:00:00"
}]
I am trying to graph from my data, three series (Series 1: Flt_Cnt, Series 2: Num_Cars, Series 3: Num_Employees). For each series, the X axis value being the active_month date, and the Y axis value being the series value.
How can I either A) convert my data to look like the sample data easily, or B) use my data as is in the AngularJs nvd3 chart? I feel a .forEach on the array would not be efficient for larger data sets, and not as easy to read. I tried to use d3.nest in some way, but haven't been able to get a correct format. Thanks for your help!
It's not elegant, but I brute forced a way to my solution. If there are any better solutions, please do let me know.
var Loc_Cnt = [];
var Num_Cars = [];
var Num_Employees = [];
var obj = {};
//arr is the array of values in my format
arr.forEach(function (element) {
//need the date in milisecond format
var date = new Date(element.active_month);
var time = date.getTime();
//load corresponding arrays
Loc_Cnt.push([time, element.Loc_Cnt]);
Num_Cars.push([time, element.Num_Cars]);
Num_Employees.push([time, element.Num_Employees]);
});
//load each key/values pair into new object
obj["Loc_Cnt"] = Loc_Cnt;
obj["Num_Cars"] = Num_Cars;
obj["Num_Employees"] = Num_Employees;
//d3.entries creates an object of key/VALUEs
arrRollup = d3.entries(obj);
//change the key word values to value
var i;
for (i = 0; i < arrRollup.length; i++) {
arrRollup[i].values = arrRollup[i]['value'];
delete arrRollup[i].value;
}

Highcharts - Add a pie chart to a line chart

I'm trying to make a combination Line and Pie chart but I'm using 2 Javascript arrays for data inputs as opposed to hard coded values. The syntax I'm using isn't going through. http://www.highcharts.com/demo/combo
The code below is what's currently implemented and I added the pieData. How do I add this to the series so that it shows up as a pie chart in the corner?
Basically instead of hard coding the series, how do you define 2 series for two graphs using two arrays like the ones below?
Kinda-sorta demo
$.each(fuelObj, function (k, i) {
var genData = [];
genData.type = 'line';
genData.name = i.name;
genData.data = i.flow.sort();
genData.visible = i.visible;
genData.color = i.color;
genData.dashStyle = 'Line';
genData.events = {
click: function (event) {
this.hide();
}
};
seriesData.push(genData);
var pie = [];
pie.name = i.name;
pie.y = i.flow[i.flow.length - 1][1];
pie.color = i.color;
pieData.push(pie);
});
series: genData
EDIT:
I'm already using Objects. fuelObj is built like this above the listed code:
fuelObj['gas'] = {name: 'gasoline', yest: [], today: [], color: '#00B050', visible: true};
series typically takes an array of objects.
For example:
series: [
{
type:'column'
name:'series 1'
data:[1,2,3,4,5]
},
{
type:'spline'
name:'series 2',
data[5,6,7,8,9]
}
]
Make sure genData and pieData are objects and then add them to the series array.

Chart.js Bar Chart - grouping and mapping data

I am trying to use Chart.js Bar Chart to display a set of data.
My data is weekly based so to my method I send the year and week and get the data back in 3 columns; Product, Area and Amount.
What I want is to have to Products horizontaly and in each Product I want different bars for each Area and offcourse the Amount verticaly. (Bonus: If an Area nothing in that product it should not be shown in that particular Product)
The problem is that the number of Products and the number of Areas can vary from each week. And I can't seem to find a good way to loop through the data and create the datasets the way chart.js wants.
Also tried using Underscore.js to group it but the fact that the each Area doesn't always have an amount for a spesific product seems to be causing some issues.
So I guess you have to loop through the data and map that data to another predefined array for each Area so it can match this structure somehow??
Also open for other Chart plugins, but really liked how Chart.js animates the data. And if I get this working I can probably figgure out an update method for when you change week.
To get the labels i can f.ex do this:
$.ajax({
....
success: function (d) {
var a = _.groupBy(d.data, function (d) { return d.Product });
var labels = [];
$.each(a, function (i, value) {
labels.push(i);
});
}
});
With data in this format
var myJSONData = [
{
Product: 'P1',
Area: 'A1',
Value: 12
},
...
]
You can use this function to convert it into the format Chart.js requires
var data = {
labels: [],
datasets: []
}
var colors = ['Red','Blue','Green', ...] // add as many colors as there will be areas (maximum)
myJSONData.forEach(function (e) {
// create labels
var labelIndex = data.labels.indexOf(e.Product)
if (labelIndex === -1) {
labelIndex = data.labels.length;
data.labels.push(e.Product);
// dummy entries for each dataset for the label
data.datasets.forEach(function (dataset) {
dataset.data.push(0)
})
}
// get the area dataset
var area = data.datasets.filter(function(area){
return (area.label === e.Area);
})[0]
// otherwise create it
if (area === undefined) {
area = {
label: e.Area,
// create a dummy array with an entry for each of the existing labels
data: data.labels.map(function () {
return 0;
}),
fillColor: colors[data.datasets.length]
};
data.datasets.push(area)
}
// set the value
area.data[labelIndex] = e.Value;
})
and use that to display the chart.
Fiddle - http://jsfiddle.net/jt4Lqkn3/
(Bonus: If an Area nothing in that product it should not be shown in
that particular Product)
You can't change any configuration to do this - there will be a space left for each series.
However you might want to set the strokeColor to a transparent value (e.g. strokeColor: "rgba(0, 0, 0, 0)", just below the fillColor line) and set the barStrokeWidth option to 0, so that 0 values don't show up at all on the chart (otherwise there will be thin line shown)
new Chart(ctx).Bar(data, {
barStrokeWidth: 0,
});

jQuery each going outside of function

I have a jqPlot chart that I want to add links on and I believe I figured out a way to do it using an array such as [[[1,2,"http://google.com"]],[[2,3,"http://yahoo.com]]] however, when I try to load this via XML, jQuery, and Ajax it doesn't quite work.
I believe that the problem lies within the .each clauses found in this code:
function getBars(xml)
{
var categoryid = 1;
var bars = [];
$(xml).find("category").each(
function()
{
bars.push(loadBars(categoryid,$(this)));
categoryid++;
});
return bars;
}
function loadBars(categoryid,xml)
{
var bar = [];
var bars = [];
$(xml).find("bar").each(function()
{
bar.push(parseInt(categoryid));
bar.push(parseInt($(this).attr("size")));
bar.push($(this).attr("link"));
bars.push(bar);
});
$("#debug").append("\nBAR:")
debug2dArray(bars);
return bars;
}
The XML looks like:
<?xml version="1.0"?>
<chart>
<category>
<bar size="20" link="http://google.com"/>
</category>
<category>
<bar size="70" link="http://yahoo.com" />
</category>
</chart>
Here is a jsFiddle
Update
After updating the variables to be non-global, the chart now displays right, but two of the same values are still being added to the array. Code has been updated to reflect changes.
I haven't digested your whole code yet, but one really fatal pitfall you're doing is using variables in your functions that haven't been declared with var (I'm particularly looking at how you've used your bar variable on both functions).
When you use a variable without declaring it with var like you're doing here, you're bringing the variable to a global visibility. That means that that variable is the same variable used (most) everywhere in your code. The same bar in the first function is the same bar in the second.
When your two functions start, the first thing it does is clear the bar variable (i.e. bar = [];). Since they're sharing bar references, calling one function effectively nullifies what the other did.
Is this your intention? If not (or even so), you should declare your variable with var:
var categoryId = 1,
bar = [];
In addition to the lack of var, you are returning variables at the end of the each iterators, instead of the end of the function. Here's a working fiddle: http://jsfiddle.net/fwRSH/1/
function loadBars(categoryid, xml) {
var bar = [];
var bars = [];
$(xml).find("bar").each(function() {
bar.push(parseInt(categoryid, 10));
bar.push(parseInt($(this).attr("size"), 10));
bar.push($(this).attr("link"));
bars.push(bar);
//$("#debug").append("\nBAR:"); //not defined in fiddle, commented out
//debug2dArray(bars); //not defined in fiddle, commented out
});
return bars; //moved from end of "each" iterator to here.
}
function getBars(xml) {
var categoryid = 1;
var bars = [];
$(xml).find("category").each(function() {
bars.push(loadBars(categoryid, $(this)));
categoryid++;
});
return bars;
}
$(document).ready(function() {
var bars = [];
$("div#barchart").css("background-color", "#F00");
$("div#barchart").css("height", "200px");
$("div#barhcart").css("width", "400px");
//moved for debugging
bars = getBars($('div#xmlDI'));
/* returns:
* [
* [
* [1, 20, "http://google.com"]
* ],
* [
* [2, 70, "http://yahoo.com"]
* ]
* ]
*/
$.jqplot("barchart", bars, {
seriesDefaults: {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
fillToZero: true
}
},
axes: {
// Use a category axis on the x axis and use our custom ticks.
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
ticks: ['one', 'two'],
autoscale: true
},
yaxis: {
autoscale: true
}
}
});
});​
None of your variables are declared using var, particularly the bars array. This causes them to be implicitly global, and you overwrite the variable every time you call loadBars.
I am not sure how you want your graph to look like. Because the data you provide to the graph, in general terms, is 'correctly' displayed. If you would write it in the following way:
[
[[1, 30, "http://google.com"], [2,0,""]],
[[1,0,""],[2, 40, "http://yahoo.com"]]
]
...it would give exactly the same results, the library just assumes that the data which is not provided for a particular series is 0 and this is how it is treated as visible here.
Since you do not like it this way my guess is that you made a formatting error in your data variable, as we can see here the 'gap' is gone.
Therefore, I think that the below is the format you are after:
[[
[1, 30, "http://google.com"],
[2, 40, "http://yahoo.com"]
]]
Additionally, as it goes to clicking on a bar of a bar chart you could find useful the answer to the problem. There you could see how to capture the click and how to open a URL. You would just need to slightly adopt it to your need as I used a global array of URLs.
Code to parse the XML:
var bars = [], cat = 0;
$.ajax({
type: 'GET',
url: 'plotlinks.xml',
dataType: "xml",
cache: true,
success: function(data, textStatus, jqXHR) {
$(data).find("category").each( function() {
var barSet = [cat];
$(this).find("bar").each(function() {
var $elt = $(this);
barSet.push([$elt.attr('size'),$elt.attr('link')]);
});
cat++;
bars.push(barSet);
});
// bars is an array; each element is an array.
// The first element in the inner array is the
// category "index" (0,1,2,...). All other
// elements represent a link for that category.
// Those elements are arrays of [size,url].
alert($.stringifyJSON(bars));
}
});
Resulting json:
[[0,
["20","http://google.com"]
],
[1,
["70","http://yahoo.com"]
]
]

Categories

Resources