Set ECG paper-like grid intervals (highcharts.js) - javascript

I need to show an ECG result with highcharts which is basically a simple line/spline diagram but with a little tweak. The ECG paper looks like this: https://cdn.printablepaper.net/samples/ECG_Paper.png
And this is what I want to achieve with hightcharts too. One little square means 0.1 mV and 0.04 s, so one big square is 0.5 mV and 0.2 s. I know there is a tickInterval setting, but that just does not work in this scenario, I think the intervals are too small for it, because it randomly hides grid lines.
The optimal solution would be that if there is a need for hiding grid lines (too broad data), it only hides the small squares. To try not to confuse the users with this, we also would like to colorize grid lines differently, so every fifth line should be red, others gray.
Is this possible with highcharts?

minorTicks functionality seems to be ideal for this case. Use this option along with minorTickInterval. Configuration for both axes will look like this:
var axesOptions = {
tickInterval: 0.5,
minorTicks: true,
minorTickInterval: 0.1,
gridLineWidth: 1,
gridLineColor: 'red'
};
Live demo: http://jsfiddle.net/kkulig/6dbnmyxf/
If minor ticks are not visible on any axis this code will hide them on all axes (and restore them when needed):
chart: {
events: {
render: function() {
var axes = this.axes,
showMinorTicks = true;
// find if minor ticks are present on both axes
axes.forEach(function(a) {
console.log(a.minorTicks);
if (Object.keys(a.minorTicks).length === 0) {
showMinorTicks = false;
}
});
// hide/show ticks
axes.forEach(function(a) {
for (var key in a.minorTicks) {
var mt = a.minorTicks[key].gridLine;
showMinorTicks ? mt.show() : mt.hide();
}
});
}
}
},
API references:
https://api.highcharts.com/highcharts/xAxis.minorTickInterval
https://api.highcharts.com/highcharts/xAxis.minorTicks

Related

Combine two line charts into one in ChartJS

What I have
I've created 2 graphs like shown in the first image below. I need these 2 graphs to be perfectly aligned on the 0 axis (which is shown in the section below).
Wanted result
In the image below, the graphs are perfectly aligned and should therefore contain 2 different datasets. One should be placed on the left grid and the other one on the right grid.
The options I thought of
I have not seen a way to implement this in ChartJS. Therefore, I thought of some possible solutions that could solve this issue in a different way.
Because both datasets don't have negative values, I could convert the left dataset to negative values (on the x-axis). The downside is that the range would be incorrect. Also, this would give issues with the popup message if you hover your mouse on a value.
Maybe, two canvas objects can be combined that could result in the wanted result
Question
Is there a way to achieve the wanted result within ChartJS? Or can I perform some wizardry to combine two charts nicely into one? Or should I move to the D3.js library for this particular graph?
I did not find a way to "nicely" implement this. I've changed the source code of the ChartJS library to implement this.
This method uses 2 different graphs that render in 2 different canvas objects. These canvas objects are placed side-by-side to let it look like one graph.
This is my result (The code in here is not exactly like the graph in the picture because I changed it to my specific needs).
I've added a custom variable in the options.layout category named sidePadding.
The code of creating the graph:
new Chart(document.getElementById(this.element), {
type: 'scatter',
data: {
datasets: [{
borderColor: this.lineColor,
borderWidth: 1,
pointBackgroundColor: '#000',
pointBorderColor: '#000',
pointRadius: 0,
fill: false,
tension: 0,
showLine: true,
data: [],
}],
},
options: {
aspectRatio: 0.3,
maintainAspectRatio: false,
responsive: true,
layout: {
sidePadding: {
left: 3,
right: -3,
}
}
}
});
The padding on the sides is by default 3 so it does not touch the border of the canvas. I wanted it to touch the border of the canvas, and therefore set it to -3.
Two different graphs need to be created, one for the left side and one for the right side.
The left graph should have the sidePadding.right set to -3. The right graph should have the sidePadding.left set to -3.
Changes in the ChartJS library
I've worked with ChartJS version v2.9.3. This method will possibly not work anymore in a new version.
I've changed the following in the Chart.js file:
Replace (line number around 11800):
// Adjust padding taking into account changes in offsets
// and add 3 px to move away from canvas edges
me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + 3;
me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + 3;
to:
// Adjust padding taking into account changes in offsets
// and add 3 px to move away from canvas edges
var layoutOptions = chart.options.layout || {};
var sidePadding = helpers$1.options.toPadding(layoutOptions.sidePadding);
me.paddingLeft = Math.max((paddingLeft - offsetLeft) * me.width / (me.width - offsetLeft), 0) + sidePadding.left;
me.paddingRight = Math.max((paddingRight - offsetRight) * me.width / (me.width - offsetRight), 0) + sidePadding.right;
Also you must add the sidePadding.left and sidePadding.right parameter to ChartJS.
This can be done by changing (line number around 7180):
core_defaults._set('global', {
layout: {
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
// Custom added parameter
sidePadding: {
left: 3,
right: 3,
}
}
});
to:
core_defaults._set('global', {
layout: {
padding: {
top: 0,
right: 0,
bottom: 0,
left: 0
},
// Custom added parameter
sidePadding: {
left: 3,
right: 3,
}
}
});
Note
This is probably not the best way to achieve this. Changing the ChartJS library is bad-practice and updating the library could revert these changes.
If someone has a better way to achieve this, please post your methods or comment below.

Highcharts: Implement a second y-axis related to the first y-axis

For my graph, I have a single y-axis (y1), and I am trying to add a second axis (y2) which is a scaled version of y1.
Simply put, is it possible to do a graph like this. But I want a second axis, with the same scaling ratio, but in different units (i.e. that is multiplied by some k).
I have tried to just change the label on the yAxis:
` labels: {
format: '$ {value* price} ',
style: {
color: Highcharts.getOptions().colors[0]
}
},`
But this hacky way does not seem to work for me.
In my case, I have a graph of percent change on y1, is it possible to put the price on y2?
I do not want to add another set of lines since I am already using 10, which would mean I would need 20 lines in total
Any help would be greatly appreciated!
You need to link the second axis to the first by linkedTo property and use formatter function to display some custom scale:
yAxis: [{}, {
opposite: true,
labels: {
formatter: function() {
return this.value * 1000
}
},
linkedTo: 0
}]
Live demo: http://jsfiddle.net/BlackLabel/j83pghta/
API Reference:
https://api.highcharts.com/highcharts/yAxis.linkedTo
https://api.highcharts.com/highcharts/yAxis.labels.formatter

stackLabels with separate data in Highcharts

Good afternoon, I'm putting together a chart that uses Highcharts stackLabels, it's exactly the same as this: JSFiddle however I do not want it to display the sum of the column values, I want it to appear item by item one on top of the other, same as the print I simulated below:
In order to set your stackLabels as you expecting, you need to use stackLabels.formatter function to calculate the labels values manually.
Next thing you've to do is make a little shifting on the labels. You are able to do it by stackLabels.y field.
At the end, don't forget about the stackLabels.useHTML parameter set equal to true. It makes the <span> and </br> elements will appear correctly. Please take a look at code below:
yAxis: {
stackLabels: {
style: {
color: 'red'
},
enabled: true,
useHTML: true,
formatter: function() {
var firstLabel = Math.round((this.points[0][1] - this.points[0][0]) * 10) / 10,
secondLabel = this.points[0][0]
return `<span>${firstLabel}</span></br>
<span>${secondLabel}</span>`
},
y: -15
}
}
You can read more about parameters mentioned above here:
https://api.highcharts.com/highcharts/yAxis.stackLabels.formatter
https://api.highcharts.com/highcharts/yAxis.stackLabels.y
https://api.highcharts.com/highcharts/yAxis.stackLabels.useHTML
Live example: http://jsfiddle.net/r01nkgyn/

How to dynamically set pie piece text colour on a C3 chart

I have a C3 pie chart similar to this online example. In my case I set the data.colors based on incoming external data.
My setup looks like the following (I pass in the colors to use)...
this.pieChart = generate({
data: {
columns: columns,
colors: colours,
type: 'pie',
onclick: (e) => {
this.handlePieClick(e.id);
},
},
bindto: '#pie-chart',
tooltip: {
show: false
},
transition: {
duration: 1000
},
legend: {
item: {
onclick: id => {
this.handlePieClick(id);
}
}
},
});
My app has not control over these colors. When the colors are light, the white text is hard to see. I may have a mixture of light and dark colours. What I need to do is examine each color and then be able to set the text either white or black for each pie piece text color, however I can see no callback function I can override to do this.
Does anyone know a way to do this? The big thing being I have no control over the colors coming in, and need to set the text colors in code once I have the color data.
Thanks in advance for any help!
Use the onrendered: option in the c3 configuration
In there, loop through the colours, and for each
Construct a new colour that shows up against the pie colour
Apply it to the text in the right segment using the class definitions (doable as the colour map and the pie segments element classes both contain the names of the data series)
http://jsfiddle.net/shxLfss3/2/
onrendered: function () {
var colEntries = d3.entries(this.config.data_colors);
colEntries.forEach (function (colEntry) {
// get pie segment colour, make a contrasting colour from it
var hsl = d3.hsl(colEntry.value);
hsl.l = hsl.l > 0.5 ? 0 : 1; // make black if light, white if dark
var newCol = hsl.toString();
// apply that color to the segmenbt's text
var segment = d3.select(this.config.bindto+" .c3-chart-arc.c3-target-"+colEntry.key);
segment.select("text").style("fill", newCol);
}, this);
}

How to disable Stacking on an individual series using Primefaces jQplot implementation

I am using Primefaces 6 for my companys charting needs, which relies on jQplot.
For a project, I am trying to overlay a line chart on a stacked bar chart with negative values, to obtain something like this:
The problem is that when I try to add a linechartseries to the same model as the two barchartseries , the linechart becomes a part of the stack when setting setStacked(true); on the model, because Primefaces seems to not allow individual disabling of stacking on series, only per model. So I end up with this when rendering the chart with
<p:chart type="bar" model="#{backingBean.cartesianChartModel}"/>
After some investigation I have notoced that jQplot is capable of disabling Stacking on individual series by passing disableStack : true in the JS options, so the question is if it's posssible to override this in some way on the rendered page,either via PF or via some JS hack? I feel that using the extender only apples to the entire model?
Related issues: Disable individual stacking
By pouring through the documentation I found a solution to the problem, if not the question:
It seems that Primefaces allows for individual series to be excempt from the stack in the series creation in this version, by passing
LineChartSeries.setDisableStack(true);
Simple as that.
I guess it may be possible. I used the extender functionality for some jqPlot hacks in the past.
In my case, for example, I had a Donut Chart defined with an extender function as follows:
private void createDonutModel() {
donutModel = new DonutChartModel();
donutModel.setLegendPosition("s");
donutModel.setLegendPlacement(LegendPlacement.OUTSIDE);
donutModel.setSliceMargin(4);
donutModel.setDataFormat("value");
donutModel.setShadow(false);
donutModel.setExtender("donutExtender");
donutModel.setSeriesColors("B81C40, FFA600, 79B54A");
}
The corresponding javascript was doing some changes to the jqPlot:
/**
* Customized jqPlot JQuery layout of the Donut Chart for Status Dashboard.
*/
function donutExtender() {
this.cfg.seriesDefaults = {
// make this a donut chart.
renderer:$.jqplot.DonutRenderer,
rendererOptions:{
thickness: 26,
ringMargin: 0,
fill: true,
padding: 0,
sliceMargin: 4,
// Pies and donuts can start at any arbitrary angle.
startAngle: -90,
showDataLabels: false,
// By default, data labels show the percentage of the donut/pie.
// You can show the data 'value' or data 'label' instead, or 'percent'
dataLabels: 'value',
shadow: false
}
}
this.cfg.gridPadding = {
top: 0, right: 0, bottom: 0, left: 0
}
this.cfg.legend = {
show: false
}
this.cfg.grid = { drawBorder: false,
shadow: false,
background: "transparent"
};
}
So you may try something like this in your case ?
Leave the extension configuration of your series empty, except for the one you are interested in...
function chartExtender() {
this.cfg.series = [
{ //...
},
{ // ...
},
{
disableStack: true
}
]
}
Worth having a shot ...

Categories