I am trying to create a 3D-scatter plot of some data with Highcharts. To make it easier to understand the 3D position of the data points, specially when using a still picture of the plot instead of the interactive draggable version, I would like to create projections of the data on each of the plot boundary planes.
It is not difficult to assign the corresponding position of these projections (example: http://jsfiddle.net/worg6jLz/1/), but I would like them to appear as real projections, not just as circles (or actually spheres) regardless of the perspective.
Is there a way to transform the plot markers according to their corresponding plane of projection, so that their shape is consistent with such projection regardless of the viewpoint?
Thanks.
$(function () {
// Give the points a 3D feel by adding a radial gradient
Highcharts.getOptions().colors = $.map(Highcharts.getOptions().colors, function (color) {
return {
radialGradient: {
cx: 0.4,
cy: 0.3,
r: 0.5
},
stops: [
[0, color],
[1, Highcharts.Color(color).brighten(-0.2).get('rgb')]
]
};
});
// Set up the chart
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
margin: 100,
type: 'scatter',
options3d: {
enabled: true,
alpha: 10,
beta: 30,
depth: 450,
viewDistance: 5,
fitToPlot: false,
frame: {
bottom: { size: 1, color: 'rgba(0,0,0,0.02)' },
back: { size: 1, color: 'rgba(0,0,0,0.04)' },
side: { size: 1, color: 'rgba(0,0,0,0.06)' }
}
}
},
title: {
text: 'Draggable box'
},
subtitle: {
text: 'Click and drag the plot area to rotate in space'
},
plotOptions: {
scatter: {
width: 10,
height: 10,
depth: 10
}
},
yAxis: {
min: 0,
max: 10,
title: null
},
xAxis: {
min: 0,
max: 10,
gridLineWidth: 1
},
zAxis: {
min: 0,
max: 10,
showFirstLabel: false
},
legend: {
enabled: false
},
series: [{
name: 'Data',
marker: {radius: 7,
symbol: 'circle',
fillColor: {
radialGradient: { cx: 0.4, cy: 0.3, r: 0.5 },
stops: [[0, 'rgba(195,195,255,1)'],
[1, 'rgba(0,0,255,1)']]}
},
colorByPoint: false,
color: 'blue',
data: [[9,9,1],[8,8,2],[7,7,3],[6,6,4],[5,5,5],[4,4,6],[3,3,7],[2,2,8],[1,1,9]]
},
{
name: 'Proj.A',
marker: {radius: 7,
symbol: 'circle'},
colorByPoint: false,
color: 'rgba(155,155,155,1)',
data: [[0,9,1],[0,8,2],[0,7,3],[0,6,4],[0,5,5],[0,4,6],[0,3,7],[0,2,8],[0,1,9]]
},
{
name: 'Proj.B',
marker: {radius: 7,
symbol: 'circle'},
colorByPoint: false,
color: 'rgba(155,155,155,1)',
data: [[9,0,1],[8,0,2],[7,0,3],[6,0,4],[5,0,5],[4,0,6],[3,0,7],[2,0,8],[1,0,9]]
},
{
name: 'Proj.C',
marker: {radius: 7,
symbol: 'circle'},
colorByPoint: false,
color: 'rgba(155,155,155,1)',
data: [[9,9,10],[8,8,10],[7,7,10],[6,6,10],[5,5,10],[4,4,10],[3,3,10],[2,2,10],[1,1,10]]
}]
});
// Add mouse events for rotation
$(chart.container).bind('mousedown.hc touchstart.hc', function (eStart) {
eStart = chart.pointer.normalize(eStart);
var posX = eStart.pageX,
posY = eStart.pageY,
alpha = chart.options.chart.options3d.alpha,
beta = chart.options.chart.options3d.beta,
newAlpha,
newBeta,
sensitivity = 5; // lower is more sensitive
$(document).bind({
'mousemove.hc touchdrag.hc': function (e) {
// Run beta
newBeta = beta + (posX - e.pageX) / sensitivity;
chart.options.chart.options3d.beta = newBeta;
// Run alpha
newAlpha = alpha + (e.pageY - posY) / sensitivity;
chart.options.chart.options3d.alpha = newAlpha;
chart.redraw(false);
},
'mouseup touchend': function () {
$(document).unbind('.hc');
}
});
});
});
Bonus:
An additional interesting feature, though not the main purpose of this post, would be to draw lines linking the data points with their projections when the mouse pointer is placed over them.
Related
I need to change the series color to uniformly be green and when the data range is high ,say I have data from Oct 2018 to Sep 2021 for one data point and the other data is Aug 20 2021 to Aug 22 2021,
the latter looks very small and barely noticeable . At this instance I want to set the minimum width and shape to the smallest data point in advanced accessible graph. How do I do that.
Any suggestion is appreciated
Image1
Image2
Code:function DrawRemoteRequestPSRChart(seriesData, yAxisCategories) {
// Define custom series type for displaying low/med/high values using boxplot as a base
Highcharts.seriesType('lowmedhigh', 'boxplot', {
keys: ['low', 'high'],
tooltip: {
}
}, {
// Change point shape to a line with three crossing lines for low/median/high
// Stroke width is hardcoded to 1 for simplicity
drawPoints: function () {
var series = this;
this.points.forEach(function (point) {
var graphic = point.graphic,
verb = graphic ? 'animate' : 'attr',
shapeArgs = point.shapeArgs,
width = 0,
left = Math.floor(shapeArgs.x) + 0.5,
right = left + width,
crispX = left + Math.round(width / 2) + 0.5,
highPlot = Math.floor(point.highPlot) + 0.5,
// Sneakily draw low marker even if 0
lowPlot = Math.floor(point.lowPlot) +
0.5 - (point.low === 0 ? 1 : 0);
if (point.isNull) {
return;
}
if (!graphic) {
point.graphic = graphic = series.chart.renderer
.path('point')
.add(series.group);
}
graphic.attr({
stroke: point.color || series.color,
"stroke-width": 4
});
graphic[verb]({
d: [
'M', left, highPlot,
'H', right,
'M', left, lowPlot,
'H', right,
'M', crispX, highPlot,
'V', lowPlot
]
});
});
}
});
// Create chart
var chart = Highcharts.chart('container', {
chart: {
type: 'lowmedhigh',
inverted: true
},
credits: {
enabled: false
}, legend: {
enabled: false
},
title: {
text: 'Daily company fruit consumption 2019'
},
tooltip: {
shared: false, formatter: function () {
return this.point.category + ', ' + new Date(this.point.options.low).toGMTString() +
' to ' + new Date(this.point.options.high).toGMTString() + '.';
}
},
accessibility: {
point: {
descriptionFormatter: function (point) {
var ix = point.index + 1,
category = point.category,
from = new Date(point.low),
to = new Date(point.high);
return ix + '. ' + category + ', ' + from.toDateString() +
' to ' + to.toDateString() + '.';
}
},
typeDescription: 'Low, high. Each data point has a low and high value, depicted vertically as small ticks.' // Describe the chart type to screen reader users, since this is not a traditional boxplot chart
},
xAxis: [{
accessibility: {
description: 'Months of the year'
},
categories: ['January', 'February'],
crosshair: true
}],
yAxis: {
type: 'datetime'
},
responsive: {
rules: [{
condition: {
minWidth: 550
},
chartOptions: {
xAxis: {
categories: ['Jan', 'Feb']
}
}
}]
},
plotOptions: {
series: {
stickyTracking: true,
whiskerWidth: 5
}
},
series: [{
name: 'Plums', color: 'lime',
data: [
[1416528000000,
1417478400000
],
[
Date.UTC(2014, 10, 1, 10, 16, 58),
Date.UTC(2014, 12, 2, 10, 16, 58)
]]
}, {
name: 'Bananas',
color: 'blue',
data: [
[Date.UTC(2014, 10, 21, 10, 16, 58),
Date.UTC(2014, 11, 22, 10, 16, 58)
],
[
Date.UTC(2014, 10, 15, 10, 16, 58),
Date.UTC(2014, 12, 12, 10, 16, 58)
]
]
}, {
name: 'Apples',
color: 'red',
type: 'scatter',
marker: { symbol: 'diamond' },
data: [
[Date.UTC(2014, 10, 20, 10, 16, 58),
Date.UTC(2014, 11, 27, 10, 16, 58)
],
[
Date.UTC(2014, 10, 24, 10, 16, 58),
Date.UTC(2014, 10, 24, 12, 17, 58)
]
]
}]
});
// Remove click events on container to avoid having "clickable" announced by AT
// These events are needed for custom click events, drag to zoom, and navigator
// support.
chart.container.onmousedown = null;
chart.container.onclick = null;
//Highcharts.chart('GraphHere', {
// chart: {
// type: 'xrange'
// },
// credits: {
// enabled: false
// },
// legend: {
// enabled: false
// },
// tooltip: {
// pointFormat: ''
// },
// title: {
// text: 'Mass Remote Request PSR'
// },
// xAxis: {
// type: 'datetime'
// },
// yAxis: {
// title: {
// text: ''
// },
// categories: yAxisCategories,
// min: 0,
// max: 5,
// scrollbar: {
// enabled: true
// },
// reversed: true
// },
// series: seriesData
//});
}
It would be best to add a point there using a scatter series, disable or hide the point you want to otherwise show in chart.event.load and redraw the chart.
chart: {
events: {
load: function() {
var chart = this;
chart.series[0].update({
borderColor: 'green',
pointWidth: 0,
}, false);
chart.redraw();
}
}
},
Demo:
https://jsfiddle.net/BlackLabel/sx2u0epw/
API References:
https://api.highcharts.com/highcharts/chart.events.load
https://api.highcharts.com/highcharts/series.scatter
Im doing a chart with 2 series. One of the series is 'scatter' type and the other is 'column' type.
The problem i have, is that when i only have the scatter series, datetime labels on x axis, starts on the extreme left, but when i add the column series, is like all labels are pushed to the center, and i dont know why.
This is my fiddle examen: https://jsfiddle.net/cswpgq8u/7/
Highcharts.chart('container', {
chart: {
},
xAxis: {
type: 'datetime',
min: 1633057200000,
max: 1634353200000,
tickPositioner: function () {
var ticklist = [];
var extremes = this.getExtremes();
var startDate = extremes.min;//this.min;
var endDate = extremes.max; // this.max;
var diff = moment.duration(endDate - startDate);
if (diff.days() * 1 > 10)
increment = moment.duration(2, 'days').asMilliseconds();
else
increment = Math.ceil((endDate - startDate) / 10);
for (var timeline = startDate; moment(timeline) <= moment(endDate); timeline += increment) {
ticklist.push(timeline);
}
return ticklist;
},
maxPadding: 0,
minPadding: 0,
crosshair: {
enabled: true,
events: {
}
},
plotLines: [{
value: 1634266800000,
color: 'green',
width: 1,
dashStyle: 'ShortDash',
label: {
text: "AHORA", // Content of the label.
verticalAlign: 'bottom',
rotate: -90,
textAlign: 'right',
x: -2,
y: 30,
style: { color: '#6d6d6d', fontSize: '8px', fontWeight: 'bold', backgroundColor: 'white' }
},
}],
plotBands: null,
labels: {
formatter: function () {
var xMin = this.axis.min;
var xMax = this.axis.max;
var labeltick = Highcharts.dateFormat('%d. %B', this.value);
if ((moment(xMax).diff(moment(xMin), 'days')) < 10) {
labeltick = Highcharts.dateFormat('%d. %B %H:%M:%S', this.value);
}
return labeltick;
},
x: -10
}
},
yAxis:[
{
plotLines: [{
id: 'ln-' + 'idEje1',
color: '#9FA0A2',
width: 0,
value: 0,
dashStyle: 'longdashdot'
}],
labels: {
enabled:true,
x: -5,
y: -3,
},
title: {
enabled: false,
},
lineColor: 'lightgray',
lineWidth: 0,
tickInterval: 1
}, {
title: {
enabled: false,
},
lineColor: 'lightgray',
opposite: true,
labels: {
enabled: false,
x: -15,
y: -3,
},
lineWidth: 0,
tickInterval: 1
}
],
plotOptions: {
},
series: [
{
yAxis: 0,
type: 'scatter',
minPointLength: 1,
allowPointSelect: false,
stack: true,
data: [{
x: 1634007600000,
y: 2,
value: 2,
marker: {
symbol: 'circle',
radius: 3,
fillColor: 'red',
},
type: "ttf"
},
{
x: 1633748400000,
y: 5,
value: 2,
marker: {
symbol: 'circle',
radius: 3,
fillColor: 'red',
},
type: "ttf"
},
{
x: 1633143600000,
y: 2,
value: 2,
marker: {
symbol: 'circle',
radius: 3,
fillColor: 'red',
},
type: "ttf"
},
{
x: 1633402800000,
y: 2,
value: 2,
marker: {
symbol: 'circle',
radius: 3,
fillColor: 'red',
},
type: "ttf"
}],
name: 'On YAxis 0',
tooltip: {
pointFormatter: function () {
var point = this;
return "Valor TTF:" + ' <b>' + point.y + ' Días</b><br/>';
},
}
},
{
yAxis: 1,
type: 'column',
allowPointSelect: false,
minPointLength: 1,
data: [{
x: 1633708800000,
y: 5,
type: "duracion",
value: 5
},
{
x: 1633881600000,
y: 3,
type: "duracion",
value: 3
}],
name: 'On YAxis1',
tooltip: {
pointFormatter: function () {
var point = this;
return "Duración:" + ' <b>' + point.y + ' minutos</b><br/>';
},
},
pointWidth: 1
}
]
});
With 2 series
With one series
I think this is happening because Column Series. But dont know how to solve it.
The additional space is caused by the default calculation of pointRange for column series. You need to define it by yourself:
series: [{
type: 'scatter',
...
},
{
type: 'column',
pointRange: 1,
...
}
]
Live demo: https://jsfiddle.net/BlackLabel/hsrxtjaw/
API Reference: https://api.highcharts.com/highcharts/series.column.pointRange
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 received a mockup of a graph using an areaspline (ignoring the column chart):
I have two questions that I can't seem to find any configuration that would satisfy the requirements.
Is there a way to extend the lines to the edge of the graph based on their trending slope, or possibly just a little above or below the end point? On a graph like this, it wouldn't look bad to omit those edge lines, but on a graph in the same area with only 3 data points, the whitespace is much more noticeable.
I have the y-axis labels stepping every other, but I can't seem to find a way to hide the first label ($0) in this case. Aside from just hiding the text element itself, is there a configuration to do this?
Edit: Here's an example of my configuration:
var themeColors = {
primaryColor: '#067bc2', //dark blue
secondaryColor: '#6a61a7', //dark purple
tertiaryColor: '#5ccadc', //turquoise
darkGray: '#37383e', //dark gray
mediumGray: '#919aa2', //medium gray
mediumLightGray: '#c9d3dc', //medium blue gray
lightGray: '#eaf1f8', //light blue gray (hover color)
primaryRed: '#e54443',
secondaryRed: '#e54443',
primaryGreen: '#2bbb2d',
secondaryGreen: '#2bbb2d',
primaryOrange: '#ffa883', //peach
primaryBackground: '#fafcfe',//light blue
secondaryBackground: '#ffffff',
primaryText: '#37383e', //dark gray
secondaryText: '#919aa2', //medium gray
selectedColor: '#f5fafd' //light blue, slightly darker than background
};
var colors = [
themeColors.secondaryColor,
themeColors.tertiaryColor,
themeColors.primaryOrange
];
(function renderChart(series) {
var options = {
chart: {
height: 400,
spacing: [0,0,0,0],
type: 'areaspline',
},
colors: colors,
credits: { enabled: false },
legend: {
align: 'left',
itemStyle: {
color: themeColors.secondaryText,
fontSize: '12px',
fontWeight: 'normal',
},
margin: 30,
symbolHeight: 14,
symbolRadius: 0,
symbolWidth: 14,
},
plotOptions: {
areaspline: {
marker: {
fillColor: themeColors.secondaryBackground,
lineColor: null,
lineWidth: 2,
radius: 3,
symbol: 'circle',
}
}
},
title: { text: null },
xAxis: {
categories: ['first', 'second', 'third'],
crosshair: {
color: Highcharts.color(themeColors.lightGray).setOpacity(.5).get('rgba'),
width: 12
},
labels: {
style: {
color: themeColors.secondaryText,
fontSize: '12px'
}
},
lineColor: themeColors.lightGray,
tickColor: themeColors.lightGray,
tickWidth: 0,
},
yAxis: {
gridLineColor: themeColors.lightGray,
labels: {
align: 'left',
step: 2,
style: {
color: themeColors.secondaryText,
fontSize: '12px'
},
x: 0,
y: 12,
},
lineColor: themeColors.lightGray,
tickColor: themeColors.lightGray,
title: { text: null },
},
};
series.forEach((s,i)=>{
s.fillColor = {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, Highcharts.color(colors[i]).setOpacity(0.1).get('rgba')],
[1, Highcharts.color(colors[i]).setOpacity(0).get('rgba')]
]
};
});
options.series = series;
Highcharts.chart('container',options);
})([
{ name: 'a', data:[1,4,2] },
{ name: 'b', data:[3,1,2] }
]);
jsFiddle: https://jsfiddle.net/mtowj6ng/
Thanks!!
You can set min and max of the axis.
xAxis: {
categories: ['first', 'second', 'third'],
min: 0.5,
max: 1.5,
If you have 3 categories, the axis range will be 0 - 2, so you can increase the min and decrease the max in the amount of 0.5 (the middle of the category).
You can also do it dynamically by updating the xAxis with xAxis.update on chart load.
chart: {
events: {
load: function () {
const min = Math.min.apply(null, this.series[0].xData)
const max = Math.max.apply(null, this.series[0].xData)
this.xAxis[0].update({
min: min + 0.5,
max: max - 0.5
})
}
}
},
You can also do it before you render the chart.
Set yAxis.showFirstLabel to false.
example: https://jsfiddle.net/bbd3pu9j/
I created a graph with Highcharts polar type. The boundaries of chart are overflowing. Can anyone please help me in fixing the graph.
Please find my problem on JSFiddle:
JSFiddle
Code:
$(function () {
$('#container').highcharts({
chart: {
polar: true,
showAxes: false
},
title: {
text: 'Highcharts Polar Chart'
},
pane: {
startAngle: 0,
endAngle: 360
},
xAxis: {
tickInterval: 45,
min: 0,
max: 360,
labels: {
formatter: function () {
return this.value + '°';
}
},
lineWidth: 10,
lineColor: "#000000",
gridLineWidth: 5,
gridLineColor: "#000000"
},
yAxis: {
offset: 120,
tickmarkPlacement: 'on',
min: 0,
max: 4,
visible: false,
endOnTick: false
},
plotOptions: {
series: {
pointStart: 0,
pointInterval: 45
},
column: {
pointPadding: 0,
groupPadding: 0
},
line: {
color: "#ff0000",
lineWidth: 10
}
},
series: [{
type: 'line',
name: 'Line2',
data: [8, 7, 6, 5, 4, 3, 2, 1],
pointPlacement: 'between'
}]
});
});
This produces axis outside plot area. I would like to increase plot area so as to see the meeting point of extremist end points.
The reason the Plot is overflowing is because you have set a Maximum Value for the yAxis of 4 but in your data you have values that are greater than 4, like 8,7,6 and 5.
Just remove the max: 4 option from the yAxis Object and your Chart will display fine.