I am new to using the Plotly library and this time I find myself with the need to show in Vuejs 2 a circular progress graph like the following one.
I know that plotly is very complete but I have not seen an example with a similar aspect and that is also with javascript.
Thanks in advance for any information or help you can provide.
Greetings!
With plotly Derek Example, the graph looks like this
My English is not very good, but note that the line of the circle does not have a smooth curvature.
You can use plotly.js traces and text to recreate the components of this chart. If you use a scatter to place down an array of markers, you can create the grey arc, then place the red arc over it. To calculate the coordinates of each these markers, you can center your axes at (0,0) then use x=r*cos(theta) and y=r*sin(theta) where theta is your angle in radians. You can get an array of x and y values to trace out the desired portions of the red and grey arcs.
To get the circular chart to look like yours, I set the range of the x-axes and y-axes both to [-2,2], made the radius of the circular arcs 0.9 with [0,0] as the center, set the markers for these arcs to be size 10, and made the grey arc go from 210 to 85 degrees and red arc go from 90 to -200 degrees (using the function makeArr written by mhodges in his answer here),. Then to get the green marker to display in the legend, I created a trace with a green marker but with null values so it doesn't plot anything on the chart. Text traces can be used to add text around the center of the circular arcs.
Here is an example (codepen is here):
// credit goes to mhodges: https://stackoverflow.com/a/40475362/5327068
function makeArr(startValue, stopValue, cardinality) {
var arr = [];
var step = (stopValue - startValue) / (cardinality - 1);
for (var i = 0; i < cardinality; i++) {
arr.push(startValue + (step * i));
}
return arr;
}
// The function returns two arrays of circle coordinates
// for the outer points of a circle centered at some (x,y)
// and with a radius r with an arc of theta values
function getCircleCoords(r, center, degree_values) {
var center_x=center[0]
var center_y=center[1]
var x_coords = []
var y_coords = []
for (var i = 0; i < degree_values.length; i++) {
x_coords.push(center_x + (r * Math.cos(degree_values[i]*Math.PI/180)));
y_coords.push(center_y + (r * Math.sin(degree_values[i]*Math.PI/180)));
}
return [x_coords,y_coords]
}
var trace1 = {
x: [0],
y: [0.15],
text: ['1000'],
mode: 'text',
textfont: {
family: 'arial',
size: 28,
color: 'black'
},
showlegend: false
};
var trace2 = {
x: [0],
y: [-0.15],
text: ['kW/kg'],
mode: 'text',
textfont: {
family: 'arial',
size: 22,
color: 'grey'
},
showlegend: false
};
circleCoords = getCircleCoords(r=0.9, center=[0,0], radian_values=makeArr(90,-200,1000))
backgroundCircleCoords = getCircleCoords(r=0.9, center=[0,0], radian_values=makeArr(210,85,1000))
// display a marker in the legend without plotting it
var trace3 = {
x: [null],
y: [null],
mode: 'markers',
marker: {color: 'green', size: 10},
name: 'Correcto funcionamiento'
};
// grey background circle
var trace4 = {
x: backgroundCircleCoords[0],
y: backgroundCircleCoords[1],
mode: 'markers',
marker: {color: '#eeeeee', size: 10},
name: null,
showlegend: false
};
// red foreground circle
var trace5 = {
x: circleCoords[0],
y: circleCoords[1],
mode: 'markers',
marker: {color: 'red', size: 10},
name: 'Funcionamiento erroneo'
};
var layout = {
title:'RelacĂon potencia peso',
xaxis: {
range: [-2, 2],
zeroline: false,
showgrid: false,
zeroline: false,
showline: false,
showticklabels: false
},
yaxis: {
range: [-2, 2],
showgrid: false,
zeroline: false,
showline: false,
showticklabels: false
},
width: 600,
height: 600,
legend: {
x: 0,
y: 0,
"orientation": "h"
}
};
var data = [trace1, trace2, trace3, trace4, trace5];
Plotly.newPlot('myDiv', data, layout);
EDIT: for a smoother circle, you can increase the number of markers used to draw the circle.
circleCoords = getCircleCoords(r=0.9, center=[0,0], radian_values=makeArr(90,-200,5000))
backgroundCircleCoords = getCircleCoords(r=0.9, center=[0,0], radian_values=makeArr(210,85,5000))
Related
I would like to achieve something like the below image.
Chart with color band in yAxis
I already have working version of line chart in different places in our application. In One place we need a chart with color band in yAxis. Somehow I achieved this result StackBlitz Example
But the problems are
Not able to adjust the width of the bar
Not able to move the position of the bar close to yAxis
You can define a separate x-axis for the bar and name it "bar-x-axis" for example. The important thing is to define offset: false, ticks.display: false and gridLines.display: false.
xAxes: [{
stacked: true
},
{
id: "bar-x-axis",
offset: false,
stacked: true,
ticks: {
display: false
},
gridLines: {
display: false
}
}
]
Your bar datasets are linked to this new x-axis through the option xAxisID: "bar-x-axis" . To change the width of the bars, you would define a value between below 1 for barPercentage.
{
label: "Low-Moderate",
backgroundColor: "#ffe100",
yAxisID: "bar-y-axis",
xAxisID: "bar-x-axis",
data: [20],
barPercentage: 0.5
}
Please have a look at your amended StackBlitz.
You can draw individual boxes directly on the canvas using the Plugin Core API. The API offers a range of hooks that may be used for performing custom code.
First, you would define the color bars in an array as follows:
const colorBars = [
{ y: 20, color: "#aad700" },
{ y: 40, color: "#ffe100" },
{ y: 60, color: "#ef0000" },
{ y: 80, color: "#aad700" },
{ y: 100, color: "#ffe100" }
];
Then you can define a plugin with the beforeDraw hook as shown below:
const plugins = [
{
beforeDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales["x-axis-0"];
var yAxis = chart.scales["y-axis-0"];
ctx.save();
var prevY = 0;
colorBars.forEach((c, i) => {
ctx.fillStyle = c.color;
ctx.beginPath();
var yBottom = yAxis.getPixelForValue(prevY);
var yTop = yAxis.getPixelForValue(c.y);
ctx.fillRect(xAxis.left - 10, yTop, 10, yBottom - yTop);
ctx.stroke();
prevY = c.y;
});
ctx.restore();
}
}
];
The drawback of this solution is, that you won't see a tooltip when the mouse pointer hovers over the bars.
Please have a look at your amended StackBlitz and see how it works.
I want to create a waterfall chart using plotlyjs which shows ups and downs as compared to its previous value. Up should be represented by green color and down with red color.
How can I create such a graph using plotly.js?
The example given on the plotly site has different colors for different value ranges and it has no connection with ups and downs.
You can actually pass an array of colors to Plotly.
If you have an array of value differences, like [200, 400, -300, -150, -150], then you can formulate a color array like below.
const labels = ["Apples", "Oranges", "Rent", "Water", "Profit"];
const values = [200, 400, -300, -150, -150];
const colors = values.map((v) => v > 0 ? 'rgba(55,128,191,1.0)' : 'rgba(219, 64, 82, 1.0)');
// Use the cumulative sum to calculate the baseline of each bar. Use this to create a stacked bar chart with white bars below and colored bars on top
const baseline = new Array(values.length);
values.reduce((sum, val, idx) => {
baseline[idx] = val > 0 ? sum : sum + val;
return sum + val;
}, 0);
const trace1 = {
x: labels,
y: baseline,
marker: {
color: 'rgba(1,1,1,0.0)'
},
type: 'bar'
};
const trace2 = {
x: labels,
y: values.map(Math.abs),
marker: {
color: colors
},
type: 'bar'
};
var layout = {
title: 'Annual Profit 2018',
barmode: 'stack',
paper_bgcolor: 'rgba(245,246,249,1)',
plot_bgcolor: 'rgba(245,246,249,1)',
width: 600,
height: 600,
showlegend: false,
annotations: []
};
Plotly.newPlot('plot', [trace1, trace2], layout);
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="plot"></div>
I've got a chart with two lines, and I want to color the area between them based on which line is above the other one.
This graph shows income and outcome, so if income is more than outcome then the area is green, but if the outcome is more than the income the area turns red.
I can't find a good way to do this in Highcharts. I've tried area charts, but they just color up from zero to the line.
I hope the picture illustration helps, and that someone knows how to do this.
Thanks very much.
My two data sets are just two simple arrays like for instance
let income = [0, 0, 0, 0, 1000, 1000, 2000, 5000, 9000]
let outcome = [0, 0, 0, 0, 0, 7000, 7000, 7000, 12000]
By using zones you can change color at given location along the axis you want. This is the syntax:
series: {
name: 'Income',
data: data,
zoneAxis: 'x',
zones: [{value: 1, fillColor: 'green'},
{value: 5, fillColor: 'red}
]
}
That snippet gives you two zones, green up to 1, and red from 1 to 5. Since it is not very interesting doing this manually, I made an example that does this automatically, see fiddle or bottom of post:
http://jsfiddle.net/zhjyn2o4/1/
In the end you have a arearange graph like this:
I did this in highstock, but if you prefer using highchart, then it should work using the same code, though it will look a bit different.
You might be tempted to change to areasplinerange (which looks better). But using splines it is difficult to find the intersect points, and therefore difficult to color the graph correctly.
let income = [0, 0, 0, 1000, 1000, 2000, 5000, 9000, 12000, 12000, 12000, 5000, 4000, 10000]
let outcome = [0, 0, 7000, 0, 7000, 7000, 7000, 12000, 9000, 9000, 9000, 5000, 5000, 5000]
//create a function to find where lines intersect, to color them correctly
function intersect(x1, x2, y1, y2, y3, y4) {
return ((x2 * y1 - x1 * y2) - (x2 * y3 - x1 * y4)) / ((y4 - y3) - (y2 - y1));
}
var ranges = []; //stores all the data for the graph like so [x, y1, y2]
var incomeZones = []; //stores the different zones based on where the lines intersect
var incomeBiggerBool = true; //used for keeping track of what current color is
//loop through all values in income and outcome array (assumes they are same length). Fill the ranges array and create color zones.
//Zones color up to a given point, therefore we need to push a color at the end, before it intersects
for (i = 0; i < income.length; i++) {
ranges.push([i, income[i], outcome[i]]); //push to range array
if (income[i] < outcome[i] && incomeBiggerBool) {
incomeZones.push({
value: intersect(i - 1, i, income[i - 1], income[i], outcome[i - 1], outcome[i]),
fillColor: '#C0D890', // green
}); //push to zone array
incomeBiggerBool = false;
} else if (income[i] > outcome[i] && !incomeBiggerBool) {
incomeZones.push({
value: intersect(i - 1, i, income[i - 1], income[i], outcome[i - 1], outcome[i]),
fillColor: '#ED4337' // red
}); //push to zone array
incomeBiggerBool = true;
}
}
//zones color up to a given point, therefore we need to push a color at the end as well:
if (incomeBiggerBool) {
incomeZones.push({
value: income.length,
fillColor: '#C0D890' // green
})
} else {
incomeZones.push({
value: income.length,
fillColor: '#ED4337' // red
})
}
var chart = Highcharts.stockChart('container', {
chart: {
type: 'arearange'
},
credits: {
enabled: false
},
exporting: {
enabled: false
},
rangeSelector: {
enabled: false
},
scrollbar: {
enabled: false
},
navigator: {
enabled: false
},
xAxis: {
visible: false
},
title: {
text: 'Example'
},
plotOptions: {},
tooltip: {
//Prettier tooltip:
pointFormatter: function() {
return 'Income: <b>' + this.low + '</b> - Expenditures: <b>' + this.high + '</b>'
}
},
series: [{
name: 'Income',
data: ranges,
zoneAxis: 'x',
zones: incomeZones
}]
});
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/highcharts-more.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
Alrighty so I've run into an interesting little problem. I am trying to create a gauge chart, which then has plot bands. Simple enough.
The complication comes in when I want the plot bands to have a gradient run through the center of if evenly.
A rough js fiddle I forked:
http://jsfiddle.net/maraket/omez0n9r/3/
It should be noticed I am trying a radialgradient with a sharp change as per:
{
color: {
radialGradient: {
cx: 0.5,
cy: 0.5,
r: 0.5
},
stops: [
[0, '#000000'],
[0.8, '#ffffff'],
[1, '#000000'],
]
},
from: 0,
to: 100,
innerRadius: '90%',
outerRadius: '110%'
}
Now for a single plotband I noticed that the radial Gradient is more eliptical then circular which makes the solution I'm using not ideal. Furthermore when using multiple plotbands this solution won't work, since it uses the local plotbands x,y which changes if there are multiple plot bands. Any thoughts would be very helpful.
The reason why gradient is elliptical is because the bounding box of the plot band is not a square, but more a rectangle. It is because, you don't use full angle. Compare the gradient with full angle pane - http://jsfiddle.net/3mm1bjqf/.
Because your bounding box is not a square, then you need to define your own coordinate system for the gradient. Gradient attributes can be obtained from axis properties.
Your gradient should look like this:
plotBands: [{
color: {
radialGradient: {
gradientUnits: 'userSpaceOnUse', // we use our own coord system instead of bbox
cx: axis.left + axis.center[0],
cy: axis.top + axis.center[1],
r: axis.center[2] / 2 * 1.1 // multiplying by 1.1 because plotBand's outerRadius is set to 110%
},
Unfortunately, this gradient must be set dynamically because you do not know axis coords before the chart is rendered - so it must be done on load/redraw events.
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'gauge',
events: {
load: function() {
var axis = this.yAxis[0];
axis.update({
plotBands: [{
color: {
radialGradient: {
gradientUnits: 'userSpaceOnUse',
cx: axis.left + axis.center[0],
cy: axis.top + axis.center[1],
r: axis.center[2] / 2 * 1.1
},
stops: [
[0, '#000000'],
[0.8, '#ffffff'],
[1, '#000000'],
]
},
from: 0,
to: 100,
innerRadius: '90%',
outerRadius: '110%'
}]
})
}
}
},
example: http://jsfiddle.net/zfyzvqw1/
Using plotly.js, you can make a heatmap, and the heatmap comes with a color scale on the right side, as do some of the 3d charts.
Here is an example in codepen.
Plotly.d3.json('https://raw.githubusercontent.com/plotly/datasets/master/custom_heatmap_colorscale.json', function(figure) {
var data = [{
z: figure.z,
colorscale: 'Jet',
type: 'heatmap'
}
];
var layout = {title: 'Jet'};
Plotly.newPlot('myDiv', data, layout);
});
I am struggling to see if the api allows the color scale location and length to be modified. Does plotly.js allow manipulation of these attributes, and do you have an example of this for a heatmap and/or 3d graph?
You can set the position and length of the colorbar by adding the following line to your data:
colorbar: {x: -.5, len: 0.5}
Below is a snippet with a colorbar pushed to the left and half the regular size.
Plotly.d3.json('https://raw.githubusercontent.com/plotly/datasets/master/custom_heatmap_colorscale.json', function(figure) {
var data = [{
z: figure.z,
colorscale: 'Jet',
type: 'heatmap',
colorbar: {x: -.5, len: 0.5}
}
];
var layout = {title: 'Jet'};
Plotly.newPlot('myDiv', data, layout);
});
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv" style="width: 480px; height: 400px;"></div>
Adding this answer to show more tweaks you can do on Plotly colorbar
var plotData8kW = [{
z: data8kW,
hoverinfo:"z",
colorbar:{
// len: 0.35, //Change size of bar
title: 'Speed(RPM)<br\><br\>', //set title
titleside:'top', //set postion
//tickvals:[0,50,100],
titlefont: {color: 'blue'} //title font color
},
type: 'heatmap',
colorscale: enhancedScale,
},
];