Connecting Two Datasets in Chart.js Into a Single Line - javascript

I have two separate datasets in Chart.JS, one represents a selection of historical data, the other the predictions based on that data. Here is what the chart currently looks like. Historical data is black, prediction is blue:
I'd like to maintain the two as separate datasets, but connect them so the chart displays as a single line. In a previous version I accomplished this by adding a datapoint to the prediction dataset that is an exact duplicate of the final datapoint in the historical set, but this creates an inaccurate redundancy I want to avoid.
Unless there's some chart.js setting I've been unable to find I'm worried I might have to register a plugin to do this, which seems like it would become needlessly clunk. Thanks.

Managed to solve the problem, posting to help anyone with a similar problem.
I ended up creating a chart.js plugin that draws a line finds the X and Y coordinates of the last historical point and then draws a line between that and the X and Y coordinate of the first prediction point. After beating my head against it for a while I can't believe it was so simple.
I'm posting the code down below - this is a react application so it may not fit cleanly into every use case. The JSFiddle isn't setup to work, but you can see how to grab the point data which was the hardest part for me!
//Grab the chart meta data and identify the x and y coordinates of the
appropriate points
var meta = chartInstance.getDatasetMeta(0);
var predictionMeta = chartInstance.getDatasetMeta(1);
var chartFirstPointX = meta.data[this.props.actualData.length - 1]._model.x;
var lastActualY = meta.data[this.props.actualData.length - 1]._model.y;
var predictionFirstPointX = predictionMeta.data[0]._model.x;
var predictionFirstPointY = predictionMeta.data[0]._model.y;
Chart.pluginService.register({
id: 'forecastLine',
beforeDraw: function(chartInstance) {
var ctx = chartInstance.chart.ctx,
yTop = chartInstance.chartArea.top,
yBottom = chartInstance.chartArea.bottom,
firstPredictionX = predictionFirstPointX,
firstPredictionY = chartInstance.config.data.datasets[1].data[0].y;
ctx.fillStyle = '#FAFAFA';
ctx.fillRect(0, 0, chartInstance.chart.width, chartInstance.chart.height);
//draw a vertical line separating the two datasets, then draw a line
connecting them
ctx.save();
ctx.beginPath();
ctx.moveTo(chartFirstPointX, yBottom);
ctx.lineTo(chartFirstPointX, yTop);
ctx.moveTo(chartFirstPointX, lastActualY);
ctx.lineTo(predictionFirstPointX, predictionFirstPointY);
ctx.lineWidth = 2;
ctx.strokeStyle = '#68D1FE';
ctx.stroke();
ctx.restore();

Related

Blazor chart vertical line

I'm trying hard to make dashboard with Blazor. But I'm new to js and css so feel some hard.
Currently I'm implementing chart components and I'd like to add vertical line which is moved automatically like below image.
I searched a lot of pages, however I didn't even know what to do.
I'm not asking you to write code, but it would be nice to tell me about sites, methods or libraries that I can refer to.
Situation
I'm using Blazorise Chart .net core 5.0
My tries
Create a data set to be expressed like a vertical line, set a timer, and move the vertical line data once per second.(The StateHasChange() function should be continues to be called.
Draw vertical line using css(But I couldn't know how to move it)
Thank you for reading.
Since Blazorise uses ChartJs internally to draw the charts, you might be able to use the ChartJs annotation plugin. You might need to leverage a JavaScript Interop method to inject the plugin options.
Alternatively you can simply draw the line yourself, as the Chart renders in a <canvas /> element:
Heres a piece of code i use (Personally I use ChartJs.Blazor, and my chart is a Bar chart, so you will need to adapt the code to fit your own needs and library)
annotationConfig: function (canvasID, indexToDrawAt) {
var originalLineDraw = Chart.controllers.bar.prototype.draw;
Chart.helpers.extend(Chart.controllers.bar.prototype, {
draw: function () {
originalLineDraw.apply(this, arguments);
var chart = this.chart;
var ctx = chart.chart.ctx;
var index = chart.config.options.lineAtIndex;
if (index) {
var xaxis = chart.scales['x-axis-0'];
var yaxis = chart.scales['y-axis-0'];
var x1 = xaxis.left;
var y1 = yaxis.getPixelForValue(index);
var x2 = xaxis.right;
var y2 = yaxis.getPixelForValue(index);
ctx.save();
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.strokeStyle = '#3C3E4F';
ctx.lineTo(x2, y2);
ctx.stroke();
ctx.restore();
}
}
});
let chart = window.ChartJsInterop.BlazorCharts.get(canvasID);
chart.options.lineAtIndex = indexToDrawAt;
chart.update();
}
This code adds a option to the chart called lineAtIndex, which when set, draws a line across the canvas at the provided index.
I use the method as such:
if (ShowAvgLine)
{
if (currentAverage != 0.0)
{
jSRuntime.InvokeVoidAsync("generalInterop.annotationConfig", _config.CanvasId, currentAverage);
}
}
If you get the above to work with your library, you might try your previous solution of making timed calls to the interop method to move it.
I don´t know Blazorise Chart, maybe there is also a built in solution.
If you want to solve it in a pretty hacky way, you could just create a <div> which has the same height as your chart. Then you can use the top and left CSS properties to set the div at the leftmost position over the chart (you may have to give the div a z-index and a position: absolute so that it appears over the chart). The positions should be saved in a C# variable. Now you can implement a function which gets called every second which increments the left value and calls the StateHasChanged() method.
This is a really hacky solution but it should work as expected. But you have to beware, that on different devices the chart can be displayed in a other way, so maybe the div won´t align anymore.

HighCharts: How to draw a straight line in multiple axes like plotLines with fixed X-axis with different values

I'm using highcharts for my project. I've used multiple axes for drawing the chart. What I want is there should be a straight line on fixed x-axis even though with different values.
I want to have something like plotLines which will be straight and fixed on the x-axis but still shows different values I put it.
I would create separate series to achieve that. I think it's simpler solution than playing around with renderer. Demo: http://jsfiddle.net/6wo5mzo4/
(function(H) {
var seriesTypes = H.seriesTypes;
seriesTypes.fakeLine = Highcharts.extendClass(seriesTypes.line);
seriesTypes.fakeLine.prototype.translate = function() {
var s = this;
seriesTypes.line.prototype.translate.call(s);
H.each(s.points, function(point) {
point.plotY = s.chart.plotWidth / 2; // display in the middle - note: plotWidth, not plotHeight, because chart is inverted
});
}
})(Highcharts);
However, I still have doubts how useful such chart will be. I would also suggest to review the requirements.

Equivalent of canvas quadraticCurveTo in SVG

I am working on a plugin to allow "natural looking" signatures to be drawn using mouse or touch. When confirmed by the user, the result will be a stored SVG that can then be displayed in place of the "Click to sign" button.
The attached JSFiddle http://jsfiddle.net/TrueBlueAussie/67haj4nt/3/ shows a testbed for what I am trying to do. The SVG generated image should look close to the original canvas paths.
The first div contains a canvas, in which I draw some multiple-segment lines (e.g. paths). Using quadraticCurveTo, and a midpoint for the control point, I draw the lines with smooth curves. This works just fine.
The key part of the curved line drawing is:
$.each(lines, function () {
if (this.length > 0) {
var lastPoint = this[0];
ctx.moveTo(lastPoint[0], lastPoint[1]);
for (var i = 1; i < this.length; i++) {
var point = this[i];
var midPoint = [(lastPoint[0] + point[0]) / 2, (lastPoint[1] + point[1]) / 2];
ctx.quadraticCurveTo(lastPoint[0], lastPoint[1], midPoint[0], midPoint[1]);
lastPoint = point;
}
// Draw the last line straight
ctx.lineTo(lastPoint[0], lastPoint[1]);
}
});
I have tried multiple options for SVG generation of the same output, but I am stumped on how to convert the same sets of points to equivalent curved lines. Quadratic Beziers require "proper" control points, but I would prefer to use the far simpler mid-points if possible.
Any ideas? Is this possible or will I have to convert both to use Beziers with calculated control point(s). Is there a simple way to calculate control points that will do the same job?
jQuery or raw JavaScript solutions are fine, but you need to demonstrate in the JSFiddle provided :)
It's just a bug in your code. You are not updating lastPoint in your SVG version.
http://jsfiddle.net/67haj4nt/4/
And if you update the SVG version to match the canvas version, you get identical curves.
http://jsfiddle.net/67haj4nt/5/

Draw logarithmic scale html canvas and javascript no libraries

I'm really struggling with this project of mine. I need to draw a logarithmic scatter scale and plot data on it.
This is the function that needs to be adjusted to my example ( see fiddle )
for(var i = 0; i < maxHeight; i += axisStep) {
// get normalized value, multiply with canvas height and reverse axis
var y = h - myLog(i, maxHeight) * h;
// show axis mark each 100
ctx.moveTo(0, y);
ctx.lineTo(30, y);
}
ctx.lineWidth=2;
ctx.stroke();
ctx.fillStyle = "red";
The idea is to build something like this and data is taken from: here the scales thus need to be logarithmic to actually view the data. My problem is now is too get the correct axis and plot them from the data
I want to use the 'sumlogp0' and 'sumlogp2' field for the x and y axis to plot the scatter plot. Any ideas or tutorials or snippets that can help me?
This is my fiddle so far link

Best way to use multiple canvases in single page

I have a dynamic graph with real time array values which using canvas to plot graph which is working fine, but I want over 50 unique graphs in single page with 50 different array of values.
What is the best way to achieve this? I have goggled it got to know that using multiple canvas application performance may slow, but my application is for desktops not for mobiles.
-- Edit--
I did some thing like this to get my requirement..
http://jsfiddle.net/atluriajith/v4Rhv/
graphs are plotting properly upto 100, after that the speed of the graphs are getting slow. Is this the right way what i did?
-- Edit--
You can use a single canvas that is big enough to hold the graphs.
Then you can use translate and clip to draw the graphs into isolated areas on that canvas (aka. "virtual canvases"). Depending on how you intend to draw the graphs even this may not be necessary.
This only needs a single clear before you redraw the graphs.
In this case you would probably organize it into 5 x 10 cells.
For example (disclaimer: not tested, meant for example):
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d'),
cols = 5,
rows = 10,
cellWidth = 200,
cellHeight = 100;
canvas.width = cols * cellWidth;
canvas.height = rows * cellHeight;
document.body.appendChild(canvas);
...
/// draw a graph:
var x = cellX * cellWidth, /// cell indexes to position
y = cellY * cellHeight;
ctx.save();
ctx.rect(x, y, cellWidth, cellHeight);
ctx.clip();
/// draw graph here
ctx.restore();
If you can contain the graph within x, y, and cellWidth/cellHeight the clipping is not necessary.
Before you continue I would think it worthwhile to verify that you have performance issues.
"Premature optimization is the root of all evil"
One fairly straight forward solution is simply to reduce the number of canvasses you're rendering. Make your UI such that you never render more canvasses than you can reasonable support with a given performance. No one needs to see 50 graphs at once, that is simply too much information.

Categories

Resources