Add data to existing chart series in Lightning Chart - javascript

I have a react js application in which, I have used a point series to plot data in chart.
I got the chart to display. But, I want real time data to be binded to chart.
Eg: If a chart contains 10 points on loading, it is marked to chart. Then in 5 sec interval, I will be getting datas from socket which I have to add to the existing chart. So now, there has to be 15 points in charts and the chart has to move from left to right as the data comes in.
Here is my code that is used to bind data at the starting
import {
lightningChart,
AxisTickStrategies,
LegendBoxBuilders,
Themes,
PointShape,
translatePoint,
AxisScrollStrategies
} from "#arction/lcjs";
const dateOrigin = new Date(startingDate);
const chart = lightningChart()
.ChartXY({
container: id,
theme: Themes.lightNew,
columnIndex: 0,
columnSpan: 1,
rowIndex: 0,
rowSpan: 1
})
.setTitle(id.split(" ")[1]);
const originDate = new Date(moment().subtract(1, 'days'))
const xAxis = chart.getDefaultAxisX().setTickStrategy(
// Use DateTime TickStrategy for this Axis
AxisTickStrategies.DateTime,
// Modify the DateOrigin of the TickStrategy
// (tickStrategy) => tickStrategy.setDateOrigin(dateOrigin)
(tickStrategy) => tickStrategy.setDateOrigin(originDate)
);
const yAxis = chart
.getDefaultAxisY()
.fit(true)
.setScrollStrategy(undefined)
.setInterval(-20, 20)
.setTitle("");
const series = chart
.addPointSeries({
xAxis: xAxis,
yAxis: yAxis,
pointShape: PointShape.Circle
})
.setName("Actual");
const dataFrequency = ((((600 - 600 * 250) - (60 * 60 * 100)) - (1000 * 2)) - 900)
//0-24 hrs
chart.getDefaultAxisX().setInterval(92 * dataFrequency, 60);
series.add(
sensorData.map((point) => ({
//x: new Date(point.x).getTime() - startingDate.getTime(),
x: new Date(point.x).getTime() - new Date(startingDate).getTime(),
y: point.y, // * 1000,
}))
);
Please let me know how the incomming data from socket has to be added to the existing points in charts.
Thanks in Advance.

You can append more data points to a PointSeries with another call to add method, same way as you use it for the initial data.
So keep a reference to the PointSeries object and when you receive the new data from websocket just use the add method in exactly same way as you did before to append the data on top of the previous data points.
For "moving the chart from left to right as data points come in", you should configure the X axis scroll strategy to be progressive. Please refer to this ECG chart example for code reference (line 44, chart.getDefaultAxisX()...).
For means of Stack Overflow historical completeness I'll also add the highlighted code line here:
// Configure X Axis as progressive scrolling with value interval of 1000
chart.getDefaultAxisX().setInterval(0, 1000).setScrollStrategy(AxisScrollStrategies.progressive)

Related

How to change the scale to an arbitrary number using lightningchart

When you use lightningchart to set the scale to be displayed, the default size is 5 or 10 increments.
Is it possible to change this to an arbitrary increments (2 increments, 3 increments, etc.)?
Please let me know.
The Axis automatically calculates the increment size depending on the level of zooming for the Axis and space available for each tick label. We're looking towards improving our Axis behavior with the next Major release this Summer.
In the meanwhile, it is possible to create this behavior manually by using customTicks.
/*
* LightningChartJS example that showcases a simple XY line series.
*/
// Import LightningChartJS
const lcjs = require('#arction/lcjs')
// Extract required parts from LightningChartJS.
const {
lightningChart,
ColorRGBA,
UIElementBuilders,
emptyFill,
emptyLine,
emptyTick,
SolidLine,
SolidFill
} = lcjs
// Create a XY Chart.
const chart = lightningChart().ChartXY()
.setTitle('XY Chart with custom tick interval')
// Get the default X Axis and cache it
const defXAxis = chart.getDefaultAxisX()
// Set the default Axis style as emptyTick, hiding the default Ticks created
// by the AxisTickStrategy
defXAxis.setTickStyle(emptyTick)
// Iterate an arbitrary amount of ticks, creating a new customTick with interval of 2
for ( let i = 10; i <= 90; i += 2 ) {
// Add a new custom tick to the X Axis
defXAxis.addCustomTick(
// Modify the textBox to hide its background and border
UIElementBuilders.PointableTextBox.addStyler(
(styler) => {
styler.setBackground(
(bg)=>
bg
.setFillStyle(emptyFill)
.setStrokeStyle(emptyLine)
) }
))
// Set the tick position
.setValue( i )
// Make the Grid stroke less visible
.setGridStrokeStyle(
new SolidLine( {
thickness: 1,
fillStyle: new SolidFill( { color: ColorRGBA(200, 200, 200, 50)})
})
)
}

Chartjs animate x-axis

I want to use a chartjs linechart to visualize my data points. Chartjs seems to animate the graph by default, but it does not animate the values on the x-axis. The x-axis only move in discrete steps.
Is there any way to enable animation on the axis also?
Thanks!
As far as I am aware, ChartJS does not support x-axis animation out-of-the-box. So you'll have to hack it. There are several ways to possibly do this, but the following methods seems to work.
If You Want to Animate the Data On the X-Axis
When a chart is updated, the following steps occur: 1) The axes are drawn, and then 2) a draw() function is called to draw the data. There are different draw() functions for different chart types, and the function for line charts is Chart.controllers.line.prototype.draw. The draw() functions take one argument, which I will call animationFraction, that indicates how complete the animation is as a fraction. For instance, if an animation is 5% complete, animationFraction will be 0.05, and if an animation is 100% complete (i.e. if the chart is in its final form), animationFraction=1. The draw() function is called at each step of the animation to update the data display.
One hack to animate the x-axis then is to monkey-patch the line chart draw() function to translate the canvas in the horizontal dimension at every draw step:
var hShift = (1-animationFraction)*ctx.canvas.width;
hShift is the horizontal shift in pixels of the chart. As defined above, the data will sweep in from the right; if you want it to sweep in from the left, you can make the above negative. You then save the canvas context state, transform the canvas using hShift, draw the chart data, and then restore the canvas to its original state so that on the next animation frame the axes will be drawn in the correct spot:
ctx.save();
ctx.setTransform(1, 0, 0, 1, hShift, 0);
ctx.oldDraw.call(this, animationFraction);
ctx.restore();
In the above, this refers to the chart object, and oldDraw refers to the original line chart drawing function that was saved previously:
var oldDraw = Chart.controllers.line.prototype.draw;
You can additionally setup your new draw() function to read new animation options that allow you to set whether the x-axis and y-axis are animated:
var oldDraw = Chart.controllers.line.prototype.draw;
Chart.controllers.line.prototype.draw = function(animationFraction) {
var animationConfig = this.chart.options.animation;
if (animationConfig.xAxis === true) {
var ctx = this.chart.chart.ctx;
var hShift = (1-animationFraction)*ctx.canvas.width;
ctx.save();
ctx.setTransform(1, 0, 0, 1, hShift,0);
if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
ctx.restore();
} else if (animationConfig.yAxis === true) {
oldDraw.call(this, animationFraction);
} else {
oldDraw.call(this, 1);
}
}
You can then create a line chart with both axes animated with:
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: {
duration: 5000,
xAxis: true,
yAxis: true,
}
}
});
See https://jsfiddle.net/16L8sk2p/ for a demo.
If You Want to Animate the X-Axis Limits
If you want to animate the x-axis limits--i.e. move the data, axis ticks, and tick labels, then you can use the following strategy. It's a bit quirky, so it might take some effort to work out the kinks for any given use-case, but I believe it should work generally. First, you'll need to convert the line plot to a scatter plot. Line charts have categorical x-axes that move in steps, so you can't set the axis limits to be between ticks, which is what you'll need to do to get the animation. So you'll need to use a line scatter plot instead, since scatter plots can have arbitrary axis limits. You can do this by numbering each data point, and assigning that number to the x-value for that data point. For instance, to generate a random dataset, you could do:
var DATA_POINT_NUM = 58;
var data = {
labels: [],
datasets: [
{
data: [],
},
]
}
for (var i=0; i<DATA_POINT_NUM; i++) {
data.datasets[0].data.push({ x: i,
y: Math.random()*10
});
data.labels.push(String.fromCharCode(65+i));
}
You'll then need to write a function to convert between the assigned x-values of your data points, and the data point labels (i.e. the categories that will be on the charts x-axis):
function getXAxisLabel(value) {
try {
var xMin = lineChart.options.scales.xAxes[0].ticks.min;
} catch(e) {
var xMin = undefined;
}
if (xMin === value) {
return '';
} else {
return data.labels[value];
}
}
where lineChart is our Chart object, which will be defined below. Note that ChartJS draws the chart slightly differently if there's a label at x-axis's minimum value, so you'll need to write this function to return an empty string if the value==the minimum value of the x-axis. You can then define the Chart object:
var lineChart = new Chart(ctx, {
type: 'line',
data: data,
options: {
animation: false,
scales: {
xAxes: [{
type: 'linear',
position: 'bottom',
ticks: {
min: 0,
max: 10,
callback: getXAxisLabel, // function(value) { return data.labels[value]; },
autoSkip: false,
maxRotation: 0,
},
}]
}
}
});
ticks.callback is set to our getXAxisLabel function above. When ChartJS draws the x-axis, it will pass the x-values of the data points to the callback function and then use the resulting string as the value on the x-axis. In this way, we can draw a scatter chart like a line chart. I've also set autoSkip=false and maxRotation=0 to make sure the axis labels get drawn in a consistent way.
You can then animate the chart by adjusting the x-axis ticks.min and ticks.max values and calling the chart's .update() method. To illustrate this, the code below scans along the charts x-axis, showing ten data points at a time.
var xMin = 0; // Starting minimum value for the x-axis
var xLength = 10; // Length of the x-axis
var animationDuration = 5000; // Duration of animation in ms
// Calculate animation properties
var framesPerSec = 100;
var frameTime = 1000/framesPerSec;
var xStep = (DATA_POINT_NUM-xMin+xLength)/(animationDuration/1000*framesPerSec);
function nextFrame() {
var xMax = xMin+xLength;
if (xMax < DATA_POINT_NUM-1) {
if (xMax+xStep > DATA_POINT_NUM-1) {
xMax = DATA_POINT_NUM-1;
xMin = xMax-xLength;
}
lineChart.options.scales.xAxes[0].ticks.min = xMin;
lineChart.options.scales.xAxes[0].ticks.max = xMax;
lineChart.update();
setTimeout(nextFrame, frameTime);
xMin += 0.1;
}
}
nextFrame();
Putting it all together: https://jsfiddle.net/qLhojncy/
I am no expert in javascript but I found an example for Chartjs that, when inserted a new data point, updates the x-axis via animation as it seems, maybe it helps you: example.
Example source: sitepoint.com

Pass parameter to c3js tooltop after .generate() and before .load()

I'm trying to graph out metrics that don't have any relation to one another, so instead of plotting out the actual values, I've calculated an alternate set of values that are scaled between 0-1 like a percentage.
For example: [1, 2, 5] => [0.2, 0.4, 1]
So now I have 2 sets of data - the original and scaled versions. I have the scaled version plotting on to my graph just fine, but I want the tooltip to show the original value to the user. See what I mean?
I checked out http://c3js.org/samples/tooltip_format.html, which shows you can set tooltip as a function when you initially generate the C3 object. But I want to change the tooltip later on after I recalculate my original/scaled values and re-load() the graph.
All attempts I've made to explicitly change myGraph.tooltip.format.value = function (...) {...} after initially setting myGraph = C3.generate({...}) have been unsuccessful.
Any ideas how I can accomplish this without having to regenerate the graph from scratch every time?
You need to override internal.getTooltipContent
var data = ['data1', 30000, 20000, 10000, 40000, 15000, 250000];
// simple fake data
var fakeData = data.map(function (d, i) {
return i ? (d / 100) : d;
})
var chart = c3.generate({
data: {
columns: [
fakeData,
['data2', 100, 200, 100, 40, 150, 250]
],
}
});
// do code to take over mars and plant potatoes
// save the original
var originalGetTooltipContent = chart.internal.getTooltipContent;
chart.internal.getTooltipContent = function (data, defaultTitleFormat, defaultValueFormat, color) {
// we modified the first series, so let's change that alone back
var originalValue = {
id: data[0].id,
index: data[0].index,
name: data[0].name,
// unfaked
value: data[0].value * 100,
x: data[0].x
};
var originalValues = data.map(function (d, i) {
return i ? d : originalValue;
})
return originalGetTooltipContent.call(this, originalValues, defaultTitleFormat, defaultValueFormat, color)
}
I assume you are already doing something about the scaled y axis label?
Fiddle - http://jsfiddle.net/puf248en/
Thanks potatopeelings,
I did turn out solving this one by simply loading the form data in all at once, and then programmatically show/hide certain metrics. So that allowed me to use all the generate() options as intended.
Did try out your solution, and it seemed to do the trick till I found the simpler option. Thanks!

Trouble using DC.js (crossfilter and d3 convenience library) - bar chart not showing values

I am using this library: Dimensional Charting to build some relatively standard charts that need CrossFilter functionality.
I have been following the examples but they aren't working for me.
Here is my code:
var dashData = crossfilter(data.report),
dataByHour = dashData.dimension(function(d){ return d3.time.hour(new Date(d.timestamp))}),
totalByHour = dataByHour.group().reduceSum(function(d) { return d.amount }),
dc.barChart("#graphTimeOverview")
.width(990) // (optional) define chart width, :default = 200
.height(250) // (optional) define chart height, :default = 200
.transitionDuration(500) // (optional) define chart transition duration, :default = 500
.margins({top: 10, right: 50, bottom: 30, left: 40})
.dimension(dataByHour) // set dimension
.group(totalByHour) // set group
.elasticY(true)
.centerBar(true)
.gap(1)
.x(d3.time.scale().domain([new Date(data.report[0].timestamp), new Date(data.report[(data.report.length - 1)].timestamp)]))
.round(d3.time.hour.round)
.xUnits(d3.time.hours)
.renderHorizontalGridLines(true);
dc.renderAll();
I know the crossfilter data is working correctly, here is a sample of the group:
totalByHour:
[ {key:(new Date(1361746800000)), value:6170.17},
{key:(new Date(1361678400000)), value:3003},
{key:(new Date(1361581200000)), value:2350.42},
{key:(new Date(1361667600000)), value:1636.19},
etc...
]
Unfortunately all this gets me is an empty graph, it seems to compute the y-axis correctly, so it would seem to me that it can read the data, however I never see any bar values:
Maybe the data.report array is not sorted by timestamp (the sample provided is unsorted). In your code, you assume that those values are sorted. You can try using
// Compute the timestamp extent
var timeExtent = d3.extent(data.report, function(d) { return d.timestamp; });
dc.barChart("#graphTimeOverview")
// more settings here
.x(d3.time.scale().domain(timeExtent.map(function(d) { return new Date(d); })))
.round(d3.time.hour.round)
.xUnits(d3.time.hours)
.renderHorizontalGridLines(true);
It would be easier to tell what is the problem if you provide a jsFiddle.
I started using dc.js few days ago, so I don't know for sure. But, I think your code should go in
d3.csv("data.csv", function(data) { //your-code };
or
d3.json("data.json", function(data) {//your-code};
or
jQuery.getJson("data.json", function(data){//your-code});

Create a custom lines in EXTJS Line Chart

I need to add a vertical line and a text on my Line chart near to a specified point on chart (specified by data, not coordinates). I tried to use CompositeSprites, but it doesn't show on screen completely. I'm new to ExtJS drawing.
You should put the logic that adds the vertical line inside of the chart's refresh event listener, that way, if the data changes the line position will be updated to reflect the new data.
Here's an example of how you could do it, assuming you can get a reference to the chart container (e.g. "myPanel"):
var myChart = myPanel.down('chart'),
myChart.on('refresh', function(myChart) {
// First, get a reference to the record that you want to position your
// vertical line at. I used a "findRecord" call below but you can use
// any of the datastore query methods to locate the record based on
// some logic: findBy (returns index #), getAt, getById, query, queryBy
var myRecord = myChart.store.findRecord(/*[someField]*/, /*[someValue]*/),
// a reference to the series (line) on the chart that shows the record
mySeries = myChart.series.first(),
// get the chart point that represents the data
myPoint = Ext.each(mySeries.items, function(point) {
return myRecord.id === point.storeItem.id;
}),
// the horizontal position of the point
xCoord = point.point[0],
// check for any previously drawn vertical line
myLine = myChart.surface.items.findBy(function(item) {
item.id === 'vert'
});
// if there is no previously drawn line add it to the "surface"
if (!myLine) {
myChart.surface.add({
id: 'vert', // an id so that we can find it again later
type: 'rect',
width: 4,
height: myChart.surface.height, // the same height as the chart
fill: 'black',
opacity: 0.5, // some transparency might be good
x: xCoord,
y: 0 // start the line at the top of the chart
});
// if we already had a line just reposition it's x coordinate
} else {
myLine.setAttributes({
translate: {
x: xCoord,
y: 0
}
// I think the chart gets drawn right after the refresh event so
// this can be false, I haven't tested it though
}, false);
}
});
If you are using the MVC pattern your event handler would look a little different (you wouldn't use myChart.on()).

Categories