I've have had some problems with the highmaps library since a while back. I got reports from users that a map displaying statistics was not rendering sometimes.
When looking at the problem, I noticed that the map rendered quite inconsistently, sometimes perfect, but quite often - not at all.
When consulting the documentation at highcharts docs I realised that the example on jsfiddle suffers from the exact same problem. Sometimes the map simply does not render. I made a screen recording to show the problem.
This problem occurs in chrome and ff, I have not tried other browsers.
This is my code for the map, it's a part of a Angular app.
function createChart() {
chart = new Highcharts.Map({
chart: {
renderTo: 'mapContainer'
},
title: {
text: '',
},
credits: {
enabled: false
},
legend: {
layout: 'horizontal',
borderWidth: 0,
backgroundColor: 'rgba(255,255,255,0.85)',
floating: false,
verticalAlign: 'bottom',
horizontalAlign: 'center',
},
colorAxis: {
maxColor: '#7b9b00',
minColor: '#d7e241',
type: 'linear',
allowDecimals: false,
marker: {
color: 'black'
},
minPadding: 0.005
},
tooltip: {
formatter: function() {
return '<b>' + this.point.name + '</b><br>' +
'Asset Hits: ' + this.point.options.hits + '<br>' +
'Total Volume: ' + this.point.options.volume + '<br>';
},
style: {
fontSize: '12px',
fontFamily: 'Helvetica, Arial, sans-serif'
}
},
series: [{
mapData: Highcharts.maps['custom/world'],
name: 'countries',
showInLegend: false,
borderWidth: 0.2,
states: {
hover: {
borderWidth: 1
}
}
}]
});
}
scope.$on('geoData', function(event, data) {
var validData = function (data) {
var equal = _.isEqual(data, cachedData);
if (!equal && data.length > 1) {
return data;
}
};
if (validData) {
if (!chart) {
createChart();
}
updateSeries(data);
}
cachedData = data;
}, true);
function updateSeries(data) {
if (chart !== undefined) {
if (chart.series.length > 1) {
chart.series[1].remove();
}
}
chart.addSeries({
data: data,
title: 'Performance by Country',
mapData: Highcharts.maps['custom/world'],
joinBy: ['hc-key', 'countryiso'],
name: 'countries',
borderWidth: 0.2,
borderColor: 'black',
states: {
hover: {
borderWidth: 1,
color: '#7b9b00'
}
}
});
}
}
};
}
Video displaying the issue:
Link to video
Related
I have a stacked column/scatter chart with some dataLabels off to one side. The issue I am facing is that when my two markers begin to get close to one another, their dataLabels overlap
I need to always show both labels, so is there a way to detect when labels are overlapping and move the bottom one down by adjusting its y value based on how much overlap there is?
sample fiddle of the issue
Highcharts.chart('container', {
chart: {
type: 'column',
width: 500
},
title: {
text: 'Stacked column chart'
},
xAxis: {
visible: false,
},
yAxis: {
min: 0,
visible: false,
title: {
},
},
legend: {
layout:"vertical",
align: "right",
verticalAlign: "bottom",
itemMarginTop: 15,
y: -10,
x: -50
},
tooltip: {
enabled: false,
},
plotOptions: {
scatter: {
marker: {
symbol: "triangle",
},
dataLabels: {
enabled: true,
x: -80,
y: 50,
allowOverlap: true,
useHTML: true,
}
},
column: {
pointWidth: 70,
stacking: 'normal',
dataLabels: {
enabled: false
}
}
},
series: [{
name: '',
data: [100],
color: "#ededed"
}, {
name: '',
data: [500]
}, {
name: '',
data: [400]
},
{
type: "scatter",
data: [1000],
color: "#000",
dataLabels: {
formatter: function(){
return "<div class='label-text'>Your goal of <br/>$"+ this.y +"<br/>text</div>"
},
}
},
{
type: "scatter",
data: [900],
color: "#000",
dataLabels: {
formatter: function(){
return "<div class='label-text'>You are here <br/>$"+ this.y +"<br/>text</div>"
},
}
}]
});
You can correct data-labels positions by using the attr method on their SVG elements.
For example:
chart: {
events: {
render: function() {
const series = this.series;
const dl1 = series[3].points[0].dataLabel;
const dl2 = series[4].points[0].dataLabel;
if (dl1.y + dl1.height > dl2.y) {
dl2.attr({
y: dl1.y + dl1.height
});
}
}
}
}
Live demo: https://jsfiddle.net/BlackLabel/5Lmh4owb/
API Reference:
https://api.highcharts.com/class-reference/Highcharts.SVGElement.html#attr
https://api.highcharts.com/highcharts/chart.events.render
Since I'm trying to use your Highcharts package, I'm facing an issue when unit testing the component that use Highcharts function.
Here is my context:
I have a component (the only one) that use Highcharts. In the Web client, When I run unit test on this component, chart function legendItemClick not getting covered.
Code below are the Javascript transpilled code. Here is my original typescript file:
let dsCapacityChartOptions = {
chart: {
type: 'pie',
spacing: [0, 10, 2, 10],
margin: [-10, 0, 30, 0]
},
title: {
text: ''
},
credits: {
enabled: false
},
plotOptions: {
pie: {
shadow: false,
colors: ['#3E7BBF', '#CBCBCB', '#92B2D9'],
point: {
events: {
legendItemClick: function () {
return false;
}
}
}
},
series: {
states: {
hover: {
enabled: false
},
inactive: {
opacity: 1
}
}
}
},
tooltip: {
outside: true,
style: {
color: '#000000',
opacity: 1,
fontSize: '12px',
fontFamily: 'Metropolis,Avenir Next,Helvetica Neue,Arial,sans-serif'
},
formatter:
function () {
return '<b>' + this.point.x + '</b>: ' + this.point.formattedCapacity + " (" + this.percentage.toFixed(2) + "%)";
}
},
series: [{
type: 'pie',
name: this.labelsObj["vsc.portlet.dsCapacity.label"],
size: '80%',
innerSize: '70%',
showInLegend: true,
dataLabels: {
enabled: false
},
data: []
}],
legend: {
labelFormatter: function () {
return this.x + ": " + this.formattedCapacity;
},
align: 'center',
layout: 'vertical',
itemStyle: {
color: '#454545',
fontWeight: '500',
fontSize: '12px',
fontFamily: 'Metropolis,Avenir Next,Helvetica Neue,Arial,sans-serif'
},
itemHoverStyle: {
color: '#454545',
cursor: 'default'
}
},
exporting: { enabled: false }
};
Here is what I'm doing to make the unit tests works-
it('should call legendItemClick', () => {
let spy = spyOn(component.dsCapacityChartOptions.plotOptions.pie.point.events, 'legendItemClick')
component.dsCapacityChartOptions.plotOptions.pie.point.events.legendItemClick();
expect(spy).toHaveBeenCalledTimes(1);
})
Highcharts function not covered with unit tests with Karma + Jasmine
Please find below snippet of code coverage-
Code coverage
Your unit test doesn't make sense. You explicitly invoke a function and test if it was really called. In your case, legendItemClick returns a value, hence you should check the result of the call. There's no need to use a spy.
The test would then look simple as follows:
it('#legendItemClick should return false', () => {
const result = component.dsCapacityChartOptions.plotOptions.pie.point.events.legendItemClick();
expect(result).toBe(false);
})
I am bringing in to my chart json objects, indexed two different ways.
I have a button to flip my index, and it works in the sense that my catagories will flip back and forth, but my new data does not...
var $chart_data = data[1];
$button = $('#button');
var chart_ops_support_times = new Highcharts.Chart({
chart: {
renderTo: 'chart_ops_support_times',
type: 'column'
},
title: {
useHTML: true,
text: '',
align: 'left'
},
subtitle: {
text: ' '
},
xAxis: {
categories: [
'priority 1',
'priority 2',
'priority 3'
]
},
yAxis: {
min: 0,
max: 60,
title: {
text: 'Total Actual Hours by Type',
style: {
fontSize: '8px',
fontWeight: 'normal'
}
},
stackLabels: {
enabled: false,
style: {
fontWeight: 'bold'
}
}
},
legend: {
align: 'center',
x: 0,
verticalAlign: 'top',
y: 0,
floating: true,
borderColor: '#CCC',
borderWidth: 1,
shadow: false,
symbolHeight: 10,
symbolWidth: 10
},
credits: {
enabled: false
},
tooltip: {
formatter: function () {
return '<b>' + this.x + '</b><br/>' +
this.series.name + ': ' + this.y + '<br/>' +
'Total: ' + this.point.stackTotal;
}
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: false,
style: {
textShadow: '0 0 3px black'
}
}
}
},
exporting: {
enabled: false
},
series: $chart_data
});
$button.click(function() {
if (!flipxAxis) {
console.log('flip to priority x axis');
chart_ops_support_times.xAxis[0].categories = priority_bkts ;
chart_ops_support_times.series[0].update({
dataLabels: {
enabled: true
},
series: data[0]
});
chart_ops_support_times.redraw();
} else {
console.log('flip to time span x axis');
chart_ops_support_times.xAxis[0].categories = time_span_bkts;
chart_ops_support_times.series[0].update({
dataLabels: {
enabled: false
},
series: data[1]
});
chart_ops_support_times.redraw();
}
flipxAxis = !flipxAxis;
});
}
I have put in the swap of datalabels just to test if the update is firing, and it is...
I'm working with the highcharts stacked bar and want to remove some space between the bars so I can make room for some text I am rendering.
I've tried pointPadding and groupPadding but those are not working. I've tried minPadding/maxPadding on the xAxis and that did not do anything as well.
Seems the only way get rid of that space is to change the width of the whole chart which is what I really don't want.
Here is my fiddle:
http://jsfiddle.net/nick1572/dfcysj39/
$("#profit-chart").highcharts({
lang: {
thousandsSep: ","
},
chart: {
type: "column",
style: {
fontFamily: "Open Sans, sans-serif"
}
},
legend: {
enabled: false
},
title: {
text: ""
},
xAxis: {
//minPadding: 20, Not working here
//maxPadding:1, Not working here either
categories: [ "other business", "somekind of business profit" ],
labels: {
style: {
color: "#333333",
fontSize: "15px"
}
}
},
yAxis: {
gridLineDashStyle: "longdash",
title: {
text: "Dollars"
},
labels: {
enabled: true,
formatter: function() {
return "$" + Highcharts.numberFormat(this.value, 0, "", ",");
}
}
},
tooltip: {
enabled: false
},
plotOptions: {
column: {
stacking: "normal",
dataLabels: {
enabled: true,
color: "white",
inside: true,
useHTML: true,
style: {
fontSize: "18px",
fontWeight: "600"
}
}
},
series: {
//pointPadding: 0,
//groupPadding: 0, this does not work
animation: {
complete: function() {
}
},
pointWidth: 145
}
},
series: [ {
color: "#327631",
data: [ 0, 850 ],
stack: "female",
dataLabels: {
enabled: true,
formatter: function() {
if (0 != this.y) return "$" + Highcharts.numberFormat(this.y, 0);
else return null;
}
}
}, {
color: "#ADC9AD ",
data: [ 10000, 10000 ],
stack: "female",
dataLabels: {
enabled: true,
formatter: function() {
return "$" + Highcharts.numberFormat(this.y, 0);
}
}
}]
},
function (chart) { // on complete
chart.renderer.text('<span class="bracketed">}</span> <em>Profit</em>', 870, 85)
.css({
color: 'green',
fontSize: '24px',
x: 200
})
.add();
});//End HighCharts Call
Thanks in advance!
it seems that the point width setting is conflicting with the groupPadding and pointPadding .. if you remove the pointWidth: 145 in the series and add groupPadding and pointPadding to the column you will get the gap removed. on the flipside however the bars will take up the entire available width of your chart.
column: {
stacking: "normal",
groupPadding: 0,//add here
pointPadding: 0,//add here
...
}
http://jsfiddle.net/dfcysj39/9/
Ok so I think this may solve my problem at this point. Not sure if its the right way but it works. The pointPadding and groupPadding partially worked for this particular problem. I thank you Birgit for taking the time to respond!!!
What I did to solve this is explicitly put a spacingRight on the chart to give the plot area more space w/o making the bars wider. I did go back and add some pointPadding to make the cols a little wider for aesthetics. This allowed for the rendered text to show.
http://jsfiddle.net/nick1572/dfcysj39/
$("#profit-chart").highcharts({
lang: {
thousandsSep: ","
},
chart: {
spacingRight: 220,
type: "column",
width: 1200,
style: {
fontFamily: "Open Sans, sans-serif"
}
},
legend: {
enabled: false
},
title: {
text: ""
},
xAxis: {
//minPadding: 20, Not working here
//maxPadding:1, Not working here either
categories: [ "other business", "somekind of business profit" ],
labels: {
style: {
color: "#333333",
fontSize: "15px"
}
}
},
yAxis: {
gridLineDashStyle: "longdash",
title: {
text: "Dollars"
},
labels: {
enabled: true,
formatter: function() {
return "$" + Highcharts.numberFormat(this.value, 0, "", ",");
}
}
},
tooltip: {
enabled: false
},
plotOptions: {
column: {
stacking: "normal",
dataLabels: {
enabled: true,
color: "white",
inside: true,
useHTML: true,
style: {
fontSize: "18px",
fontWeight: "600"
}
}
},
series: {
pointPadding: 0.05,
//groupPadding: 0, this does not work
animation: {
complete: function() {
}
}//,
//pointWidth: 145
}
},
series: [ {
color: "#327631",
data: [ 0, 850 ],
stack: "female",
dataLabels: {
enabled: true,
formatter: function() {
if (0 != this.y) return "$" + Highcharts.numberFormat(this.y, 0);
else return null;
}
}
}, {
color: "#ADC9AD ",
data: [ 10000, 10000 ],
stack: "female",
dataLabels: {
enabled: true,
formatter: function() {
return "$" + Highcharts.numberFormat(this.y, 0);
}
}
}]
},
function (chart) { // on complete
chart.renderer.text('<span class="bracketed">}</span> <em>Profit</em>', 900, 84)
.css({
color: 'green',
fontSize: '24px',
x: 200
})
.add();
});//End HighCharts Call
Trying to increase bar height to accommodate series name inside bar and trying to reduce default gap between series group. The section "series: []" is, I assume for the JSON return function to populate. Tried many examples from all the generous JSFiddle examples to no avail. I'm a noob so please be patient. Any ideas? So far I have: http://jsfiddle.net/PeterHanekom/7ue5bkm8/1/
var options = {
chart: {
renderTo: 'container',
type: 'bar'
},
colors: ['#00B9B9', '#527CED', '#A7D36B', '#B9B900', '#0097FF', '#400040', '#EA4D00', '#336699', '#8080C0', '#ADA870'],
title: {
text: 'Cluster Totals',
x: -20 //center
},
subtitle: {
text: 'Dept - Branch',
x: -20
},
xAxis: {
categories: []
},
yAxis: {
min: 0,
max: 100,
title: {
text: 'Percentage'
},
labels: {
overflow: 'justify'
}
},
tooltip: {
formatter: function() {
return '<b>'+ this.series.name +'</b><br/>'+
this.x +': '+ this.y;
}
},
legend: {
align: 'center',
borderWidth: 0
},
plotOptions: {
bar: {
stacking: 'normal',
pointWidth: 20,
pointPadding: 0,
cursor: 'pointer',
point: {
events: {
click: function() {
alert('later drilldown events');
}
}
dataLabels: {
enabled: true,
inside:true,
useHTML: true,
align: 'left',
color: 'white',
style: {
fontWeight: 'bold'
},
verticalAlign: 'middle',
formatter: function () {
if (this.series.name)
return '<span style="color:black; height: 25px;">' + this.series.name + ' = ' + this.y + '</span>';
else return '';
}
}
}
},
series: []
}
$.getJSON("./services/get360Pivot.php?s_Search=" + sOperatorSearch, function(json)
{
options.xAxis.categories = json[0]['data'];
options.series[0] = json[1];
options.series[1] = json[2];
options.series[2] = json[3];
options.series[3] = json[4];
chart = new Highcharts.Chart(options);
});
I think you options you need are groupPadding and pointWidth. e.g.
groupPadding:0.1,
pointWidth:20,
http://jsfiddle.net/s3t2pL94/