First time ever working with JS and HighCharts... But I'll try to formulate a question so it'll make sense!
At the moment I'm working with only 4 sources of data, which is incredibly easy to throw right in to highcharts.
The problem is, the 4 aggregated numbers is... well, not very consistent.
The numbers I have atm is: 349531093, 156777100, 572480, 7 and 0.
The first number and the second covers the whole funnel, which makes the plot very unattractive and hard to visually see the values.
(Yeah, yeah - the labels are brilliant, but I want to be able to visually see each section).
I've been reading through the documentation of the funnel plot, but I cannot find a way to limit the section size in any way.
So I tried to play around a bit with the different kind of limits, like:
minSize - The minimum size for a pie in response to auto margins. The pie will try to shrink to make room for data labels in side the
plot area, but only to this size. (which does exactly what it says,
so I'm not sure why I even tried it...)
size - that ofc just changed the size of the whole chart....
series: {
dataLabels: {
enabled: true,
format: '<b>{point.name}</b> ({point.y:,.0f})',
minSize: '10%',
color: 'black',
softConnector: true
},
neckWidth: '50%',
neckHeight: '50%',
minSize: '20%',
//-- Other available options
height: '200'
// width: pixels or percent
}
You can see my horrible attempt here at it here: JSFiddle thingy
So to the actual question: Is it possible to set an minimum limit for the section in the funnel?
Any suggestions or just a simple: "dude, not possible" is appreciated!
Cheers!
Unfortunately this is not supported (good idea to post this on userVoice!)
However I have created simple example that you can preprocess data and still display proper values: https://jsfiddle.net/69eey/2/
$(function () {
var dataEx = [
['Raw Events', 349531093],
['Filtered/Aggregated Events', 156777100],
['Correlated Events', 2792294],
['Use Case Events', 572480],
['Finalized', 0]
],
len = dataEx.length,
sum = 0,
minHeight = 0.05,
data = [],
i;
for(i = 0; i < len; i++){
sum += dataEx[i][1];
}
for(i = 0; i < len; i++){
var t = dataEx[i],
r = t[1] / sum;
data[i] = {
name: t[0],
y: ( r > minHeight ? t[1] : sum * minHeight ),
label: t[1]
}
}
It is only workaround of course. You also need to use formatter for a tooltip to make sure you will display proper values (like for dataLabels).
I took Paweł Fus's great example and extended it to include the tooltip correction. Just add the snippet below:
tooltip: {
formatter: function() {
return '<b>'+ this.key +
'</b> = <b>'+ Highcharts.numberFormat(this.point.label, 0) +'</b>';
}
},
JSFiddle with a working example:
HTML
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/modules/funnel.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="width: 600px; height: 400px; margin: 0 auto"></div>
JavaScript
$(function () {
var dataEx = [
['Raw Events', 349531093],
['Filtered/Aggregated Events', 156777100],
['Correlated Events', 2792294],
['Use Case Events', 572480],
['Finalized', 0]
],
len = dataEx.length,
sum = 0,
minHeight = 0.05,
data = [];
for(var i = 0; i < len; i++){
sum += dataEx[i][1];
}
for(var i = 0; i < len; i++){
var t = dataEx[i],
r = t[1] / sum;
data[i] = {
name: t[0],
y: ( r > minHeight ? t[1] : sum * minHeight ),
label: t[1]
}
}
$('#container').highcharts({
chart: {
type: 'funnel',
marginRight: 100
},
title: {
text: 'SEIM Metrics',
x: -50
},
tooltip: {
//enabled: false
formatter: function() {
return '<b>'+ this.key +
'</b> = <b>'+ Highcharts.numberFormat(this.point.label, 0) +'</b>';
}
},
plotOptions: {
series: {
dataLabels: {
enabled: true,
formatter: function(){
var point = this.point;
console.log(point);
return '<b>' + point.name + '</b> (' + Highcharts.numberFormat(point.label, 0) + ')';
},
minSize: '10%',
color: 'black',
softConnector: true
},
neckWidth: '50%',
neckHeight: '50%',
//-- Other available options
height: '200'
// width: pixels or percent
}
},
legend: {
enabled: false
},
series: [{
name: 'Unique users',
data: data
}]
});
});
You can try normalizing the values first by taking log.
log(349531093)=8.5
log(572480)=5.75
Related
I am using highcharts.js for multiple series column chart and I want to show the shared tooltip on the top of group of column(It should take the highest column length)
So far I have tried this https:JSFiddle.
$(function() {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'column',
plotBorderColor: '#CDCDCD',
plotBorderWidth: 1,
},
xAxis: {
type: "category"
},
yAxis: {
endOnTick: true,
startOnTick: true,
tickInterval: null,
},
tooltip: {
useHTML: true,
borderRadius: 0,
borderWidth: 0,
shadow: false,
followPointer: false,
hideDelay: 0,
shared: true,
enabled: true,
backgroundColor: "none",
positioner: function(labelWidth, labelHeight, point) {
var tooltipX, tooltipY;
var chart = this.chart;
tooltipX = point.plotX + chart.plotLeft - 20;
if (point.negative)
tooltipY = point.plotY + chart.plotTop + 20;
else
tooltipY = point.plotY + chart.plotTop - 30;
return {
x: tooltipX,
y: tooltipY
};
},
formatter: function() {
var templateHtmlString = "";
templateHtmlString += '<div class ="ttContainer">';
$.each(this.points, function(index, item) {
templateHtmlString += '<div class = "ttItem">';
templateHtmlString += item.y;
templateHtmlString += '</div>';
});
templateHtmlString += '</div>';
return templateHtmlString;
}
},
series: [{
"color": "red",
"data": [5,-1,17,9,8,19,-2,8,10],
"name": "ABCD"
}, {
"color": "Green",
"data": [8, -7,2,11,28,14,-3,8,-1],
"name": "XYZ"
}]
});
});
.ttItem {
display: inline-block;
padding: 5px;
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<div id="container" style="height: 400px"></div>
but it doesn't work when height of second column is greater than the first column. Please suggest how to fix this issue.
Finally, I figured out the solution myself.I have updated the JSFiddle.
I have modified the tooltip.positioner function to set the tooltip position.
positioner: function(labelWidth, labelHeight, point) {
var tooltipX, tooltipY;
var chart = this.chart;
var hoverPoints = this.chart.hoverPoints;
var y = 0;
var x = 0;
var totalColumnWidth = 0;
var deltaX = 0;
this.charts.hoverPoints contains the array of point object being hovered in case of multiple series chart.I loop through each point object in hoverpoints array.
plotY- y coordinate value of each column/point
barX - Starting x coordinate of each column/point
pointWidth - Width of each column/point
I have set the x as start of first point and y as the lowest plotY value among all points(lowest plotY means highest column)
$.each(hoverPoints, function(index, hPoint) {
var plotY = Math.ceil(hPoint.plotY);
if (index === 0) {
x = Math.ceil(hPoint.barX);
y = plotY;
}
totalColumnWidth += Math.ceil(hPoint.pointWidth);
if ((plotY > y && point.negative) || (plotY < y && !point.negative)) {
y = plotY;
}
});
delta = (totalColumnWidth - labelWidth) / 2
delta variable used to center align the tooltip.
tooltipX = x + chart.plotLeft + delta;
If column is on positive axis then add labelHeight so that tooltip wont overlap on column.
if (point.negative)
tooltipY = y + chart.plotTop;
else
tooltipY = y + chart.plotTop - labelHeight;
return {
x: tooltipX,
y: tooltipY
};
}
}
Alternatively, you could find the total height of y-axis and compare that with the points in the tooltipPos in hoverPoints.
$('#div_id').find('.highcharts-container').find('.highcharts-root').find('.highcharts-plot-background').attr('height'));
This way you could position each tooltip accordingly.
I am trying to pass an Array as a variable, with Javascript, into a HighCharts graph. The points with their markers are displayed correcty, but there is NO spline connecting the dots (Data 2, Purple).
I tried on the same chart as well, to pass an Array directly with the values, and this time HighCharts is displaying the markers and the splines (Data 1, Red).
For both series, all the line parameters (lineWidth, dashStyle, color) are set up.
I tested it on IE11 and Chrome and FireFox, and the result is the same...
Below is the full code. If anybody already encountered this or, better !, have an idea on how to solve this (have both set of data displayed with markers AND line joining those markers), I would be very very interested ! Thanks a lot !!
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Chart</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript">
$(function () {
var myData = new Array();
for (n=1;n<=10;n++)
{
myData[n]=Math.floor((Math.random() * 10) + 1);
}
var mySeries = [];
for (var i = 0; i < myData.length; i++){
mySeries.push([i,myData[i]]);
}
$('#container').highcharts({
title: {
text: 'Chart',
x: -20 //center
},
xAxis: {
opposite:true,
title: {text: 'Horizontal Axis'},
showFirstLabel: true,
showLastLabel: true,
min: 0, max: 12,
tickInterval: 1,
startOnTick: true,
endOnTick: true,
},
yAxis: {
title: {text: 'Vertical Axis - inverted'},
reversed: true,
showFirstLabel: true,
showLastLabel: true,
min: 0, max: 12,
tickInterval: 1,
startOnTick: true,
endOnTick: true,
},
tooltip: {
valueSuffix: 'Week'
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
borderWidth: 0
},
series: [
{name:'Data 1',data:[0,1,2,3,4,5,6,7,8,9,10],marker:{symbol:'circle'},lineWidth:1,dashStyle:'Solid',color:'#FF0000'},
{name:'Data 2',data:mySeries ,marker:{symbol:'circle'},lineWidth:1,dashStyle:'Solid',color:'#FF00FF'},
]
});
});
</script>
</head>
<body>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="width: 1000px; height: 700px; margin: 0 auto"></div>
</body>
</html>
I believe Highcharts is silently failing to draw the spline because your first value of mySeries is undefined.
With myData you start off at n = 1, so myData[0] is still undefined after you run through your for loop. Then you start off mySeries with i = 0so that first undefined value is added to mySeries. If instead you either start i = 1 or change your myData for loop to i = 0 then it should work.
In other words, do this:
var myData = new Array();
for (var n=0; n<10; n++){
myData[n]=Math.floor((Math.random() * 10) + 1);
}
var mySeries = [];
for (var i = 0; i < myData.length; i++){
mySeries.push([i,myData[i]]);
}
That said, I would condense your data generation to one for loop for efficiency reasons which would also remove the problem:
//define an empty array
var mySeries = [];
for (var i = 0; i < 10; i++){
//create your value
var d = Math.floor((Math.random() * 10) + 1);
//push an array into your array
mySeries.push([i,d]);
}
Considering that my data array is always composed of 4 elements as :
var data = [
{"type":"column","name":"My Label 1","y":38.9500000000003,"color":"#7cb342"},
{"type":"column","name":"My Label 2","y":30,"color":"#7cb342"}, {"type":"column","name":"My Label 3","y":51.85,"color":"#fbc02d"}, {"type":"column","name":"My Label 4","y":55.2999999999997,"color":"#fbc02d"}];
I want to know how to set my data names (data.name) each 45 degrees tick interval to keep them well positioned ?
Here is the example:
http://jsfiddle.net/eento/7rupgxde/4/
It's important for me to display only those labels & keep them inside the global highchart container.
Like this?
dataLabels: {
enabled: true,
formatter: function() {
return this.point.name;
}
}
Example:
http://jsfiddle.net/jlbriggs/7rupgxde/5/
You can use renderer to render those labels, for example, create two methods (one to add labels and one to position them):
function renderLabels(chart) {
var alignments = ['right', 'right', 'left', 'left'];
$.each(chart.series[0].points, function(i, point) {
point.myName = chart.renderer.text(point.name, -9999, -9999).attr({
align: alignments[i],
color: 'black'
}).add();
});
}
function positionLabels(chart, anim) {
var positions = [
// top right label
[chart.plotLeft + chart.plotWidth, chart.plotTop],
// bottom right label
[chart.plotLeft + chart.plotWidth, chart.plotTop + chart.plotHeight],
// bottom left label
[chart.plotLeft, chart.plotTop + chart.plotHeight],
// top left label
[chart.plotLeft, chart.plotTop],
]
$.each(chart.series[0].points, function(i, point) {
if (point.myName) {
point.myName[(anim ? 'animate' : 'attr')]({
x: positions[i][0],
y: positions[i][1],
})
}
});
}
Then use those methods in chart.events:
chart: {
polar: true,
renderTo: 'container',
backgroundColor: null,
events: {
load: function() {
renderLabels(this);
positionLabels(this, false);
},
redraw: function() {
positionLabels(this, true);
}
}
},
And working demo: http://jsfiddle.net/7rupgxde/7/
I need two labels for a column, one above to show the score and one below to show if it is a "test" or a "retest". How do I go about doing it?
I assume complicated calculation is needed, something like this? http://jsfiddle.net/72xym/4/ (but I can't really understand)
I got other diagrams need to achieve this as well. Basically I need to do this:
My code is inside here: http://jsfiddle.net/kscwx139/6/
I did this for the part to add the label:
..., function() {
var i = 0, j = 0, len_i, len_j, self = this, labelBBox, labels=[];
for(i=0; i<this.series.length; i++) {
labels[i] = [];
for(j=0; j<this.series[i].data.length; j++) {
labels[i][j] = this.renderer.label(self.series[i].name)
.css({
width: '100px',
color: '#000000',
fontSize: '12px'
}).attr({
zIndex: 100
}).add();
labelBBox = labels[i][j].getBBox();
labels[i][j].attr({
x: 100, //1) what to put here?
y: 100 //2) what to put here?
});
}
}
}
I would do something like this:
var chart2 = new Highcharts.Chart(options2, function() {
var i = 0, j = 0, len_i, len_j, self = this, labelBBox, labels=[], point;
for(i=0; i<this.series.length; i++) {
labels[i] = [];
for(j=0; j<this.series[i].data.length; j++) {
labels[i][j] = this.renderer.label(self.series[i].name)
.css({
width: '100px',
color: '#000000',
fontSize: '12px'
}).attr({
zIndex: 100,
align: "center"
}).add();
point = this.series[i].data[j];
labelBBox = labels[i][j].getBBox();
labels[i][j].attr({
x: point.plotX + this.plotLeft + point.series.pointXOffset + point.shapeArgs.width / 2,
y: this.plotTop + this.plotHeight - labelBBox.height
});
}
}
});
Demo: http://jsfiddle.net/kscwx139/7/
Note: I would suggest using chart.redraw event to update labels position (label[i][j].animate({ x: ..., y: ... });) when resizing browser, updating data, etc.
Short explanation:
point.plotX - x-position in the plotting area (center of the point's shape)
this.plotLeft - left offset for left yAxis (labels, title)
point.series.pointXOffset - offset for multiple columns in the same category
this.plotTop - the same as plotLeft but from top ;)
this.plotHeight - height of the plotting area
labelBBox.height - height of the label to place it above the xAxis line
The problem is when I have a lot of bars to show. I plot the graph and doesn't show the background or the descriptions but when I scroll down they appear (you can see in the images). It doesn't happen in IE or Firefox.
I detect that when I reduce the size of the graph it doesn't happen, but when I have more data to plot I have to reduce more and for that reason this solution doesn't scale.
It seems to be a chrome bug because i don't have any JavaScript error in console more than
"event.returnValue is deprecated. Please use the standard event.preventDefault() instead. "
This is before scroll down: https://www.dropbox.com/s/0c295g64okupxnc/error%20.jpg
And this, after: https://www.dropbox.com/s/1gj9iyuvzrshs9p/error%202.jpg
That's the function who draws the chart:
/**
* Creates a bar chart
*
* #param title
* #param data
* #param labelX
* #param labelY
*/
_draw_bars: function (title, data, labelX, labelY) {
var values = [];
var min = 0;
var max = 0;
var items = [];
$(data).each(function(){
var n = values.length+1;
if (this[1] > max) {
max = this[1];
}
values.push([[n, this[1]]]);
items.push('<li >' + n + ': ' + this[0] + ' (' + this[1] + ')</li>');
});
if(values.length < 15){
this._show_modal(this._get_html(title, items,800,400),950,450);
}else{
this._show_modal(this._get_html(title, items,800,300),1350,650);
}
var barSize = this._get_bar_size(values.length);
$.jqplot.config.defaultHeight = $(window).height()*0.5;
$.jqplot('chart_div', values, {
legend:{show:false},
title: {show:false},
seriesDefaults:{
renderer:$.jqplot.BarRenderer,
rendererOptions: {
barDirection:'vertical',
barPadding: barSize[0],
barMargin: barSize[1]
}
},
series: [],
axes:{
xaxis:{
renderer:$.jqplot.CategoryAxisRenderer,
label: labelY,
min:0,
max:values.length,
autoscale: true,
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
labelOptions: {enableFontSupport: true,
fontFamily: 'Arial',
fontSize: '12pt'
}
},
yaxis:{
min:0,
max:max,
ticks:this._get_ticks(min, max),
tickOptions:{formatString:'%d'},
label: labelX,
autoscale: true,
labelRenderer: $.jqplot.CanvasAxisLabelRenderer,
labelOptions: {enableFontSupport: true, fontFamily: 'Arial',fontSize: '12pt'}
}
},
highlighter: {
show: false
}
});
},