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
});
}
Related
How can i add data label in front of each point in solid gauge chart. I am try to achieve something like this
Thanks
You can add custom data labels in render event, for example:
chart: {
...,
events: {
render: function() {
var dataLabels = ['Custom DL1', 'Custom DL2', 'Custom DL3'],
chart = this,
series = chart.series;
dataLabels.forEach(function(dl, i) {
if (!series[i].customDL) {
series[i].customDL = chart.renderer.text(dl)
.attr({
zIndex: 10
})
.add(series[i].group);
}
series[i].customDL.translate(
chart.chartWidth / 2 + 10,
chart.plotHeight / 2 - series[i].points[0].shapeArgs.innerR -
(series[i].points[0].shapeArgs.r - series[i].points[0].shapeArgs.innerR) / 2
);
});
}
}
}
Live demo: https://jsfiddle.net/BlackLabel/c5eq2htg/
API Reference:
https://api.highcharts.com/highcharts/chart.events.render
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#text
Here is the highchart demo of map with pie charts.
US map with pie charts
But this map is only for US. Here is my js fiddle.
World wide map with pie charts
// Add the pies after chart load, optionally with offset and connectors
Highcharts.each(chart.series[0].points, function (state) {
if (!state.id) {
return; // Skip points with no data, if any
}
var pieOffset = state.pieOffset || {},
centerLat = parseFloat(state.properties.latitude),
centerLon = parseFloat(state.properties.longitude);
// Add the pie for this state
chart.addSeries({
type: 'mappie',
name: state.id,
zIndex: 6, // Keep pies above connector lines
sizeFormatter: function () {
var yAxis = this.chart.yAxis[0],
zoomFactor = (yAxis.dataMax - yAxis.dataMin) /
(yAxis.max - yAxis.min);
return Math.max(
this.chart.chartWidth / 45 * zoomFactor, // Min size
this.chart.chartWidth / 11 * zoomFactor * state.sumVotes / maxVotes
);
},
tooltip: {
// Use the state tooltip for the pies as well
pointFormatter: function () {
return state.series.tooltipOptions.pointFormatter.call({
id: state.id,
hoverVotes: this.name,
demVotes: state.demVotes,
repVotes: state.repVotes,
libVotes: state.libVotes,
grnVotes: state.grnVotes,
sumVotes: state.sumVotes
});
}
},
data: [{
name: 'Democrats',
y: state.demVotes,
color: demColor
}, {
name: 'Republicans',
y: state.repVotes,
color: repColor
}, {
name: 'Libertarians',
y: state.libVotes,
color: libColor
}, {
name: 'Green',
y: state.grnVotes,
color: grnColor
}],
plotOptions: {
pie: {
center: [state.plotX+400, state.plotY]
}
}
}, false);
// Draw connector to state center if the pie has been offset
/* if (pieOffset.drawConnector !== false) {
var centerPoint = chart.fromLatLonToPoint({
lat: centerLat,
lon: centerLon
}),
offsetPoint = chart.fromLatLonToPoint({
lat: centerLat + (pieOffset.lat || 0),
lon: centerLon + (pieOffset.lon || 0)
});
chart.series[2].addPoint({
name: state.id,
path: 'M' + offsetPoint.x + ' ' + offsetPoint.y +
'L' + centerPoint.x + ' ' + centerPoint.y
}, false);
} */
});
I want to show map world wide map where each pie represent a country. Everything is working fine but world.js file dont have lat and long values.due to which i am facing positon issue of pie on each location.i tried using plotx and ploty .but it doesnt works. So is there any way to show pie axactly on the country area the way it is working US map?
You can use plotX & plotY properties of the region when it doesn't have the lat & lon defined.
Replace these two pieces of code:
plotOptions: {
pie: {
center: [state.plotX+400, state.plotY]
}
}
// Handle lat/lon support
if (options.center.lat !== undefined) {
var point = chart.fromLatLonToPoint(options.center);
options.center = [
chart.xAxis[0].toPixels(point.x, true),
chart.yAxis[0].toPixels(point.y, true)
];
}
with these ones:
center: {
plotX: state.plotX,
plotY: state.plotY
}
// Replace lat/lon with plotX/plotY
if (options.center.plotX !== undefined) {
options.center = [options.center.plotX, options.center.plotY];
}
By the way: using plotOptions inside series options is not valid. plotOptions should be placed in the top level of the chart options structure. Here's more info on that: https://api.highcharts.com/highcharts/plotOptions
Live demo: https://jsfiddle.net/BlackLabel/k5mdL6vn/
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 } }
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 am developing an asp applicaiton which uses WCF service (.svc not .asmx) to send JSON data to highcharts. It only seems to work if I use ie compatibility view. If I don't I get the message:
Microsoft JScript runtime error: Highcharts error #13: http://www.highcharts.com/errors/13
Error number 13 along with online searching suggests I don't have the proper renter to DIV, but it is present. Any ideas what I can to to make my application work in non-compatibility view?
Thanks!
As requested here is some code. I have two charts placed side by side one deals with "Low" turnover items the other with high. Here is the low item chart thus it renders to a named containerLow.
var lowchart = new Highcharts.Chart({
chart: {
renderTo: 'containerLow'
},
xAxis: {
},
plotOptions: {
series: {
cursor: 'pointer',
point: {
events: {
click: function () {
//ChartClicked(this.x, this.y, this, this.series.name, this.series.color, this.index);
ChartClicked(this, 'Low');
}
}
}
}
},
tooltip: {
formatter: function () {
var txt = "$= <b>" + this.point.x + "</b><br/>"
+ "Wait = <b>" + this.point.y + "</b><br/>"
+ "Other DB Info for future Bow Wave, etc. <b><br/>"
+ "Avg Fill Rate = <b>" + this.point.config.averageFillRate + "</b><br/>"
+ "Average On Hand = <b>" + this.point.config.averageOnHand + "</b><br/>"
+ "Average Workload = <b>" + this.point.config.averageWorkload + "</b><br/>"
return txt;
}
}
}); //end chart
My web service get a JSON string with multiple series in it. On page load I call the web service (twice once for hight and low items) and loop through the series array to plot the multiple curves.
function pageLoad() {
JSONGraphing.GetDataDB(getParameterByName("id"), 2, OnGetDataCompleteLow, OnError);
JSONGraphing.GetDataDB(getParameterByName("id"), 1, OnGetDataCompleteHigh, OnError);
}
Here is an example of one of the call back functions for the low graph.
function OnGetDataCompleteLow(result) {
var webServiceData = eval("(" + result + ")");
for (var i = 0; i < webServiceData.series.length; i++) {
lowchart.addSeries({
data: webServiceData.series[i].data
});
}
lowchart.yAxis[0].setTitle({
text: 'Wait time in Days'
});
lowchart.xAxis[0].setTitle({
text: 'Investment in Dollars'
});
lowchart.setTitle({ text: 'Low Frequency Items' });
}
I am in a bit of a time crunch any and all help greatly appreciated.
To make sure that the container exists you have to run your code after it gets rendered, it can be done by the following ways.
document.ready(function() {
// your code
});
or
<div id="container"> </div>
<script type="type="text/javascript"">
// your code here
</script>
or
(function() {
// your code here
})