I'm trying to plot some straight lines over my chart and have gotten most of the way there, but am now stuck with the offsets. The red lines below are supposed to draw a line between two markers, but as you can see, they're high and left.
I was expecting plotX and plotY to sort this out for me, but I'm missing something. There are four series in the chart: the green, the blue, the red triangles, and the red triangle-downs. I want to draw a line linking the red triangles (3rd series) and the red triangle-downs (4th series). I'm currently doing it by a callback which loops over series three and adds a path using plotX and plotY.
It currently looks something like this, but I'm open to better ways.
function(){
var chart = this;
$.each(chart.series[2].data, function(index, value){
startX = chart.series[2].data[index].plotX;
startY = chart.series[2].data[index].plotY;
endX = chart.series[3].data[index].plotX
endY = chart.series[3].data[index].plotY
chart.renderer.path(['M', startX, startY, 'L', endX, endY])
.attr({ 'stroke-width': 2, stroke: 'red' })
.add();
console.log(index, startX, startY, endX, endY);
})
});
The axis and everything else look something like:
$(document).ready(function () {
chart1 = new Highcharts.StockChart({
chart:{
renderTo:'container'
},
yAxis:[
{ // Leader yAxis
labels:{
formatter:function () { return "$" + this.value.toFixed(2); },
style:{ color:'green' }
},
title:{
text:'Leader',
style:{ color:'green' }
}
},
{ // Follower yAxis
title:{
text:'Follower',
style:{ color:'#4572A7' }
},
labels:{
formatter: function () { return "$" + this.value.toFixed(2); },
style: { color:'#4572A7' }
},
opposite: true
}],
series:[
{
type: 'line',
name: 'Buyer Moving Average',
data: buyer,
color: 'green',
yAxis: 1
},{
type:'line',
name:'Data',
data: equity,
color: "#4572A7",
yAxis: 0
},{
type: 'scatter',
name: 'Buys',
data: buys,
color: 'red',
marker: { symbol: "triangle" },
yAxis: 0
},{
type:'scatter',
name:'Sells',
data: [{{ sells }}],
color: 'red',
marker: { symbol: "triangle-down" },
yAxis: 0
}
]
Funny how being forced to turn a problem into a question helps me straighten out my thoughts and frustrations enough to find an answer for myself...
The plotX and plotY attributes don't take into account the labelling and whatever else that the axes introduce, so it's just a matter of finding the offset they create and adding it in:
function(){
var chart = this;
xoffset = chart.series[2].xAxis.left;
yoffset = chart.series[2].xAxis.top;
$.each(chart.series[2].data, function(index, value){
startX = chart.series[2].data[index].plotX + xoffset;
startY = chart.series[2].data[index].plotY + yoffset;
endX = chart.series[3].data[index].plotX + xoffset;
endY = chart.series[3].data[index].plotY + yoffset;
chart.renderer.path(['M', startX, startY, 'L', endX, endY])
.attr({ 'stroke-width': 2, stroke: 'red' })
.add();
console.log(index, chart.series[2].xAxis.left, startX, startY, endX, endY);
}
Related
I have 2 pie charts rendered next to each other corresponding 2 series. I want to add a title under each series, but don't want to use the title property, because each Highcharts component only gets one title. I've opted to use annotations and that is working, which requires me to manually specify the x and y coordinates for the annotation labels. I'm using a formula to calculate these exact positions. However, when I render the annotations, they don't seem to exactly line up in the middle of each chart.
My chart options look like this:
const width = 700
const height = 200
Highcharts.chart('container', {
chart: {
type: 'pie',
width,
height
},
plotOptions: {
pie: {
dataLabels: {
enabled: false
}
}
},
annotations: [{
labels: [{
point: {
x: 0.33 * width,
y: height
},
text: "centered"
}]
}, {
labels: [{
point: {
x: 0.635 * width,
y: height
},
text: "also centered"
}]
}],
series: [
{
center: ['33%', '50%'],
type: 'pie',
data: [
['Firefox', 44.2],
['IE7', 26.6],
['IE6', 20],
['Chrome', 3.1],
['Other', 5.4]
]
},
{
center: ['66%', '50%'],
type: 'pie',
data: [
['Firefox', 44.2],
['IE7', 26.6],
['IE6', 20],
['Chrome', 3.1],
['Other', 5.4]
]
}]
});
Here's what it looks like now: https://jsfiddle.net/0cfztdms/
And here's what I want to achieve without hardcoding the x positions:
Pie charts with centered annotations
I think that a better solution will be rendering the custom label via using SVGRendere tool than using the annotation feature.
Demo: https://jsfiddle.net/BlackLabel/L08bvngk/
render() {
let chart = this;
chart.series.forEach((s, i) => {
if (chart['label' + i]) {
chart['label' + i].destroy();
}
chart['label' + i] = chart.renderer.label('testtestestest', 0, 0)
.css({
color: '#FFFFFF'
})
.attr({
fill: 'rgba(0, 0, 0, 0.75)',
padding: 8,
r: 5,
zIndex: 6
})
.add();
//center the label
chart['label' + i].translate(s.center[0] + chart.plotLeft - chart['label' + i].getBBox().width / 2, chart.plotHeight)
})
}
API: https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#label
API: https://api.highcharts.com/highcharts/chart.events.render
I figured out a way to center my labels without having to do the manual calculations using Highcharts annotations module. Set anchorX = 'center' and anchorY = 'middle' (these props are from the annotations module) will automatically center the annotations for you. Read more here
const width = 600;
annotations: [{
labels: [{
point: {
x: (1 / numberOfCharts + 1) * width,
y: 200,
anchorX: 'center',
anchorY: 'middle'
},
text: 'Label 1'
}, {
point: {
x: (1 / numberOfCharts + 1) * 2 * width,
y: 200,
anchorX: 'center',
anchorY: 'middle'
},
text: 'Label 2'
}
}]
How can I get the X and Y coordinates of a point?
I would like to place a badge at the arc line, right between the two points in the pie chart. At the moment I add circle and text using the chart renderer.
$(function() {
$('#chart').highcharts({
chart: {
type: 'pie',
options3d: {
enabled: false,
alpha: 0
},
height: 470
},
colors: ['#EF3F3E', '#757575'],
title: {
style: {
fontSize: '90px',
align: 'center',
color: '#EF3F3E'
},
verticalAlign: 'middle'
},
tooltip: {
enabled: false
},
plotOptions: {
pie: {
innerSize: 420,
dataLabels: {
enabled: false
},
shadow: false,
center: ['50%', '50%'],
borderWidth: 0
}
},
series: [{
name: 'Delivered amount',
data: [56, 44]
}]
}, function(chart) { // on complete
chart.setTitle({
color: "#333333",
});
chart.renderer.circle(650, 420, 50).attr({
fill: '#3A3A3B',
'stroke-width': 0,
zIndex: 3
}).add();
chart.renderer.circle(650, 420, 40).attr({
fill: '#EF3F3E',
'stroke-width': 0,
zIndex: 4
}).add();
chart.customText = chart.renderer.text('30', 635, 430)
.css({
color: 'black',
font: '28px Arial, sans-serif',
'z-index': '5'
}).attr({
zIndex: 5
}).add();
});
});
https://jsfiddle.net/yq2s5ng1/5/
Is there a better way to display this badge? For example using markers insted of rendering custom elements?
EDIT:
In case anyone is interested, here is how I solved this:
function getCoordinates() {
var x;
var y;
var h = chart.series[0].data[0].shapeArgs.x;
var k = chart.series[0].data[0].shapeArgs.y;
var r = chart.series[0].data[0].shapeArgs.innerR;
//var theta = chart.series[0].data[0].angle;
//var theta = chart.series[0].data[0].series.endAngleRad;
var theta = chart.series[0].data[0].shapeArgs.end;
x = h + r * Math.cos(theta);
y = k + r * Math.sin(theta);
return { x: x, y: y };
}
This post helped me to find the Cartesian coordinates: https://math.stackexchange.com/questions/475917/how-to-find-position-of-a-point-based-on-known-angle-radius-and-center-of-rotat
I've been messing around with the demo code for highcharts pie charts to see if I can accomplish several things...
Have labels inside unless the slice is too thin. If it's too thin,
label should be outside
Color fonts white. If the slice is outside or if the slice is a
specific color, this should be black
In the below, I was able to address some of what I'm trying to do in the bottom function. It moves labels outside based on the percentage of the slice and also colors it black. But...
I don't want to use attr({y:x}) for adjusting the position when the
slice is too thin. I would rather be able to use distance.
I'm not sure how to check the color of a slice to then be able to color the font. Ideally it would be something like the function
below that uses "point" but would be for the slice instead.
Anyone know how to handle?
$(function () {
Highcharts.chart('container', {
chart: {
type: 'pie'
},
plotOptions: {
pie: {
dataLabels: {
useHTML: true,
enabled: true,
format: '{point.percentage:.1f} %',
color: 'white',
distance: -40
},
showInLegend: true,
}
},
series: [{
data: [
['Firefox', 45.0],
['Chrome', 26.8],
['IE 11', 12.8],
['Edge', 8.5],
['Opera', 6.2],
['Other', 0.7],
]
}]
},
function(chartObj) {
$.each(chartObj.series[0].data, function(i, point) {
if(point.percentage < 6) {
point.dataLabel.css({color: 'black'}),
point.dataLabel.attr({y: -20})
}
})
});
});
</script>
Solution to your problem is to take angle of desired arc (pie slice) and calculate x and y using Math.cos and Math.sin functions.
const options = {
chart: {
type: 'pie',
events: {
load: function() {
const series = this.series[0];
const points = series.data;
const chart = this;
points.forEach(function(point) {
if (point.y <= 1) {
const myOffset = 40;
const {x: centerX, y: centerY} = point.graphic.attr();
const {x, y} = point.dataLabel.attr();
const angle = point.angle;
point.dataLabel.attr({
x: x + Math.cos(angle) * myOffset,
y: y + Math.sin(angle) * myOffset
});
point.dataLabel.css({color: 'black'});
}
});
}
}
},
title: {
text: ''
},
plotOptions: {
pie: {
dataLabels: {
enabled: true,
distance: -25,
formatter: function() {
return this.y + '%';
}
}
}
},
series: [{
innerSize: '60%',
data: [40, 20, 20, 19, 1]
}]
}
const chart = Highcharts.chart('container', options);
Live example:
https://jsfiddle.net/b1ya1x9h/
I am using Area chart with navigator, what I am trying to do is highlight high and Low point markers (only two points ) in the current visible chart, and on top of these markers I want to display fixed tooltips displaying the point information.
And as user navigates through navigator these point markers should be recalculated and tooltips should follow them.
[This is what I am trying to accomplish][1]
So far I have been able to calculate high and low points and highlight corrosponding markers in chart, and I have also been able to display two tooltips, but they are not ACURATELY positioned along with point markers.
![So far this has been accomplished][2]
Here is my Code:
function customTooltip(point) {
console.log(chart);
var text = chart.renderer.text(
'Max',
point.plotX + chart.plotLeft + 10,
point.plotY + chart.plotTop - 10
).attr({
zIndex: 5
}).add();
var box = text.getBBox();
chart.renderer.rect(box.x - 5, box.y - 5, box.width + 10, box.height + 10, 5)
.attr({
fill: '#FFFFEF',
stroke: 'gray',
'stroke-width': 1,
zIndex: 4
})
.add();
}
var labelArr = [], // keeps Tooltips for High and Low
chart = $('#container').highcharts('StockChart', {
chart: {
type: 'line',
width: 900
},
scrollbar: {
enabled: false
},
navigator: {
top: -1,
height: 25,
handles: {
backgroundColor: 'transparent',
borderColor: 'transparent'
}
},
rangeSelector: {
inputPosition: {
align: 'left',
x: 10,
y: 30
},
buttons: [{
type: 'year',
count: 1,
text: ''
}],
buttonTheme: { // styles for the buttons
fill: 'none',
stroke: 'none',
'stroke-width': 0,
states: {
hover: {
fill: 'none'
},
select: {
fill: 'none'
}
}
},
inputDateFormat: '%m-%d-%Y',
inputStyle: {
fontSize: '14px',
borderColor: '#FFF'
},
selected: 0
},
yAxis: {
labels: {
align: 'right',
x: -5,
formatter: function () {
return "$" + this.value;
}
}
},
xAxis: {
type: 'datetime',
tickWidth: 0,
labels: {
y: 20
},
dateTimeLabelFormats: {
month: '%B'
},
events: {
setExtremes: function () {
var pointMax, pointMin, labelLength = labelArr.length;
// Flush out Old Tooltips for High and Low
if (labelLength > 0) {
for (var i = 0; i < labelLength; i++) {
var popVal = labelArr.pop();
this.chart.container.firstChild.removeChild(popVal);
}
}
$.each(this.series[0].points, function (i, point) {
if (!pointMax && !pointMin) pointMax = pointMin = point;
// If marker is enabled for any point, disable it
if (typeof point.marker != 'undefined') point.marker.enabled = false;
// Calculate Highest point
if (pointMax.y < point.y) pointMax = point;
// Calculate Lowest point
if (pointMin.y > point.y) pointMin = point;
});
pointMax.update({
marker: {
enabled: true
}
}, false, false);
//customTooltip( pointMax );
pointMin.update({
marker: {
enabled: true
}
}, false, false);
//customTooltip( pointMin );
}
}
},
tooltip: {
crosshairs: false
/*useHTML: true,
borderRadius: 0,
backgroundColor: '#000',
shadow: false,
borderWidth: 0,
enabled: true,
formatter: function(){
/* Temp Disable Custom tool-Tip
var arr = [];
$.each(this.points, function(k, obj){
arr.push( "<span style='color:#FFF'>Low price w/o sales charge</span>" );
arr.push( "<br>" );
arr.push( "<span style='font-weight:bold;color:#FFF'>$" + obj.y + "</span>" );
arr.push( "<br>" );
});
return arr.join(' ');
} */
},
plotOptions: {
area: {
marker: {
fillColor: '#f7941e',
states: {
hover: {
enabled: false
}
}
}
},
series: {
point: {
events: {
update: function (event) {
event.target.series.chart.tooltip.refresh([event.target]);
var cloneToolTip = event.target.series.chart.tooltip.label.element.cloneNode(true);
event.target.series.chart.container.firstChild.appendChild(cloneToolTip);
labelArr.push(cloneToolTip);
}
}
}
}
},
series: [{
name: 'Quarterly Returns w/o sales charge',
data: [
[1149120000000, 62.17],
[1149206400000, 61.66],
[1369699200000, 441.44],
[1369785600000, 444.95]
]
// pointWidth: 14,
}]
}).highcharts();
});
Can you please guide me on how to proceed from here?
I want to display the datatable inside Pie chart using Highcharts.I can display the table outside but on exporting the image, datatable is not exported.
So my aim is to display datatable inside the Pie chart.
Refering to this.I was able to partly acheive it.demo
But how can i give the column headers and borders to it?
$(function () {
Highcharts.drawTable = function() {
// user options
var tableTop = 200,
colWidth = 60,
tableLeft = 50,
rowHeight = 20,
cellPadding = 2.5,
valueDecimals = 1,
valueSuffix = '';
// internal variables
var chart = this,
series = chart.series,
renderer = chart.renderer,
cellLeft = tableLeft;
// draw category labels
$.each(series, function(serie_index, serie) {
renderer.text(
serie.name,
cellLeft + cellPadding,
tableTop + (serie_index + 1) * rowHeight - cellPadding
)
.css({
fontWeight: 'bold'
})
.add();
});
$.each(series[0].data, function(i) {
renderer.text(
series[0].data[i].name,
cellLeft + colWidth - cellPadding,
tableTop + (i + 2) * rowHeight - cellPadding
)
.attr({
align: 'right'
})
.add();
});
$.each(series[0].data, function(i) {
renderer.text(
Highcharts.numberFormat(series[0].data[i].y, valueDecimals) + valueSuffix,
150,
tableTop + (i + 2) * rowHeight - cellPadding
)
.attr({
align: 'left'
})
.add();
});
}
$('#container').highcharts({
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
events: {
load: Highcharts.drawTable
},
height: 600,
width: 800,
marginBottom: 250
},
title: {
text: 'Browser market shares at a specific website, 2010'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
color: '#000000',
connectorColor: '#000000',
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
}
}
},
series: [{
type: 'pie',
name: 'Browser share',
data: [
['Firefox', 45.0],
['IE', 26.8],
{
name: 'Chrome',
y: 12.8,
sliced: true,
selected: true
},
['Safari', 8.5],
['Opera', 6.2],
['Others', 0.7]
]
}]
});
});
For the column headers, I don't know. Depends on what you want to do.
As for the borders, since that's SVG, you could draw the borders using RECT and PATH. Take a look at this fork of your demo for some crude borders and maybe you can improve on it.