Draw text at center of polygons - javascript

I get an array of polygons from a database. Each shape may be a triangle, a rectangle, a square, or any polygon.
I want to draw text at the center of each polygon. Font size must be dynamic according to the size of each polygon. Text color should match the line color.
Example from database:
Here is my code:
var polygons = [
{
text: "ROI", color: "#00ff00",
jointLength: 5, lineWidth: 3,
X: [890, 893, 409, 21, 27], Y: [658, 205, 199, 556, 659],
}, {
text: "Lane 3", color: "#ff0000",
jointLength: 4, lineWidth: 3,
X: [915, 911, 643, 879], Y: [5, 682, 683, 2],
}, {
text: "Lane 4", color: "#ff0000",
jointLength: 4, lineWidth: 3,
X: [888, 656, 170, 701], Y: [2, 680, 682, 1],
}, {
text: "Lane 5", color: "#ff0000",
jointLength: 5, lineWidth: 3,
X: [712, 182, 4, 4, 590], Y: [1, 681, 682, 532, 1],
}, {
text: "Speed", color: "#0000ff",
jointLength: 4, lineWidth: 3,
X: [290, 911, 873, 5], Y: [367, 357, 668, 664],
}
];
polygons.forEach((polygon) => {
const ctx = document.getElementById("canvas").getContext("2d");
ctx.strokeStyle = polygon.color;
ctx.lineWidth = polygon.lineWidth;
ctx.beginPath();
ctx.moveTo(polygon.X[0], polygon.Y[0]);
for (let i = 1; i < polygon.jointLength; i++) {
ctx.lineTo(polygon.X[i], polygon.Y[i]);
}
ctx.closePath();
ctx.stroke();
});
<canvas id="canvas" width=999 height=999></canvas>

Explanation of main logic:
The center of polygon I calculated by the formula of the arithmetic mean
The size of font I calculated by getting the width of text with font-size = 300 (but you can change the first check size as you want) and then check if text with is more than the smallest distance between 2 nearest dots (I think that this is good limit if text will be at the center of polygon). If yes then I start to find correct font-size with binary search algorithm
Because of this logic the text in second polygon is smaller than it can be because we have 2 dots at the top which are very close to each other
There is a code (open in full page for better visibility):
const polygons = [
{
text: "ROI",
color: "red",
jointLength: 5,
lineWidth: 3,
X: [890, 893, 409, 21, 27],
Y: [658, 205, 199, 556, 659],
},
{
text: "Lane 3",
color: "blue",
jointLength: 4,
lineWidth: 3,
X: [915, 911, 643, 879],
Y: [5, 682, 683, 2],
},
{
text: "Lane 4",
color: "green",
jointLength: 4,
lineWidth: 3,
X: [888, 656, 170, 701],
Y: [2, 680, 682, 1],
},
{
text: "Lane 5",
color: "orange",
jointLength: 5,
lineWidth: 3,
X: [712, 182, 4, 4, 590],
Y: [1, 681, 682, 532, 1],
},
{
text: "Speed",
color: "purple",
jointLength: 4,
lineWidth: 3,
X: [290, 911, 873, 5],
Y: [367, 357, 668, 664],
},
];
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext("2d");
canvas.width = 1000;
canvas.height = 1000;
class Polygon {
#ctx;
#dots = [];
#text;
#color;
#lineWidth;
#dotsCount;
constructor(ctx, data) {
this.#ctx = ctx;
this.#text = data.text;
this.#color = data.color;
this.#lineWidth = data.lineWidth;
this.#dotsCount = data.jointLength;
for (let i = 0; i < this.#dotsCount; ++ i) {
this.#dots.push({x: data.X[i], y: data.Y[i]})
}
}
#getCenterCoords() {
const x = this.#dots.reduce((sum, dot) => sum += dot.x, 0) / this.#dotsCount;
const y = this.#dots.reduce((sum, dot) => sum += dot.y, 0) / this.#dotsCount;
return {x, y};
}
#distance = (dot1, dot2) => Math.sqrt((dot1.x - dot2.x) ** 2 + (dot1.y - dot2.y) ** 2);
#getMinimalDistanceBetweenDots() {
let minDist = Infinity;
for (let i = 0; i < this.#dotsCount; ++i) {
const dot1 = this.#dots[i];
for (let j = i + 1; j < this.#dotsCount; ++j) {
const dot2 = this.#dots[j];
const dist = this.#distance(dot1, dot2);
if (dist < minDist) minDist = dist;
}
}
return minDist;
}
#getTextSize() {
const minAvailableWidth = this.#getMinimalDistanceBetweenDots();
let rightBound = 300;
let leftBound = 0;
let fontSize = rightBound;
while (rightBound - leftBound > 1) {
fontSize = Math.round((leftBound + rightBound) / 2);
this.#ctx.font = `${fontSize}px verdana`;
const textSize = this.#ctx.measureText(this.#text).width;
if (textSize > minAvailableWidth) {
rightBound = fontSize;
continue;
}
if (textSize < minAvailableWidth) {
leftBound = fontSize;
continue;
}
if (textSize === minAvailableWidth) {
break;
}
}
return fontSize;
}
draw() {
const path = new Path2D();
const firstDot = this.#dots[0];
const center = this.#getCenterCoords();
this.#dots.forEach(dot => path.lineTo(dot.x, dot.y));
path.lineTo(firstDot.x, firstDot.y);
this.#ctx.strokeStyle = this.#color;
this.#ctx.lineWidth = this.#lineWidth;
this.#ctx.lineCap = 'round';
this.#ctx.lineJoin = 'round';
this.#ctx.stroke(path);
this.#ctx.font = `${this.#getTextSize()}px verdana`;
this.#ctx.fillStyle = this.#color;
this.#ctx.textAlign = 'center';
this.#ctx.fillText(this.#text, center.x, center.y);
}
}
polygons.forEach((polygon) => new Polygon(ctx, polygon).draw());
<canvas id="canvas"></canvas>

Related

I am trying to draw straight lines on mouse click in plotyjs with image loaded as background

I am trying to draw a straight line on mouse click on plotlyjs with image loaded as background. I am able to get the co-ordinates but not able to draw lines on successive mouse clicks.
With the help of myPlot.on('plotly_click', function(data){ // some code }) I am able to get the co-ordinates but no idea to draw lines on mouse click. Any idea or reference will be helpful. Thanks in advance.
this is the output of the plotly graph with background image.
<script src='https://cdn.plot.ly/plotly-2.16.1.min.js'></script>
<div id='plotlyContainer'></div>
var d3 = Plotly.d3;
var imgWidth = 0;
var imgHeight = 0;
const imgs = new Image();
let plotCon = document.getElementById('plotlyContainer');
imgs.onload = function() {
imgWidth = this.width;
imgHeight = this.height;
loadImgGraph(imgWidth, imgHeight);
}
imgs.src = 'https://i.stack.imgur.com/cRbua.png';
function loadImgGraph(imgWidth, imgHeight) {
var data = [
{
x: [],
y: [],
type: 'scatter'
}
];
var layout = {
autosize: false,
width: imgWidth,
height: imgHeight,
xaxis: {range: [0, 100], dtick: 25, ticks: 'outside', tickwidth: 2, gridcolor: "black", gridwidth: 2, mirror: true, linecolor: 'black', linewidth: 1},
yaxis: {range: [140, 0], dtick: 20, ticks: 'outside', tickwidth: 2, gridcolor: "black", gridwidth: 2, mirror: true, linecolor: 'black', linewidth: 1},
margin: {
l: 32,
r: 20,
b: 30,
t: 30
},
images: [
{
source: 'https://i.stack.imgur.com/cRbua.png',
xref: 'paper',
yref: 'paper',
x: 0,
y: 1,
sizex: 1,
sizey: 1,
sizing: 'fill',
opacity: 1,
layer: 'below'
}
],
};
Plotly.newPlot('plotlyContainer', data, layout).then(clickEv);
function clickEv() {
var xaxis = plotCon._fullLayout.xaxis;
console.log('xaxis',xaxis);
var yaxis = plotCon._fullLayout.yaxis;
console.log('yaxis',yaxis);
var l = plotCon._fullLayout.margin.l;
console.log('l',l);
var t = plotCon._fullLayout.margin.t;
console.log('t',t);
plotCon.addEventListener('mousemove', function(evt) {
var xInDataCoord = xaxis.p2c(evt.x - l);
var yInDataCoord = yaxis.p2c(evt.y - t);
Plotly.relayout(plotCon, 'title', ['x: ' + xInDataCoord, 'y : ' + yInDataCoord].join('<br>'));
});
plotCon.on('plotly_click', function(){
alert('You clicked this Plotly chart!');
});
}
}
In you setting, one way to do it could be:
plotCon.addEventListener('click', function(evt){
data[0].x.push(xaxis.p2c(evt.x - l))
data[0].y.push(yaxis.p2c(evt.y - t))
Plotly.update(plotCon, data, layout);
});
Also, see Plotly.react for a more efficient way to update the plot.

Plotly chart using Javascript curve issue

I want to make step chart has curved line using plotly.js.
The difference between the connecting parts is felt differently depending on the size of the currently different numerical value.
The thickness of the curve path is also different.
And want to hide Xaxis and Yaxis divide lines.
I want to make the whole chart look perfect while making it the same.
Like this image.
enter image description here
<!DOCTYPE html>
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<!-- Plotly chart will be drawn inside this div -->
<div id="plotly-div"></div>
<body>
<script>
y = [0, 25, -35, -28, -10, 10, 25, 30, 35, 40, 65, 35];
x = [0, 5, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52];
trace = {
line: { shape: "vh" },
mode: "lines",
name: "vh",
type: "scatter",
x: x,
y: y,
};
let layout = {
xaxis: {
side: 'top'
},
showlegend: false,
//TODO: These curves interconnect diagonal lines and horizontal lines
shapes: [
//Use Cubic Quadratic Curve
{
type: 'path',
path: 'M 4,25 Q 4.8, 25 4.8 20',
line: {
color: 'black'
},
},
{
type: 'path',
path: 'M 5.2,-30 Q 5.2, -35 5.5, -35',
line: {
color: 'black'
}
},
]
};
let data = [];
for (let i = 1; i < x.length - 1; i++) {
let _x = x.slice(i - 1, i + 1);
// trace1 for the horizontal lines
//TODO: horizontal lines should stop at the appropriate x and y values
let bigger = y[i+1] > y[i] ? true : false
let _trace1 = {
mode: 'lines',
x: [_x[0] + 0.5, _x[1] - 1],
y: [y[i], y[i]],
type: 'line+markers',
line: {
color: 'black',
width: 1
}
};
// trace2 for the diagonal joining lines
//TODO: diagonal lines should cover about a percentage of the distance between the horizontal lines. Needs corrections
let _trace2 = {
mode: 'lines',
x: [x[i] - 0.2, x[i] + 0.2],
y: bigger ? [y[i], y[i + 1] + 5] : [y[i] -5 , y[i + 1] + 5],
type: 'line',
line: {
color: 'green',
width: 1
},
};
diff = y[i] - y[i - 1];
// trace3 for the vertical lines
//TODO: vertical lines should take into consideration correct increase or decrease
let _trace3 = {
mode: 'lines',
x: [x[i - 1] + 0.5, x[i - 1] + 0.5],
y: [1, diff],
type: 'line',
line: {
color: diff > 0 ? 'black' : 'red',
width: 5
}
};
layout.shapes[2*i -2] = {
type: 'path',
path: `M ${x[i]-1}, ${y[i]} Q ${x[i]-1}.8, ${y[i]} ${x[i]-1}.8 ${!bigger ? y[i]-5 : y[i]+5}`,
line: {
color: 'black'
},
}
layout.shapes[2*i -1] = {
type: 'path',
path: `M ${x[i]}.2, ${bigger ? y[i+1]-5 : y[i+1]+5} Q ${x[i]}.2, ${y[i+1]} ${x[i]}.5, ${y[i+1]}`,
line: {
color: 'black'
}
},
data.push(_trace1);
data.push(_trace2);
data.push(_trace3);
}
Plotly.plot("plotly-div", {
data: data,
layout: layout,
});
</script>
</body>
</html>
Yup. I solved the issue myself.
Plotly.js had solutions.
<!DOCTYPE html>
<head>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
</head>
<body>
<!-- Plotly chart will be drawn inside this div -->
<div id="plotly-div"></div>
<body>
<script>
y = [0, 25, -35, -28, -10, 10, 25, 30, 35, 40, 65, 35];
x = [0, 5, 7, 12, 17, 22, 27, 32, 37, 42, 47, 52];
let shapes = [];
let data = [];
for (let i = 1; i < x.length - 1; i++) {
// trace1 for the horizontal lines
//TODO: horizontal lines should stop at the appropriate x and y values
let bigger = y[i] > y[i - 1] ? true : false
let _w = (Math.abs(y[i] - y[i - 1]) / 80 + 0.3)
let _trace1 = {
mode: 'lines',
x: [x[i - 1], x[i] - _w],
y: [y[i - 1], y[i - 1]],
type: 'line+markers',
line: {
color: y[i - 1] < 0 ? '#EB5757' : (y[i - 1] > 50 ? '#2F80ED' : '#000000'),
width: 1
}
};
// trace2 for the diagonal joining lines
//TODO: diagonal lines should cover about a percentage of the distance between the horizontal lines. Needs corrections
let _trace2 = {
mode: 'lines',
x: [x[i] - _w + 0.15, x[i] - 0.15],
y: bigger ? [y[i - 1] + 1.5, y[i] - 1.5] : [y[i - 1] - 1.5, y[i] + 1.5],
type: 'line',
line: {
color: y[i] < 0 || y[i - 1] < 0 ? '#EB5757' : (y[i] > 50 || y[i - 1] > 50 ? '#2F80ED ' : '#000000'),
width: 1
},
};
var diff = y[i] - y[i - 1];
// trace3 for the vertical lines
//TODO: vertical lines should take into consideration correct increase or decrease
let _trace3 = {
mode: 'lines',
x: [x[i], x[i]],
y: [1, diff],
type: 'line',
line: {
color: diff > 0 ? '#000000' : '#EB5757',
width: 5
}
};
data.push(_trace1, _trace2, _trace3);
shapes.push({
type: 'path',
path: `M ${x[i] - _w}, ${y[i - 1]} Q ${x[i] - _w + 0.1}, ${y[i - 1]} ${x[i] - _w + 0.15} ${!bigger ? y[i - 1] - 1.5 : y[i - 1] + 1.5}`,
line: {
color: y[i - 1] < 0 || y[i] < 0 ? '#EB5757' : (y[i] > 50 || y[i - 1] > 50 ? '#2F80ED' : '#000000'),
width: 1
}
}, {
type: 'path',
path: `M ${x[i] - 0.15}, ${bigger ? y[i] - 1.5 : y[i] + 1.5} Q ${x[i] - 0.1}, ${y[i]} ${x[i]}, ${y[i]}`,
line: {
color: y[i - 1] < 0 || y[i] < 0 ? '#EB5757' : (y[i] > 50 || y[i - 1] > 50 ? '#2F80ED' : '#000000'),
width: 1
}
})
}
layout = {
xaxis: {
side: 'top',
showgrid: false,
autotick: false,
ticks: 'outside',
tick0: '',
dtick: 5,
tickcolor: 'red',
showline: true,
linecolor: '#E0E0E0',
linewidth: 1,
tickfont: {
size: 0,
color: 'red',
},
},
yaxis: {
showgrid: false,
showline: false,
ticks: 'outside',
tick0: 0,
dtick: 30,
ticklen: 4,
tickwidth: 2,
tickcolor: '#BDBDBD',
automargin: false,
tickfont: {
family: 'Old Standard TT, serif',
size: 12,
color: '#828282',
},
zeroline: true,
zerolinecolor: '#828282',
zerolinewidth: 2,
},
showlegend: false,
shapes: shapes,
}
Plotly.plot("plotly-div", {
data: data,
layout: layout,
});
</script>
</body>
</html>

Projecting a Point onto a Line

I'm trying to find the red point coordinates in this image where the points are projected on to a line formed by the jagged line's start and end point.
I'm using the code from this article, What are the coordinates of the projected point on a line segment using the perp dot product ?, but it appears that I'm doing something incorrectly. Could someone please help me achieve the result in the above image. Thank you!
const points = [
{ x: 2, y: 2 },
{ x: 3, y: 1 },
{ x: 4, y: 4 },
{ x: 5, y: 3 },
{ x: 6, y: 6 },
{ x: 7, y: 5 },
{ x: 8, y: 6 }
];
const first = points[0];
const last = points.slice(-1)[0];
const d = {
x: last.x - first.x,
y: last.y - first.y
};
function dot_product(pt1,pt2)
{
return pt1.x*pt2.x + pt1.y*pt2.y;
}
function drawChart() {
let projected = [];
for (var i=0; i<points.length; i++)
{
p = points[i];
let e2 = {x:p.x-first.x,y:p.y-first.y};
let dot = dot_product(d,e2);
let len2 = d.x * d.x + d.y * d.y;
let v = {
x: first.x + (dot*d.x)/ len2,
y: first.y + (dot*d.y)/ len2,
}
projected.push(v);
}
chart = new CanvasJS.Chart("chartContainer", {
animationEnabled: false,
theme: "light2",
data: [
{
type: "line",
color: "blue",
lineDashType: "dash",
indexLabelFontSize: 16,
dataPoints: [points[0],points.slice(-1)[0]]
},
{
type: "line",
color: "blue",
indexLabelFontSize: 16,
dataPoints: points
},
{
type: "line",
color: "red",
indexLabelFontSize: 16,
dataPoints: projected
}
]
});
chart.render();
}
drawChart();
html
{
font-family: sans-serif;
}
body
{
width: 80%;
position: relative;
}
#chartContainer { height: 400px; width: 320px; }
<script src="https://canvasjs.com/assets/script/canvasjs.min.js"></script>
<div id="chartContainer"></div>

Different xAxis label, max and colors for drilldown in Highcharts

I have a setup where I am generating multiple Highcharts with different sets of data. I now started implementing a drilldown, which needs different labels for xAxis, max needs to be changed and I would like to set the colors for each bar.
regarding the xAxis, the object I am using get the data for the drilldown seems alright to me, but instead of using the name, the i of the loop is being displayed
(5) […]
​ 0: {…}
​​ data: Array(5) [ 3, 8, 3, … ]
​​ id: "dd1"
​​ name: "Bad!"
​​ type: "column"
for the max value, which should be 5 for the basic chart but unset for the drilldown, I tried
drilldown: {
yAxis: {
max: 100
}
}
but it did not do anything.
TBH I did not try myself at colors yet, expected outcome would be that the bars are being colored according to the quality of the week, e.g. Bad week = red up to Fantastic = green
I also tried setting the chart type for the drilldown in general (right now I am setting 'type': 'column' for every data object, which appears redundant to me, but I could not figure out whether I can use something like ...drilldown.chart: { 'type':'column'} to define a general setting for the drilldown, but this too does not show any results.
I have tried implementing several setup examples using either functions bound to charts.events.drilldown or plotoptions.series.points.events.click but my very limited knowledge of Highcharts and JavaScript prevented me from succeeding.
Code:
// Fallback for browsers that cannot handle Object.values (i.e. that do not support ES6)
if (!Object.values) {
Object.values = function(source) {
var result = Object.keys(source).map(function(x) {
return source[x];
});
return result;
};
}
// Setting general HighCharts options
var options = {
chart: {
type: 'areaspline'
},
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
type: 'category'
},
yAxis: {
title: {
enabled: false
},
max: 5
},
series: [{
fillColor: {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, '#F0A830'],
[1, '#f4f4f4']
]
},
marker: {
fillColor: 'white',
lineWidth: 2,
lineColor: '#F0A830',
radius: 5
},
lineColor: '#F0A830',
lineWidth: 5,
}],
plotOptions: {
series: {
marker: {
enabled: false,
symbol: 'circle'
}
}
},
drilldown: {
yAxis: {
max: 100
},
chart: {
'type': 'column'
},
drillUpButton: {
theme: {
fill: 'white',
'stroke-width': 1,
stroke: 'silver',
r: 0,
states: {
hover: {
fill: '#a4edba'
},
select: {
stroke: '#039',
fill: '#a4edba'
}
}
}
}
}
};
// Temporary data delivery
var circlesData = {
"0": {
"title": "MyTeam",
"values": [4, 3, 2, 3, 4],
"weeks": [1, 2, 3, 4, 5],
"weeksTotal": [6, 7, 8, 9, 10],
"valuesDetail": {
"valuesDetailLabel": ["Bad!", "Hmm", "Itwasokay", "Prettygood", "Fantastic"],
"valuesDetailData": {
0: [3, 4, 4, 1, 15],
1: [2, 12, 5, 3, 1],
2: [18, 2, 2, 2, 2],
3: [3, 2, 4, 1, 5],
4: [1, 2, 1, 1, 15]
}
}
},
"1": {
"title": "YourTeam",
"values": [1, 4, 5, 2, 3],
"weeks": [1, 2, 3, 4, 5],
"weeksTotal": [6, 7, 8, 9, 10],
"valuesDetail": {
"valuesDetailLabel": ["Bad!", "Hmm", "Itwasokay", "Prettygood", "Fantastic"],
"valuesDetailData": {
0: [3, 8, 3, 1, 4],
1: [3, 12, 4, 3, 1],
2: [4, 2, 2, 2, 2],
3: [3, 2, 4, 5, 8],
4: [1, 2, 1, 1, 15]
}
}
}
}
console.log(circlesData);
var circlesDataString = JSON.stringify(circlesData); //this just turns the object array 'info' into a string
var obj = JSON.parse(circlesDataString);
console.log('Loading initiated...');
// Loop for creating individual HighCharts
Object.keys(obj).forEach(function(item) {
// Create container div
$('#outerContainer').append('<div class="innerContainer" id="circles' + item + '"></div>');
// Get data of last iteration and determin quality of last week for color coding/wording
var latestScore = circlesData[item].values.slice(-1)[0];
console.log('latestScore: ' + latestScore)
var chartColor = '';
var weekText = '';
var className = '';
if (latestScore < 2.5) {
chartColor = '#FD6E72';
chartColorLight = '#fc8385';
weekText = 'Bad week';
} else if (latestScore >= 2.5 && latestScore < 3.5) {
chartColor = '#FFCC73';
chartColorLight = '#FBD486';
weekText = 'Ok week';
} else {
chartColor = '#2CCC76';
chartColorLight = '#82DB9D';
weekText = 'Good week';
}
// create array for first chart view
var chartData = [];
var len = circlesData[item].values.length;
for (i = 0; i < len; i++) {
chartData.push({
'name': 'w' + circlesData[item].weeks[i],
'y': circlesData[item].values[i],
'drilldown': 'dd' + circlesData[item].values[i]
});
};
// set array for drilldown items
var drillDown = [];
for (i = 0; i < len; i++) {
drillDown.push({
'type': 'column',
'id': 'dd' + circlesData[item].values[i],
'data': circlesData[item].valuesDetail.valuesDetailData[i],
'name': circlesData[item].valuesDetail.valuesDetailLabel[i]
});
};
console.log('This is drillDown');
console.log(drillDown);
// Setting individual Highcharts options per Circle
options.series[0] = {
name: circlesData[item].title,
data: chartData,
color: chartColor,
fillColor: {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, chartColor],
[1, '#f4f4f4']
]
},
};
// Set drilldown options
options.drilldown.series = drillDown;
options.title = {
text: circlesData[item].title
};
options.subtitle = {
text: weekText,
style: {
color: chartColor,
}
};
console.log(options);
// Call Highcharts
$('#circles' + item).highcharts(options);
console.log('Circle' + item + ' loaded...');
});
https://codepen.io/anon/pen/eqRPGV
xAxis labels: For labels to have different names, each data point should have a name, like: 0: [["Bad!",3], ["Hmm",8], ["Itwasokay",3], ["Prettygood",1], ["Fantastic",4]]
Max value: drilldown.yAxis is not an API and therefore it won't work. Instead, the global max value should be updated. In this case, we can set it to null conditionally, as: if(options.drilldown) options.yAxis.max = null;
For each column to have different color, you need to overwrite the global colors array, options.colors with the desired one and define property: 'colorByPoint': true for each series object under drilldown object.
For chart type, drilldown.chart: { 'type':'column'} won't work, because there is no chart API for drilldown. Though it appears redundant, but each drilldown.series object will have its own chart type. In this case 'column'.
Also, the id in each drilldown.series object should be unique. In your code instead of doing this way, 'id': 'dd' + circlesData[item].values[i], you can do it using weeks array like: 'id': 'dd' + circlesData[item].weeks[i]. Because circlesData["0"].values have duplicate data.
Below is the updated code. You can refer jsfiddle.
// Fallback for browsers that cannot handle Object.values (i.e. that do not support ES6)
if (!Object.values) {
Object.values = function(source) {
var result = Object.keys(source).map(function(x) {
return source[x];
});
return result;
};
}
// Setting general HighCharts options
var options = {
chart: {
type: 'areaspline'
},
colors: ['#ED561B', '#DDDF00', '#24CBE5', '#058DC7', '#50B432'],
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
type: 'category'
},
yAxis: {
title: {
enabled: false
},
max: 5
},
series: [{
fillColor: {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, '#F0A830'],
[1, '#f4f4f4']
]
},
marker: {
fillColor: 'white',
lineWidth: 2,
lineColor: '#F0A830',
radius: 5
},
lineColor: '#F0A830',
lineWidth: 5,
}],
plotOptions: {
series: {
marker: {
enabled: false,
symbol: 'circle'
}
}
},
drilldown: {
drillUpButton: {
theme: {
fill: 'white',
'stroke-width': 1,
stroke: 'silver',
r: 0,
states: {
hover: {
fill: '#a4edba'
},
select: {
stroke: '#039',
fill: '#a4edba'
}
}
}
}
}
};
// Temporary data delivery
var circlesData = {
"0": {
"title": "MyTeam",
"values": [4, 3, 2, 3, 4],
"weeks": [1, 2, 3, 4, 5],
"weeksTotal": [6, 7, 8, 9, 10],
"valuesDetail": {
"valuesDetailLabel": ["Bad!", "Hmm", "Itwasokay", "Prettygood", "Fantastic"],
"valuesDetailData": {
0: [["Bad!",3], ["Hmm",4], ["Itwasokay",4], ["Prettygood",1], ["Fantastic",15]],
1: [["Bad!",2], ["Hmm",12], ["Itwasokay",5], ["Prettygood",3], ["Fantastic",1]],
2: [["Bad!",18], ["Hmm",2], ["Itwasokay",2], ["Prettygood",2], ["Fantastic",2]],
3: [["Bad!",3], ["Hmm",2], ["Itwasokay",4], ["Prettygood",1], ["Fantastic",5]],
4: [["Bad!",1], ["Hmm",2], ["Itwasokay",1], ["Prettygood",1], ["Fantastic",15]]
}
}
},
"1": {
"title": "YourTeam",
"values": [1, 4, 5, 2, 3],
"weeks": [1, 2, 3, 4, 5],
"weeksTotal": [6, 7, 8, 9, 10],
"valuesDetail": {
"valuesDetailLabel": ["Bad!", "Hmm", "Itwasokay", "Prettygood", "Fantastic"],
"valuesDetailData": {
0: [["Bad!",3], ["Hmm",8], ["Itwasokay",3], ["Prettygood",1], ["Fantastic",4]],
1: [["Bad!",3], ["Hmm",12], ["Itwasokay",4], ["Prettygood",3], ["Fantastic",1]],
2: [["Bad!",4], ["Hmm",2], ["Itwasokay",2], ["Prettygood",2], ["Fantastic",2]],
3: [["Bad!",3], ["Hmm",2], ["Itwasokay",4], ["Prettygood",5], ["Fantastic",8]],
4: [["Bad!",1], ["Hmm",2], ["Itwasokay",1], ["Prettygood",1], ["Fantastic",15]]
}
}
}
}
console.log(circlesData);
var circlesDataString = JSON.stringify(circlesData); //this just turns the object array 'info' into a string
var obj = JSON.parse(circlesDataString);
console.log('Loading initiated...');
// Loop for creating individual HighCharts
Object.keys(obj).forEach(function(item) {
// Create container div
$('#outerContainer').append('<div class="innerContainer" id="circles' + item + '"></div>');
// Get data of last iteration and determin quality of last week for color coding/wording
var latestScore = circlesData[item].values.slice(-1)[0];
console.log('latestScore: ' + latestScore)
var chartColor = '';
var weekText = '';
var className = '';
if (latestScore < 2.5) {
chartColor = '#FD6E72';
chartColorLight = '#fc8385';
weekText = 'Bad week';
} else if (latestScore >= 2.5 && latestScore < 3.5) {
chartColor = '#FFCC73';
chartColorLight = '#FBD486';
weekText = 'Ok week';
} else {
chartColor = '#2CCC76';
chartColorLight = '#82DB9D';
weekText = 'Good week';
}
// create array for first chart view
var chartData = [];
var len = circlesData[item].values.length;
for (i = 0; i < len; i++) {
chartData.push({
'name': 'w' + circlesData[item].weeks[i],
'y': circlesData[item].values[i],
'drilldown': 'dd' + circlesData[item].weeks[i]
});
};
// set array for drilldown items
var drillDown = [];
for (i = 0; i < len; i++) {
drillDown.push({
'type': 'column',
'id': 'dd' + circlesData[item].weeks[i],
'data': circlesData[item].valuesDetail.valuesDetailData[i],
'name':'w' + circlesData[item].weeks[i],
'colorByPoint': true,
});
};
console.log('This is drillDown');
console.log(drillDown);
// Setting individual Highcharts options per Circle
options.series[0] = {
name: circlesData[item].title,
data: chartData,
color: chartColor,
fillColor: {
linearGradient: {
x1: 0,
y1: 0,
x2: 0,
y2: 1
},
stops: [
[0, chartColor],
[1, '#f4f4f4']
]
},
};
// Set drilldown options
options.drilldown.series = drillDown;
options.title = {
text: circlesData[item].title
};
options.subtitle = {
text: weekText,
style: {
color: chartColor,
}
};
//do this conditionally
if(options.drilldown) options.yAxis.max = null;
console.log('option', options);
// Call Highcharts
$('#circles' + item).highcharts(options);
console.log('Circle' + item + ' loaded...');
});
Hope this helps!

How to add fill color in highcharts data series

I need to fill the color of data series dynamically based on the variable ( left , right , center ) from my code.
I have attached my code and expected output:
$(document).ready(function() {
var left = [
[4, 7],
[9, 2]
];
var right = [
[2, 2],
[9, 9]
];
var center = [
[4,5.5],
[10,5.5]
];
Highcharts.chart('container', {
chart: {
events: {
load: function () {
const xAxis = this.xAxis[0]
const yAxis = this.yAxis[0]
const leftBottom = {
x: xAxis.toPixels(right[0][0]),
y: yAxis.toPixels(right[0][1])
}
const leftTop = {
x: xAxis.toPixels(left[0][0]),
y: yAxis.toPixels(left[0][1])
}
const rightBottom = {
x: xAxis.toPixels(left[1][0]),
y: yAxis.toPixels(left[1][1])
}
const rightTop = {
x: xAxis.toPixels(right[1][0]),
y: yAxis.toPixels(right[1][1])
}
const leftMiddle = {
x: xAxis.toPixels(4),
y: yAxis.toPixels(5.5)
}
const rightMiddle = {
x: xAxis.toPixels(10),
y: yAxis.toPixels(5.5)
}
const leftTopMiddle = {
x: xAxis.toPixels(3.7),
y: yAxis.toPixels(6.5)
}
const leftBottomMiddle = {
x: xAxis.toPixels(2.1),
y: yAxis.toPixels(4)
}
const rightTopMiddle = {
x: xAxis.toPixels(9.8),
y: yAxis.toPixels(8)
}
const rightBottomMiddle = {
x: xAxis.toPixels(9.8),
y: yAxis.toPixels(3)
}
const curveTopLeft = this.curveTopLeft = this.renderer.path().attr({
d: `M ${leftMiddle.x} ${leftMiddle.y} Q ${leftTopMiddle.x} ${leftTopMiddle.y} ${leftTop.x} ${leftTop.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
const curveBottomLeft = this.curveBottomLeft = this.renderer.path().attr({
d: `M ${leftMiddle.x} ${leftMiddle.y} Q ${leftBottomMiddle.x} ${leftBottomMiddle.y} ${leftBottom.x} ${leftBottom.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
const curveTopRight = this.curveTopRight = this.renderer.path().attr({
d: `M ${rightMiddle.x} ${rightMiddle.y} Q ${rightTopMiddle.x} ${rightTopMiddle.y} ${rightTop.x} ${rightTop.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
const curveBottomRight = this.curveBottomRight = this.renderer.path().attr({
d: `M ${rightMiddle.x} ${rightMiddle.y} Q ${rightBottomMiddle.x} ${rightBottomMiddle.y} ${rightBottom.x} ${rightBottom.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
}
}
},
title: {
text: ''
},
tooltip: {
enabled: false
},
exporting: {
enabled: false
},
credits: {
enabled: false
},
plotOptions: {
series: {
pointStart: 1
}
},
xAxis: {
max: 10,
min: 1,
tickInterval: 1
},
yAxis: {
max: 11,
min: 0,
tickInterval: 1,
},
series: [{
showInLegend: false,
data: left
}, {
showInLegend: false,
data: right
},
{
showInLegend: false,
marker: {
enabled: true
},
data: center
}],
});
});
And my expected output should look like below,
You need to create a new path with no stroke but fill. The new path should be combined from the points you already defined.
const d = `M ${leftBottom.x} ${leftBottom.y}
Q ${leftBottomMiddle.x} ${leftBottomMiddle.y} ${leftMiddle.x} ${leftMiddle.y}
Q ${leftTopMiddle.x} ${leftTopMiddle.y} ${leftTop.x} ${leftTop.y}
L ${rightBottom.x} ${rightBottom.y}
Q ${rightBottomMiddle.x} ${rightBottomMiddle.y} ${rightMiddle.x} ${rightMiddle.y}
Q ${rightTopMiddle.x} ${rightTopMiddle.y} ${rightTop.x} ${rightTop.y}
Z`
const fillPath = this.renderer.path().attr({
d: d,
'stroke-width': 0,
fill: '#b19cd9',
zIndex: 1
}).add()
example: http://jsfiddle.net/r0j46wn6/24/
You can make use of the .css() method of your this.renderer.path() to set the fill style.
For example, Let's take the case of curveTopRight for what you can add that css as,
const curveTopRight = this.curveTopRight = this.renderer.path().css({
color: '#c8bfe7',
fill: '#c8bfe7'
}).attr({
d: `M ${rightMiddle.x} ${rightMiddle.y} Q ${rightTopMiddle.x} ${rightTopMiddle.y} ${rightTop.x} ${rightTop.y}`,
'stroke-width': 2,
stroke: 'red',
zIndex: 99
}).add()
However it is working only for the curved elements and not for the area's which are between the lines that you have drawn.
Here is a partially working example:
http://jsfiddle.net/r0j46wn6/23/
Hope this helps!

Categories

Resources