Sorry if this question has been asked before but I could not find a similar question that is related to my problem.
The issue I am experiencing is that when exporting to PNG, JPG etc. the series would double up. So if my on screen chart has four series plotted, when it comes to exporting it will have eight series in the legend.
I think the problem is related to 'load' event of the chart is being executed subsequent times when exporting.
$(function () {
Highcharts.setOptions({
global: {
useUTC: false
}
});
function fnFetchData(chart) {
// Just imagine an AJAX request has just been done to get a JSON response
// JSONData = $.getJSON('/FetchSales');
var JSONData
JSONData = {
seriesName: 'Sales 2013',
data: [(Math.random() * 100) + 1, (Math.random() * 100) + 1,
(Math.random() * 100) + 1, (Math.random() * 100) + 1,
(Math.random() * 100) + 1, (Math.random() * 100) + 1,
(Math.random() * 100) + 1, (Math.random() * 100) + 1,
(Math.random() * 100) + 1, (Math.random() * 100) + 1,
(Math.random() * 100) + 1, (Math.random() * 100) + 1,
(Math.random() * 100) + 1]
};
//alert('About to load the chart Data');
chart.addSeries({
name: JSONData.seriesName,
data: JSONData.data,
type: 'spline'
}, true);
chart.redraw(true);
};
// Create the chart
$('#container').highcharts({
chart: {
events: {
load: function () {
fnFetchData(this);
}
}
},
title: {
text: 'Chart Data from load Event'
},
exporting: {
enabled: true,
scale: 2,
filename: 'ChartWithDoubleupSeries'
},
spline: {
animation: false
},
series: []
});
});
I can reproduce the issue, here is an example:
http://jsfiddle.net/FtjJF/
I keep thinking it might be a bug that the 'load' event is being executed when exporting unless this is the standard behaviour and I need to include and extra option.
Versions details:
Highstock JS v1.3.1 (2013-04-09)
Highstock JS v1.3.1 (2013-04-09) Exporting module
Export chart through pragmatically like , make load event null
var chart = $('#chart-container').highcharts();
chart.exportChart(
{type: 'image/png', filename: 'name'},
{chart: { events:null } });
Simple workaround is to use setTimeout():
load: function () {
var chart = this;
setTimeout(function() {
fnFetchData(chart);
}, 1);
}
And for me looks like a bug, reported: https://github.com/highslide-software/highcharts.com/issues/1868
Thanks!
Currently as an alternative solution was to store the series names into an array. When the load event is fired I check the series name to see if it exists in the array. If it does, don't call the addSeries method.
var AvailableSeries = [];
$( chart.series).each(function(key,value){
AvailableSeries.push(value.name);
});
if ($.inArray( 'Sales 2013' ,AvailableSeries)==-1 ) {
chart.addSeries(name:'Sales 2013', data:[234,453,56732,435,4,45,32,232,43]});
};
After a little bit more investigation for a long term solution, I've added this line "options.chart.events = null;" to the exportpting.js script.
The problem with this solution might be a legitimate reason for the chart's events to be executed when the chart is being exported. I can't think of one. May be there should be an option to suppress events when exporting:
{ exporting:{ skipEvents:true } }
Related
I want to have data on the chart with scale 1 hour for point and have navigator data with scale 1 day per point.
At the same time I want to add new points to the chart in real time.
When I run my example:
https://jsfiddle.net/antongrinenko/aj7m2euk/3/
$(function () {
var val = 0;
function generateValue() {
val++;
if (val > 5) {
val = 0;
}
return val;
}
function generateData(backInDays, pointDuration) {
var points = [];
var d = new Date();
d.setDate(d.getDate() - backInDays);
for (var i = d.getTime(); i<new Date().getTime(); i += pointDuration) {
points.push([i, generateValue()]);
}
return points;
};
var weekData = generateData(30, 1000 * 60 * 60 * 24); //1 day per point
var oneDayData = generateData(1, 1000 * 60 * 60); //1 hour per point
$('#chart').highcharts('StockChart', {
chart: {
type: 'line'
},
series: [{
name: 'SomeData',
data: oneDayData
}],
navigator : {
adaptToUpdatedData: false,
series: {
data: weekData
},
},
scrollbar: {
liveRedraw: false
},
yAxis: {
floor: 0
}
});
setInterval(function() {
$('#chart').highcharts().series[0].addPoint([new Date().getTime(), generateValue()]);
}, 2000);
});
all works fine. But when I scroll a litte left and then back to right main chart stops updating with new points.
How can I fix this problem?
try commenting adaptToUpdatedData: false, working well
I have tens of thousands (possibly hundreds of thousands) of points that I need plotted with Highcharts. Is there a way where I can cluster the data on the server, so it will show less than 1000 points, but as you zoom in it will make AJAX calls to the server to get the data for that zoomed region (it would probably run through the same cluster algorithm). How would this interface with the Highcharts API?
There is a highstock demo that does this http://www.highcharts.com/stock/demo/lazy-loading.
But you can do the same thing with highcharts http://jsfiddle.net/RHkgr/
The important bit is the afterSetExtremes function
...
xAxis : {
events : {
afterSetExtremes : afterSetExtremes
},
...
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var url,
currentExtremes = this.getExtremes(),
range = e.max - e.min;
var chart = $('#container').highcharts();
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start='+ Math.round(e.min) +
'&end='+ Math.round(e.max) +'&callback=?', function(data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
Here is an improvement for Barbara's answer,
It registers to the setExtremes event,
to know if this is a reset zoom event.
If it is - it gets the entire dataset,
thus allowing reset zoom to work correctly.
It also allows zooming in both x and y.
http://jsfiddle.net/DktpS/8/
var isReset = false;
...
xAxis: {
events: {
afterSetExtremes : afterSetExtremes,
setExtremes: function (e) {
if (e.max == null || e.min == null) {
isReset = true;
}
else
{
isReset = false;
}
}
},
minRange: 3600 * 1000 // one hour
},
series: [{
data: data,
dataGrouping: {
enabled: false
}
}]
});
});
});
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var url,
currentExtremes = this.getExtremes(),
range = e.max - e.min;
var chart = $('#container').highcharts();
var min = 0;
var max = 1.35e12;
if(!isReset)
{
min = e.min;
max = e.max;
}
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start=' + Math.round(min) +
'&end=' + Math.round(max) + '&callback=?', function (data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
In case when you will not have a limit of points, you can increase turboThreshold paramter.
I am currently trying to put a Highcharts chart inside of a Highslide pop-out (the Highslide pop-out itself comes out of an existing Highcharts line graph). Basically, I want someone to be able to click on a point in the line graph and have a Highslides popout that contains additional charts with further information on that data point.
The code below works - once. After the user clicks on a point and then closes the Highslide popout, the Highcharts graphs no longer show up in the Highslide popout if you click on the point again (the data contained in the "tile" div does continue to appear, however).
What's happening here? Why do the charts only show up when you first click on the point but not on any subsequent clicks?
PS: If there are multiple points in the line graph, clicking on each point once will correctly show the additional charts for that data point. But if you close the Highslide popout and click on the point again, it will not show the Highcharts graphs.
Note: The two functions in the .done call create the Highcharts graphs for the Highslide pop-out
Code (inside series for the existing Highcharts line graph):
click: function ()
{
window.Task_ID = this.Task_ID;
window.Task_Record = this.Task_Record;
hs.htmlExpand(null,
{
pageOrigin:
{
x: this.pageX,
y: this.pageY
},
headingText: "<p style='margin: 0 auto;'> Task: " + this.Task_ID + " for " + this.Company + "</p>",
maincontentText: "<p class='tile'></p>" + "<div class='charts'><p class = 'studentTaskChart' id='studentTaskChart" + this.Task_ID + "'></p>" + "<p class = 'businessTaskChart' id='businessTaskChart" + this.Task_ID + "'></p></div>",
width: 700,
height: 700
}),
hs.Expander.prototype.onAfterExpand(null, {
maincontentText:
$.ajax
({
type: "post",
url: "TrackRecord_Beta/practice.php",
data:
{
"timestamp" : this.x
},
success: function(result)
{
$('.tile').html(result);
}
})
.done(function()
{
createStudentTaskChart();
createBusinessTaskChart();
}),
})
}
The problem is that Highslide each time creates new window (that's why you can have more than one), so you need to change each time container's ID (not create duplicates). For example: http://jsfiddle.net/y4JV5/4/
I know, this is not using AJAX, but idea should be the same:
var counter = 0;
hs.Expander.prototype.onAfterExpand = addChart;
function addChart() {
var chart = $("#hc-test" + counter).highcharts();
if (chart) {
chart.destroy();
}
$("#hc-test" + counter).highcharts({
chart: {
width: 300,
height: 300
},
series: [{
type: 'pie',
data: [Math.random() * 10, Math.random() * 10, Math.random() * 10]
}]
});
}
Click handler:
click: function () {
counter++;
hs.htmlExpand(null, {
pageOrigin: {
x: this.pageX,
y: this.pageY
},
headingText: this.series.name,
maincontentText: '<div>' + Highcharts.dateFormat('%A, %b %e, %Y', this.x) + ':<br/> ' + this.y + ' visits' + '</div><div id="hc-test' + counter + '"></div>',
width: 310,
height: 500
});
}
I need to display multiple pie charts that are all related to the same data. As such I want them all to be part of the same chart and implement them as individual series.
This all works no problems until I tried to put labels/titles over each individual pie chart. It seems that I can only have a title over the entire group. See jsfiddle
Is there any way to have the titles over each chart?
See above jsfiddle for example
I encountered the same problem, and found this solution via highcharts support forum :
http://highcharts.uservoice.com/forums/55896-general/suggestions/3073133-pie-title
The Highcharts dude has written a plugin that you can see working on the following jsfiddle : http://jsfiddle.net/highcharts/tnSRA/
I have copy-pasted this plugin in a highcharts-plugins.js file that I included in my website, works like a charm!
Here's the plugin code :
/**
* Pie title plugin
* Last revision: 2012-12-21
*/
(function (Highcharts) {
Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'render', function (proceed) {
var chart = this.chart,
center = this.center || (this.yAxis && this.yAxis.center),
titleOption = this.options.title,
box;
proceed.call(this);
if (center && titleOption) {
box = {
x: chart.plotLeft + center[0] - 0.5 * center[2],
y: chart.plotTop + center[1] - 0.5 * center[2],
width: center[2],
height: center[2]
};
if (!this.title) {
this.title = this.chart.renderer.label(titleOption.text)
.css(titleOption.style)
.add()
.align(titleOption, null, box);
} else {
this.title.align(titleOption, null, box);
}
}
});
}(Highcharts));
And this is how you configure your title (put this in your series elements) :
title: {
// align: 'left',
// x: 0
// style: { color: XXX, fontStyle: etc }
text: '<b>Pie 1</b><br>Subtext',
verticalAlign: 'top',
y: -40
},
To further improve on this, the code below correctly handles left, center, and right justification for the titles. See fiddle at http://jsfiddle.net/9y4Lj4yr/ for an example.
/**
* Pie title plugin
* Last revision: 2015-5-20
*/
(function (Highcharts) {
Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'render', function (proceed) {
var chart = this.chart,
center = this.center || (this.yAxis && this.yAxis.center),
titleOption = this.options.title,
box;
proceed.call(this);
if (center && titleOption) {
box = {
x: chart.plotLeft + center[0] - 0.5 * center[2],
y: chart.plotTop + center[1] - 0.5 * center[2],
width: center[2],
height: center[2]
};
if (!this.title) {
this.title = this.chart.renderer.label(titleOption.text)
.css(titleOption.style)
.add()
}
var labelBBox = this.title.getBBox();
if (titleOption.align == "center")
box.x -= labelBBox.width/2;
else if (titleOption.align == "right")
box.x -= labelBBox.width;
this.title.align(titleOption, null, box);
}
});
} (Highcharts));
You can use renderer which allows to add text in any place.
http://api.highcharts.com/highcharts#Renderer.text()
Improving on Vincent's answer. This is how I used it for simple Text titles.
extendHighcharts = function(){
Highcharts.wrap(Highcharts.seriesTypes.pie.prototype, 'render', function (proceed) {
//Clear the title if added previously
//if your chart data does not change, this reference storage for removal can be left out
if(this.chart.subChartTitleElement){
this.chart.subChartTitleElement[this.id].destroy();
}
proceed.call(this);
if (this.center && this.options.title) {
//first, add the title, at center or anywhere, we need the container width to center it
this.chart.subChartTitleElement[this.id] = this.chart.renderer.text(this.options.title.text,this.center[0],this.center[1]);
this.chart.subChartTitleElement[this.id].css(this.options.title.style)
this.chart.subChartTitleElement[this.id].add();
//Center the title using the container(Bounding Box = BBox) width
var xCenter = this.chart.plotLeft + this.center[0] - (this.chart.subChartTitleElement[this.id].getBBox().width/2),
yCenter = this.chart.plotTop + this.center[1] - 0.6 * this.center[2];
this.chart.subChartTitleElement[this.id].attr(
{
x:xCenter,
y:yCenter
}
);
}
});
}
USAGE
this.chart.addSeries({
type: 'pie',
name: 'pie_chart_1',
data: pieChartData,
center: ['80%','25%'],
size: '35%',
showInLegend: false,
dataLabels: {
enabled: false
},
//this part adds the relevant information
title: {
text:'Pie Chart Title',
verticalAlign: 'top',
y: -40
},
id:'a_unique_id_or_name'
});
I've searched through the API and there are lots of examples on disabling and styling both the scroll bar and the navigator. I want to keep both intact but disable the handlebars from being draggable. I tried playing with the styles on this fiddle:
http://jsfiddle.net/gh/get/jquery/1.7.2/highslide-software/highcharts.com/tree/master/samples/stock/navigator/handles/
navigator: {
handles: {
backgroundColor: 'yellow',
borderColor: 'red'
}
}
but only the backgroundColor and borderColor are accessible attributes according to the API.
You can use setExtremes:
http://jsfiddle.net/B7vCR/3/
xAxis: {
minRange:6 * 30 * 24 * 3600 * 1000,
events: {
afterSetExtremes: function(e) {
var maxDistance = 10 * 30 * 24 * 3600 * 1000; //8 months time
var xaxis = this;
if ((e.max - e.min) > maxDistance) {
var min = e.max - maxDistance;
var max = e.max;
window.setTimeout(function() {
xaxis.setExtremes(min, max);
}, 1);
}
}
}
},