Trying to use HighCharts to build solidgauge chart with multiple layer - javascript

I'm trying to build some chart like this one:
Chart Visual
But the main struggle is to add two different series that complement each other.
I really appreciate any help that someone could give me.
Many thanks in advance.

You can achieve it, but the process of implementation is not so easy. I prepared the example which shows how to do that, and I will try to explain what I did, step by step.
First, you need to define your data array just like that:
var data = [40, 30, 10, 20]
Then define your chart configuration, and inside of chart.events.load function handler put whole logic of creating desired effect.
Next step is iterate on all data positions, and create its own specific point, series, yAxis and pane, basing on calculations like below:
load() {
var chart = this,
series = [],
panes = [],
yAxes = [],
radius = 112,
innerRadius = 88,
pointAngle,
prevPointAngle = 0,
pointPadding = (radius - innerRadius) / 4,
colors = Highcharts.getOptions().colors,
additionalPointPadding = 2;
data.forEach(function(p, i) {
pointAngle = (p * 360) / 100 // Calculate point angle
// Prepare pane for each point
panes.push({
startAngle: prevPointAngle + pointPadding + additionalPointPadding,
endAngle: (pointAngle + prevPointAngle) - pointPadding - additionalPointPadding,
background: [{
backgroundColor: '#fff',
borderWidth: 0
}]
})
// Prepare yAxis for specific pane
yAxes.push({
min: 0,
max: 100,
lineWidth: 0,
tickPositions: [],
pane: i
})
// Prepare series with specific point
series.push({
name: 'Exercise ' + i,
data: [{
color: colors[i],
radius: radius + '%',
innerRadius: innerRadius + '%',
y: 100,
percents: p
}],
yAxis: i
})
prevPointAngle += pointAngle
})
And finally, update our chart by new objects:
chart.update({
pane: panes,
yAxis: yAxes,
series: series
},true, true)
Last thing you have to know, that your chart configuration should have the same amount of empty objects in pane array, like the data positions, e.g:
var data = [10, 80, 10]
(...)
pane: [{},{},{}]
Here is the example which shows the final effect: https://jsfiddle.net/yamu5z9r/
Kind regards!

Related

Apache Echarts: dataZoom's miniature misrepresents data in a managed plot (the plot drawn over the slider doesn't match the main plot's data)

When I create simple line plot/chart using Apache Echarts I also can add built-in data scaling mechanism: dataZoom. It reaches its main goal, but there is a question to scaled data representation, made by dataZoom. By default, dataZoom doesn't take into account the chart scale limits ticks or/and the minimum and maximum allowable values (range of a function, represented by the plot). Instead, the thumbnail of the chart is drawn on the specific value range passed to the plot in series section. In addition, everytime a small indent is added from the minimum and maximum values ​​to the borders of the graphic element.
As a result, the representation of the visualised data looks inconsistent with reality: null is not null, max is not max (because they don't match the lower and higher bounds of the coordinate area of ​​the thumbnail plot, respectively), the amplitude of the chart fluctuations does not correspond to the scale of real data fluctuations.
Screenshot
Is there a way (documented or undocumented) to remove the indents and force the plot to use the minimum and maximum values ​​allowed for the yAxis ticks?
I drawn a small example, it may be pasted to Echarts online editor.
let x = [];
let y = [];
let scaled = [];
/*y = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 300, 300,
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 0, 0, 0, 0, 0, 0
];*/
for (let i = 1; i < 300; i++) {
x.push(i);
element = Math.random() * 40 + 50;
y.push(element);
scaled.push(element * 6 - 250);
}
option = {
xAxis: {
data: x
},
yAxis: {
min: 0,
max: 300
},
dataZoom: [
{
start: 50,
end: 58.5
}
],
series: [
{
name: 'Fake Data',
type: 'line',
symbol: 'none',
data: y
},
{
name: 'Simulated Scaling',
type: 'line',
symbol: 'none',
lineStyle: {
opacity: 0.3
},
data: scaled
}
]
};
As you can see, the magnitude of the fluctuations of the graph, drawn by dataZoom doesn't correspond rather to the main data, but to some kind of artificial transformation of them (light green graph). Then try to comment 11st line and uncomment lines from 4 to 7. At the start of the plot you'll see main graph touching y zero line, but not on the thumbnail.
I didn't find any params for dataZoom that make them to look like expected.

Plotly color not changing Javascript

I working on a project that needs me to use graph csv file inputs. I've been using plotly and so far it seems to be working very well.
However, when I try to change the color of the graphs (lines and markers) it doesn't work. I am posting excerpts of my code since the color is overall a small portion of the code and I don't want to dump everything here.
//There's multiple charts so changing color is important
var r = Math.random() * 256
var g = Math.random() * 256
var b = Math.random() * 256
...
//used these as a vars so I can change things to test easily (multiple time series being used)
var color='rgb('+r+', '+g+', '+b+')'
var colora='rgba('+r+', '+g+', '+b+', '+'0.14'+')'
...
//layout of markers
{
x: time,
y: time,
z: data1,
line: {
reversescale: false,
//color: "'"+color+"'"
color: "'rgb("+r+', ' +g+', '+ b+")'",
},
//mode: 'lines',
marker: {
//color: "'"+color+"'",
color: "'rgb("+r+', ' +g+', '+ b+")'",
size: 3,
line: {
//color: "'"+colora+"'",
color: "'rgb("+r+', ' +g+', '+ b+")'",
width: 0.1
},
opacity: 0.8
},
type: 'scatter3d'
}
Both the attempts just give me the standard black dots. When I tried constants that worked fine (something like color:'rgb(100,100,240)'). Is there something I'm missing here? I've console.logged this thing and it doesn't seem to be an issue with the structure of my vars.
You have too many quotation marks around your rgb strings. In order to avoid confusion when concatenating strings, you could also use template strings.
See the working fiddle below.
const r = 0;
const g = 255;
const b = 0;
const color = 'rgb(' + r + ',' + g + ',' + b + ')';
const colorTemplate = `rgb(${r},${g},${b})`;
var trace1 = {
x: [1, 2, 3, 4],
y: [10, 15, 13, 17],
type: 'scatter',
marker: {
color: color
}
};
var trace2 = {
x: [1, 2, 3, 4],
y: [16, 5, 11, 9],
type: 'scatter',
marker: {
color: colorTemplate
}
};
var data = [trace1, trace2];
Plotly.newPlot('myDiv', data);
<head>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
</head>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
The problem with my code is that I'm using vars. By switching to consts, I can fix the thing. I didn't catch the color not changing because for some reason the legend was displaying the right colors, but the markers and lines don't accept it.

Is it possible to fill particular portion between given start & end angle in pie chart in chart.js?

I want a circular chart where i can pass start angle and end angle and the portion between then get filled with some color. Is it possible in chart.js pie chart? And not then is there any other chart which is giving such kind of functionality?
Any help would be appreciated.
You can use Highcharts pie chart and wrote custom function that will 'fill' your chart depending on start angle and end angle you will pass in your point.
Your data may look like that:
data: [{
startAngle: 0,
endAngle: 105,
color: 'green'
}, {
startAngle: 315,
endAngle: 360,
color: 'green'
}]
And your custom function may be similar to this function:
function(chart) {
var series = chart.series[0],
start = series.data[0].startAngle,
updatedData = [start],
pointBetween,
point;
Highcharts.each(series.data, function(p) {
pointBetween = p.startAngle - start;
if (pointBetween !== 0) {
updatedData.push({
y: p.startAngle - start
});
}
point = {
color: p.color,
y: p.endAngle - p.startAngle
}
updatedData.push(point);
start = p.endAngle;
});
updatedData.push({
y: 360 - start
})
series.setData(updatedData);
}
I have made very simple example showing how your chart may look with this function:
http://jsfiddle.net/46vfygqu/

Fill Chart.js bar chart with diagonal stripes or other patterns

I am trying to fill a bar graph with stripes so that it looks like the attached image. Is there a way to do this? How about other patterns?
tl;dr
Just pass a CanvasPattern or CanvasGradient to the dataset's backgroundColor property as the official docs say.
Excuse me, what?
This can be done through a 3rd party library like patternomaly, but if you just want a few simple pattern it is unnecessary since you can easily create a custom function that takes a color and give you back a canvas pattern:
function createDiagonalPattern(color = 'black') {
// create a 10x10 px canvas for the pattern's base shape
let shape = document.createElement('canvas')
shape.width = 10
shape.height = 10
// get the context for drawing
let c = shape.getContext('2d')
// draw 1st line of the shape
c.strokeStyle = color
c.beginPath()
c.moveTo(2, 0)
c.lineTo(10, 8)
c.stroke()
// draw 2nd line of the shape
c.beginPath()
c.moveTo(0, 8)
c.lineTo(2, 10)
c.stroke()
// create the pattern from the shape
return c.createPattern(shape, 'repeat')
}
Then just call it in your datasets (don't forget to add a border if you need that):
datasets: [{
label: 'Good questions',
data: [3, 4, 1, 6, 10],
backgroundColor: createDiagonalPattern('green'),
// create a border with the same color
borderColor: 'green',
borderWidth: 1,
}],
Edge cases
Keep in mind that canvas has anti-aliasing so when you draw stuff around the corners it can mess up your pattern. To mitigate this just draw your lines from an edge.
If you create the diagonal line between the corners like this:
c.beginPath()
c.moveTo(0, 0)
c.lineTo(10, 10)
c.stroke()
Then the pattern wouldn't look seamless because the corner crops off parts so you lose that infinite effect:
Demo
var element = document.getElementById('chart');
var ctx = element.getContext("2d");
function createDiagonalPattern(color = 'black') {
let shape = document.createElement('canvas')
shape.width = 10
shape.height = 10
let c = shape.getContext('2d')
c.strokeStyle = color
c.beginPath()
c.moveTo(2, 0)
c.lineTo(10, 8)
c.stroke()
c.beginPath()
c.moveTo(0, 8)
c.lineTo(2, 10)
c.stroke()
return c.createPattern(shape, 'repeat')
}
var graph = new Chart(element, {
type: 'bar',
data: {
labels: ['jan', 'feb', 'mar', 'apr', 'may'],
datasets: [
{
label: 'Good questions',
data: [3, 4, 1, 6, 10],
backgroundColor: createDiagonalPattern('green'),
borderColor: 'green',
borderWidth: 1,
},
{
label: 'Bad questions',
data: [2, 7, 3, 5, 1],
backgroundColor: createDiagonalPattern('#FF0000'),
borderColor: '#FF0000',
borderWidth: 1,
},
],
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
},
}],
},
},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="chart" ></canvas>
Old question, but now there's the patternomaly add-on :-)
https://github.com/ashiguruma/patternomaly
It contains 21 pre-defined patterns you can use in chart.js.
There is a section on patterns and gradients in the ChartJS documentation that allows to pass a CanvasPattern or CanvasGradient object instead of a string colour.
Read about it here:
http://www.chartjs.org/docs/latest/general/colors.html
Looking at the bar and global options, this doesn't seem possible using only chartjs.
http://www.chartjs.org/docs/#bar-chart-chart-options
http://www.chartjs.org/docs/#getting-started-global-chart-configuration
As chartsjs uses a canvas element to display its elements, you won't be able to implement a CSS solution either. If you really want to do this, you could try editing the chartjs library. Or just pick a solid color.

Style specific bar column with Flot

I'm working with Flot to create a bar chart. However, I need to add special styling to certain columns. Is this possible at all?
My HTML looks like this:
<div id="monthly-usage" style="width: 100%; height: 400px;"></div>
And my JS like this:
somePlot = null;
$(function() {
//Data from this year and last year
var thisYear = [
[3, 231.01],
[4, 219.65],
[5, 222.47],
[6, 223.09],
[7, 248.43],
[8, 246.22]
];
var lastYear = [
[3, 171.7],
[4, 130.62],
[5, 163.03],
[6, 166.46],
[7, 176.16],
[8, 169.04]
];
var usageData = [{
//Usage this year
label: "2014",
data: thisYear,
bars: {
show: true,
barWidth: .3,
fill: true,
lineWidth: 0,
order: 1,
fillColor: 'rgba(194, 46, 52, .85)'
},
color: '#c22e34'
}, {
//Usage last year to compare with current usage
label: "2013",
data: lastYear,
bars: {
show: true,
barWidth: .3,
fill: true,
lineWidth: 0,
order: 2,
fillColor: 'rgba(73, 80, 94, .85)'
},
color: '#49505e'
}];
//X-axis labels
var months = [
[0, "Jan"],
[1, "Feb"],
[2, "Mar"],
[3, "Apr"],
[4, "Maj"],
[5, "Jun"],
[6, "Jul"],
[7, "Aug"],
[8, "Sep"],
[9, "Okt"],
[10, "Nov"],
[11, "Dec"]
];
//Draw the graph
somePlot = $.plot(('#monthly-usage'), usageData, {
grid: {
color: '#646464',
borderColor: 'transparent',
hoverable: true
},
xaxis: {
ticks: months,
color: '#d4d4d4'
},
yaxis: {
tickSize: 50,
tickFormatter: function(y, axis) {
return y + " kWh";
}
},
legend: {
show: false
}
});
var ctx = somePlot.getCanvas().getContext("2d"); // get the context from plot
var data = somePlot.getData()[0].data; // get your series data
var xaxis = somePlot.getXAxes()[0]; // xAxis
var yaxis = somePlot.getYAxes()[0]; // yAxis
var offset = somePlot.getPlotOffset(); // plots offset
var imageObj = new Image(); // create image
imageObj.onload = function() { // when finish loading image add to canvas
xPos = xaxis.p2c(data[4][0]) + offset.left;
yPos = yaxis.p2c(data[4][1]) + offset.top;
ctx.drawImage(this, xPos, yPos);
xPos = xaxis.p2c(data[5][0]) + offset.left;
yPos = yaxis.p2c(data[5][1]) + offset.top;
ctx.drawImage(this, xPos, yPos);
};
imageObj.src = 'path/to/file.png'; // set it's source to kick off load
});
});
Optimally, I would like to insert an icon in bar 5 and 6 that warns the user. Alternatively, I'd like to change the color of bars 5 and 6. Any ideas on how to fix this?
EDIT: I've updated my JS according to Mark's answer which works.
#Mark, how can I position the images correctly. They are a bit off. I need the image inside the red bar and not besides the bar. I'm trying to finetune this but it doesn't seem as if I can use for instance "0.5". I use side by side bars which is different from your version.
xPos = xaxis.p2c(data[4][0]) + offset.left;
yPos = yaxis.p2c(data[4][1]) + offset.top;
You can't do exactly what you ask with standard options, but there are a couple of possible approaches:
Write your own draw method and use the hooks to install it in place of the standard flot drawing code. This obviously entails a lot of work, but you'll have complete control over how to render your data. (That said, I wouldn't recommend it.)
Break your data into two different data sets. One data set would have dummy values (e.g. 0, or whatever your minimum is) for bars 5 and 6. The second data set would have dummy values for all bars except 5 and 6. You could then style the "two" data sets independently, giving each, for example a different color. Graph the two sets as a stacked bar chart with whatever additional styling tweaks are appropriate for your chart.
(As a FYI, there's a fair bit of information and examples at jsDataV.is. Look at the "Book" section; chapter 2 is dedicated to flot.)
flot gives you access to the HTML5 Canvas it's drawing on; so you just add your icon on there yourself. Borrowing from my own answer here.
var ctx = somePlot.getCanvas().getContext("2d"); // get the context from plot
var data = somePlot.getData()[0].data; // get your series data
var xaxis = somePlot.getXAxes()[0]; // xAxis
var yaxis = somePlot.getYAxes()[0]; // yAxis
var offset = somePlot.getPlotOffset(); // plots offset
$.get("someImage.txt", function(img) { // grad some image, I'm loading it from a base64 resource
var imageObj = new Image(); // create image
imageObj.onload = function() { // when finish loading image add to canvas
var xPos = xaxis.p2c(data[4][0]) + offset.left;
var yPos = yaxis.p2c(data[4][2]) + offset.top;
ctx.drawImage(this, xPos, yPos);
xPos = xaxis.p2c(data[5][0]) + offset.left;
yPos = yaxis.p2c(data[5][3]) + offset.top;
ctx.drawImage(this, xPos, yPos);
};
imageObj.src = img; // set it's source to kick off load
});
Example here.
Looks like:

Categories