I'm working with chart.js and to render a doughnut chart. I want to set the initial chart total value to zero so it can render a full " empty" chart. When I instatiate the chart with zeros it does not render. I cannot find how it handle zeros in the developer documentation.
var kPoints = 000;
var mPoints = 000;
var tPoints = 000;
var cPoints = 000;
var doughnutData = [ {
value : kPoints,
color : "#FF8000"
}, {
value : mPoints,
color : "#99CC00"
}, {
value : tPoints,
color : "#0099CC"
}, {
value : cPoints,
color : "#333333"
}, ];
var ctx = $("#profileChart").get(0).getContext("2d");
var myDoughnut = new Chart(ctx).Doughnut(doughnutData);
From reading the source code for Chart.js I've found that the it tries to sum each of the value fields in its datasource before rendering the chart (see the use of segmentTotal here).
To workaround this, use null for all the values and set one (or more) of the data points to a very small, near zero value. I've used a float notation here for one of the values:
var kPoints = null;
var mPoints = null;
var tPoints = null;
var cPoints = 1e-10;
After that, the example below re-renders the chart (after a 3 second delay) with different data values to show a case of the values updating from the default "dark" chart to a filled out version:
setTimeout(function () {
// Generate a new, random value for each of the data points
doughnutData.forEach(function (item) {
item.value = Math.floor((Math.random() * 10) + 1);
});
var ctx = $("#profileChart").get(0).getContext("2d");
var myDoughnut = new Chart(ctx).Doughnut(doughnutData);
}, 3000);
JSFiddle Example: http://jsfiddle.net/MasterXen/6S9DB/3/
Keep a running total of the values when building the doughnut data.
If there are zero data points, or the total value of all data points is zero, then simply inject an extra dummy point with a label like "No Data" along with an either imperceptible (near-zero) value or a dummy value like 1. In either case, you'll end up with a valid chart with a single category like "No Data".
Related
I am trying to create a bubble chart using the JS HighChart in Angular2+. Whenever there are more than 50 data points (bubbles), the graph breaks. There are the correct number of bubbles in the correct positions (x,y plots) with all different colors but the sizes are all the same even though the z-values are all different. (I am outputing the z-values in a tooltip and the z-values are accurate)
This function is how I am passing in data to the high-chart configuration.
setSeries() {
this.objData = []
this.Data.forEach(element => {
var x= element['xVal'];
var y = element['yVal'];
var z = element['zVal'].toFixed(0);
var name = element['seriesName'].trim();
var newData =[{
x:x,
y:y,
z:+z,
}]
// SetSeriesData is how i am creating the obj to pass into series=[] in highchart configuration
if(i<50) //If I comment this condition, the graph breaks. Right now, the graph is working properly
this.setSeriesData(sumData, name, this.objData)
i++
})
this.options.series = this.objData;
this.generateChart();
}
This is my setSeriesData function.
setSeriesData(graphData: any, dataName: any, objData: any){
var obj = {};
obj['name'] = dataName;
obj['data'] = graphData;
obj['events'] = {click: function(e) {
//takes me to another link
}};
objData.push(obj)
}
In the above function, I configured the chart so that when you click the bubble, it takes you to another page. When the data points >50, this click functionality is not working either. In addition, the fillOpacity is not correct.
Just a few things to point out
1. I am using Angular 2+
2. The discovered issues are, fillOpacity, click, and size based on z-value.
3. It works perfectly when the data points are less than 50
How can I fix this?
I can't seem to find an easy way to add legend which has switchable functionality for items in a single graph for amcharts. I searched around and found a column chart which has switchable graphs (JSFiddle 1). I found switchable items legend but it doesn't resize properly (JSFiddle 2).
This is the closest I can find to adding legends from multiple items of single graph (CodePen 1). It is from amchart website itself but there is no switchable functionality. How can I add the switchable functionality here which allows column resizing (ie. 2 items will be shown with bigger column than 10 columns)? I tried this initially to just see if switch functionality can be added but it does not work:
AmCharts.addInitHandler(function(chart) {
//check if legend is enabled and custom generateFromData property
//is set before running
if (!chart.legend || !chart.legend.enabled || !chart.legend.generateFromData) {
return;
}
var categoryField = chart.categoryField;
var colorField = chart.graphs[0].lineColorField || chart.graphs[0].fillColorsField;
var legendData = chart.dataProvider.map(function(data) {
var markerData = {
"title": data[categoryField] + ": " + data[chart.graphs[0].valueField],
"color": data[colorField]
};
if (!markerData.color) {
markerData.color = chart.graphs[0].lineColor;
}
return markerData;
});
chart.legend.data = legendData;
// here is the code I add
chart.legend.switchable=true;
}
Update - The AmCharts knowledge base demo has been updated to include the modifications below.
In order to resize the chart outright, you have to actually modify the dataProvider and remove the element from the array and redraw the chart. You can use the legend's clickMarker to store the data item into the event dataItem object and retrieve it as needed through the hidden flag. Combining the fiddles from previous solutions together, I came up with this:
/*
Plugin to generate legend markers based on category
and fillColor/lineColor field from the chart data by using
the legend's custom data array. Also allows for toggling markers
and completely removing/adding columns from the chart
The plugin assumes there is only one graph object.
*/
AmCharts.addInitHandler(function(chart) {
//method to handle removing/adding columns when the marker is toggled
function handleCustomMarkerToggle(legendEvent) {
var dataProvider = legendEvent.chart.dataProvider;
var itemIndex; //store the location of the removed item
//Set a custom flag so that the dataUpdated event doesn't fire infinitely, in case you have
//a dataUpdated event of your own
legendEvent.chart.toggleLegend = true;
// The following toggles the markers on and off.
// The only way to "hide" a column and reserved space on the axis is to remove it
// completely from the dataProvider. You'll want to use the hidden flag as a means
// to store/retrieve the object as needed and then sort it back to its original location
// on the chart using the dataIdx property in the init handler
if (undefined !== legendEvent.dataItem.hidden && legendEvent.dataItem.hidden) {
legendEvent.dataItem.hidden = false;
dataProvider.push(legendEvent.dataItem.storedObj);
legendEvent.dataItem.storedObj = undefined;
//re-sort the array by dataIdx so it comes back in the right order.
dataProvider.sort(function(lhs, rhs) {
return lhs.dataIdx - rhs.dataIdx;
});
} else {
// toggle the marker off
legendEvent.dataItem.hidden = true;
//get the index of the data item from the data provider, using the
//dataIdx property.
for (var i = 0; i < dataProvider.length; ++i) {
if (dataProvider[i].dataIdx === legendEvent.dataItem.dataIdx) {
itemIndex = i;
break;
}
}
//store the object into the dataItem
legendEvent.dataItem.storedObj = dataProvider[itemIndex];
//remove it
dataProvider.splice(itemIndex, 1);
}
legendEvent.chart.validateData(); //redraw the chart
}
//check if legend is enabled and custom generateFromData property
//is set before running
if (!chart.legend || !chart.legend.enabled || !chart.legend.generateFromData) {
return;
}
var categoryField = chart.categoryField;
var colorField = chart.graphs[0].lineColorField || chart.graphs[0].fillColorsField;
var legendData = chart.dataProvider.map(function(data, idx) {
var markerData = {
"title": data[categoryField] + ": " + data[chart.graphs[0].valueField],
"color": data[colorField],
"dataIdx": idx
};
if (!markerData.color) {
markerData.color = chart.graphs[0].lineColor;
}
data.dataIdx = idx;
return markerData;
});
chart.legend.data = legendData;
//make the markers toggleable
chart.legend.switchable = true;
chart.legend.addListener("clickMarker", handleCustomMarkerToggle);
}, ["serial"]);
Demo
I'm trying to have my Highstock chart update every minute, by replacing its existing data set with one that is extracted from a url request (which would have different data at different times).
So far I'm not even able to get the data to refresh based on manual changes.
Work so far: https://jsfiddle.net/Lz8gLw8j/
I'm trying to test changing data and then updating it with:
varData = [[0,0],[1,2]]
$('#chart2').highcharts().redraw();
Which does nothing.
You can set the new data to highchart in following way:
var newData = [
[1251763200000, 23.61],
[1251849600000, 23.60],
[1251936000000, 23.79]
]
var chart = $('#chart2').highcharts();
chart.series[0].setData(newData);
$('#chart2').highcharts().redraw();
Updated fiddler:
https://jsfiddle.net/Lz8gLw8j/5/
It looks like there is a load event that you can use
chart : {
events : {
load : function () {
// set up the updating of the chart each second
var series = this.series[0];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], true, true);
}, 1000);
}
}
}
Here's a fiddle: https://jsfiddle.net/jjwilly16/Lz8gLw8j/6/
FYI: I pulled this from an example off of the Highcharts website: http://jsfiddle.net/gh/get/jquery/1.9.1/highslide-software/highcharts.com/tree/master/samples/stock/demo/dynamic-update/
You can add data to the existing series using the addPoint method and replace the dataset using the setData method. The second option argument for both of the methods allows you to redraw the chart. The data should be an array of arrays. The nested array contains the actual data that is plotted. The first elements of the inner, nested array is the epoch (x) while the second is the actual value (y). The epoch is the number of seconds since January 1, 1970 Midnight GMT/UTC. I have updated the fiddle to allow you to add data points as well as reset the data.
The main change are these lines:
$('#updateButton').click(function() {
var date = $('#newDate').val();
var epoch = Math.round(new Date(date).getTime());
var value = parseInt($('#newValue').val());
var chart = $('#chart2').highcharts();
chart.series[0].addPoint([epoch, value], true);
});
$('#resetChart').click(function() {
var chart = $('#chart2').highcharts();
chart.series[0].setData(null, true);
});
https://jsfiddle.net/Lz8gLw8j/7/
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,
});
I am trying to get a radar chart from google api similar to below .I could get everything except the equally distributed 0,0.5,1,1.5,2,2.5,3
In my chart currently it takes min value out of the data given and max value ,and divisions between .its so if 6 is my max value and min value is 3. at very centre of circle it is 3 and then next cud be 3.4,and last extreme edge is 6.What change should i make in below code.to get a distribution ( say 0 to 6) evenly.
function drawVisualization() {
var options = {};
// Chart API chart type 'rs' is radar chart
options.cht = 'r';
// set the line colors
options.colors = ['#00FF00'];
// fill the area under the lines
//options.fill = true;
// create a grid for the chart
// options.chg = '25.0,25.0,4.0,4.0';
// options.chxl: '0:|1|2|3|4|5|6';
var arr = [["165q",1.3333333333333],["160q",6],["161q",6.6666666666667],["162q",7],["163q",8],["164q",5]];
//var pi = '\u03C0';
dataTable = google.visualization.arrayToDataTable(arr, true);
// Treat first row as data as well.
var chart = new google.visualization.ImageChart(document.getElementById('visualization'));
chart.draw(dataTable, options);
}
</script>
Try using the axis-range: chxr parameter with the custom axis labels: chxl parameter:
https://developers.google.com/chart/image/docs/chart_params#axis_range
https://developers.google.com/chart/image/docs/chart_params#axis_labels