We had to make a variant of a drillable chart not using the drilldown.js plugin due to other issues with having a stacking bar chart. I can get the drill down and the drill up working correctly except for the issue of the series.name for our initial 2 series not being reset. On drill we use this code (taken from Fusher's answer here):
function setChart(name, categories, data, color, level, type) {
chartMainLoc.xAxis[0].setCategories(categories);
var dataLen = data.length;
chartMainLoc.series[0].remove();
if (dataLen === 1) {
chartMainLoc.series[0].remove();
}
for (var i = 0; i < dataLen; i++) {
chartMainLoc.addSeries({
type: type,
name: name,
color: colors[i],
data: data[i],
level: level
});
}
}
On drill down this works. On drill up I am left with Series 1 - xxx in the tooltip. What am I missing to make this set the series name correctly back to the original?
Using HC 3.0.10. Live demo.
You are using the same name variable for both options. I guess there should be dataName and dataName2. Something like this: http://jsfiddle.net/8XqMp/15/
So in short:
set name param to be an array when you call setChart()
in setChart() loop over name variable
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'm using a line chart (I think) for my data, and I'm trying to have red, yellow or green dots based upon the value of the data. The problem is, I can't even change the symbols used on the graph!
I'm using data pulled from a database, so I can't simply define the data within a series[] and then define the symbol from there, it's added using the chart.addSeries() function.
I'm sorry if this is a total noob question, I'm a total noob when it comes to JavaScript and Highcharts.
EDIT: For security reasons, I can't post the code.
Answer may not be 100% accurate, but I would do something like this:
// Loop over series and populate chart data
$.each(results.series, function (i, result) {
var series = chart.get(result.id);
//I think I have to do some sort of marker: set here
$.each(result.data, function (i, point) {
var x = point.x, // OR point[0]
y = point.y; // OR point[1]
result.data[i] = {
color: y > 100 ? 'red' : 'blue',
x: x,
y: y
}
});
if (series) {
series.update(result, false);
} else {
chart.addSeries(result, false);
}
});
chart.redraw();
As you can see, here I am adding color property to the point. Right now there is simple logic (value < 100), but you can apply there anything you want to, for example function which will return correct color etc.
Note that I am extracting x and y values. How to get them depends on how your data is formatted. It can be {x: some_valueX, y: some_valueY} or [some_valueX, some_valueY] or even some_valueY only.
Important: if you have a lot of points (1000+), don't forget to increase turboThreshold or disable it.
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 have 2 Series on one graph. Only one series can show at a time but the hidden graph affects the range on the x-axis.
The data is dynamically generated via PHP but here is 2 fiddles to show what I mean:
Fiddle With Changed Scale and Hidden Data
Fiddle With removed Hidden Data and correct scale
This code snippet is to ensure that only one series can be shown at any given time.
events: {
show: function () {
var chart = this.chart,
series = chart.series,
i = series.length,
otherSeries;
var seriesName = this['options']['name'];
chart.yAxis[0].axisTitle.attr({
text: seriesName
});
while (i--) {
otherSeries = series[i];
if (otherSeries != this && otherSeries.visible) {
otherSeries.hide();
}
}
}
I am not sure why the graph with the hidden data shows until 16:00 but the graph without any additional data shows until the last data point at 15:38
It appears that Highcharts is taking into account the pointRange of the series with the largest pointRange (although it is hidden) and displaying the x-axis based on that. The range of your "Calls/Hour" series is 1 hour, so it makes sure that if that series had a point at the very end, it would still have room to show.
I'm not sure if there's any elegant way of solving this, but a bit of a "hack" in your case is to change the pointRange of all series to that of the currently showing one.
My crude implementation of this has three changes to your code:
Your series that are visible: false by default also get pointRange: 1 so they don't disrupt the x-axis range for the only visible series.
When the chart has been created we store the correct point range of each series for future reference, for example with the callback function:
$('#callFrequencyGraph').highcharts({
// Options...
}, function(event) {
var series = this.series;
// Store the correct point ranges
for(var i = 0; i < series.length; i++) {
series[i].update({
historicalPointRange: (series[i].closestPointRange ? series[i].closestPointRange : 3600000)
}, false);
this.redraw();
}
}
Extend your events.legendItemClick function to update all series pointRange to that of the series which will be showing after the click is completed:
legendItemClick: function() {
if(this.visible){
return false;
}
else {
var series = this.chart.series;
for(var i = 0; i < series.length; i++) {
series[i].update({
pointRange: this.options.historicalPointRange
}, false);
}
}
}
See this updated JSFiddle for the result of all these changes.
Edit: jsFiddle Update for bug
I have a spiderweb graph with just the one data series. In the interests of printing I'd like to incorporate the value plotted in the label after the actual label name but I can't seem to work out how to do so.
This example to help explain what I mean is a bar-graph that I found in my search. The code layout is similar and a lot less cluttered than mine and I'm fairly sure a solution on this one would be easily transferable.
labels: {
formatter: function() {
return this.value + ' XX.X';
}
}
JSFiddle Here
So for the X axis I'm trying to replace the "XX.X" with the relevant Y value to get the labels: "Foo 29.9 | Bar 71.5 | Foobar 106.4"
I dug through "this" in the xAxis labels formatter and got as far as finding the Y figures at "this.chart.series[0].yData", but couldn't work out a way to associate these back to each relevant X label, and I thought it seemed the wrong way of going about things anyway.
You can do this after rendering of the chart in the chart.events.load method using setCategories. This code below assumes that each index element on the xAxis has a corresponding data element. If it doesn't then you would need to handle that case.
chart: {
events: {
load: function (event) {
var cats = this.xAxis[0].categories;
var theData = this.series[0].data;
var newCats = [];
for (var i = 0; i < cats.length; i++) {
newCats.push(cats[i] + ' ' + theData[i].y)
}
this.xAxis[0].setCategories(newCats);
}
}
}
Live demo.