Show Marker symbol on the newest data in HightChart - javascript

I am trying to create a plugin for our chart that shows a marker on the last data in the series of a data streams. The symbol should show on the newest data only. I learnt here that i can extend the prototypes and create my chart Here like
(function (H) {
H.wrap(H.Chart.prototype, 'init', function(proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
};
H.wrap(H.Chart.prototype, 'redraw', function(proceed, points) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
//Add my marker symbol here
});
}(Highcharts));
I tested the two events above, they start add the right time. However, my points is a boolean value which is not what i was expecting. I read the api docs Here yet couldn't understand how to proceed. Please if some has a better example or guidance on how to use the extension and achieve this would highly be appreciated. My data is a stream and continuos data and need to show the the marker symbol on top of the newest data only.
Update
Thanks for the response sir. We are using react-highcharts and i noticed i don't have access to the chart load event. Nonetheless, i noticed i can extend my H.Series prototype as explained Here and my event is fired on every new tick paint. I now update my code to
H.wrap(H.Series.prototype, 'drawGraph', function (proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
const { chart } = this;
renderSpotIndicator(chart);
});
function renderSpotIndicator(chart){
const series = chart.series[0],
len = series.data.length - 1,
lastPoint = series.data[len];
if(lastPoint){
lastPoint.update({
marker: {
enabled: true
}
});
}
}
When i run , then i noticed my marker property does not exist in my lastPoint as hence its throwing errors. I was not able to use that. I was advised to paint the marker instead. following the example Here . I changed my code to
function renderSpotIndicator(chart){
const series = chart.series[0],
len = series.data.length - 1,
lastPoint = series.data[len];
if(lastPoint){
chart.renderer.circle(lastPoint.pointX, lastPoint.pointY, 5).attr({
fill: lastPoint.color,
padding: 10,
r: 5,
stroke: 'white',
'stroke-width': 1,
zIndex: 5
}).add();
}
}
The above is throwing errors as well. When i checked, i noticed the property circled it self is not available on my lastPoint. My lastPoint is returning correct but i am not able to set the marker on the specific point for the property was not available on the lastPoint. Please any help would be appreciated sir.

You can edit the load event and extract last point of series. Then call update to show the marker. Each time when your chart is refreshed (by add point) you need to do the same steps. Extract last point, update marker (hide), add point and show last marker.
var series = this.series[0],
len = series.data.length - 1,
lastPoint = series.data[len];
lastPoint.update({
marker: {
enabled: true
}
});
setInterval(function() {
var x = (new Date()).getTime(), // current time
y = Math.random();
len = series.data.length - 1;
series.data[len].update({
marker: {
enabled: false
}
}, false);
series.addPoint([x, y], true, true);
len = series.data.length - 1;
series.data[len].update({
marker: {
enabled: true
}
}, true);
}, 1000);
Example:
- http://jsfiddle.net/ga2sym0v/

Related

Highstock, True way of Get count of shown points after setExtreme (Zooming) - WITHOUT counting all data with MIN and MAX

Sure there is a way to count all of points that shown after zooming or any changing view and do reaction for that.
My target is in Highchart 7.2.0 stockChart, IF "viewed points" x "radius of circles", gone more than (>) "view-port pixels", i just hide them, or doing something special with points, because some of them are Special and still should be shown.
so i need :
HOW GET : Count of points that JUST viewed now (WITHOUT PUTTING A "FOR" TO ALL OF DATA's)
(I just think if there is no true way for it, it is better to i count svg objects instead of : counting all of my data and using isInside with min and max)
The Best Events for : "afterSetExtremes" and "events:{redraw:" [Solved i think]
events: {
afterSetExtremes: function(event) {
console.log(event.min);
console.log(event.max);
}
}
How i turn them off [Solved i think]
if (chart.userOptions.plotOptions.line.marker.enabled) {
chart.userOptions.plotOptions.line.marker.enabled=false;
chart.update({
plotOptions: {
marker: {
enabled:false
}
}
});
}
If there is automatic way like "amchart" options that i just ask "marker: { enabled: true" (when no problem) and "marker: { enabled: false" when it is tight. [Solved i think]
Solved by this:
plotOptions: {
series: {
marker: {
enabled:undefined,
enabledThreshold: 4,
symbol: 'circle',
radius: 4,
},
}
}
It was like this :
marker: {enabled:true,
enabledThreshold: 0, (By Default)
Should be :
marker: {enabled:undefined,
enabledThreshold: 4, (More than Zero)
Got help from here : https://stackoverflow.com/a/54417034/7514010
The easiest way is to loop through the data and check isInside point property or point position. As an alternative you can overwrite translate method and count the number of visible points in the existing loop:
var counter;
(function(H) {
H.Series.prototype.translate = function() {
...
counter = 0;
// Translate each point
for (i = 0; i < dataLength; i++) {
...
point.isInside =
plotY !== undefined &&
plotY >= 0 &&
plotY <= yAxis.len && // #3519
plotX >= 0 &&
plotX <= xAxis.len;
// CHANGE
if (point.isInside) {
counter++;
}
// CHANGE
...
}
series.closestPointRangePx = closestPointRangePx;
H.fireEvent(this, 'afterTranslate');
}
})(Highcharts)
Live demo: http://jsfiddle.net/BlackLabel/yx1cj0at/
Docs: https://www.highcharts.com/docs/extending-highcharts
It answered by #ppotaczek here in the third comment by this link jsfiddle.net/BlackLabel/j1tLfaxu and also in GitHub issues : https://github.com/highcharts/highcharts/issues/12017
If need to get count of points that just viewed now (by using afterSetExtremes or redraw or render events) :
chart: {
events: {
render: function() {
console.log(this.series[0].points.length)
}
}
},
processedXData can be used too instead of points
points object have enough options like isInside :
this.series[0].points[0].isInsdie.
Because it is possible the first or last point be not shown and just affect the lines in line chart or in other type of chart be not shown because zooming in Y too.
and for just calculation where the extreme started you may need :
this.series[0].cropStart
and comparing that with your main data.

How to add flags to countinuous update angular highcharts

I tried to add flags for loading dynamical data that worked at first when I used the continuous update charts as follows https://www.highcharts.com/stock/demo/dynamic-update and then the flags as follows https://www.highcharts.com/stock/demo/flags-shapes.
Can you please give me some solution for adding flags for continuous data? Also, I would want that if the continuous data exceeds some limit, the flags should appear on that series. And i want flags on continuous data also
Similar to adding points for the basic series, you should also add flag series points:
events: {
load: function () {
var series = this.series[0],
flagSeries = this.series[1];
setInterval(function () {
var x = (new Date()).getTime(), // current time
y = Math.round(Math.random() * 100);
series.addPoint([x, y], true, true);
flagSeries.addPoint({
x: x,
title: y
});
}, 1000);
}
}
Live demo: https://jsfiddle.net/BlackLabel/rh8jug5d/
API: https://api.highcharts.com/class-reference/Highcharts.Series#addPoint

Updating Bokeh Plot in JS

I am developing in Javascript to render a table using a DataTables (Table plug-in for jQuery). I am also using Bootstrap Multiselect (https://github.com/davidstutz/bootstrap-multiselect) to filter the table. I would like to re-render my BokehJS plot everytime the table is re-drawn. I have hooked up the correct calls but I am also calling Bokeh.Plotting.show to re-render the graph. This forces me to remove the last created div to avoid multiple graphs plotted. I am new to the JS side of Bokeh and wanted to understand if there is a cleaner method to update the plot in JS?
var eventFired = function ( type ) {
//var n = $('#demo_info')[0];
//n.innerHTML += '<div>'+type+' event - '+new Date().getTime()+'</div>';
//n.scrollTop = n.scrollHeight;
var plt = Bokeh.Plotting;
// set up some data
var M = 25;
var xx = [];
var yy = [];
var colors = [];
var radii = [];
for (var y = 0; y <= M; y += 4) {
for (var x = 0; x <= M; x += 4) {
xx.push(x * (Math.random() +0.5));
yy.push(y * (Math.random() +0.5));
colors.push(plt.color(50+8*x, 30+8*y, 150));
radii.push(Math.random() * 0.4 + 1.7)
}
}
// create a data source
var source = new Bokeh.ColumnDataSource({
data: { x: xx, y: yy, radius: radii, colors: colors }
});
// make the plot and add some tools
var tools = "pan,crosshair,wheel_zoom,box_zoom,reset,save";
var p = plt.figure({ title: type+' event - '+new Date().getTime(),
height: 300,
width: 300,
tools: tools });
// call the circle glyph method to add some circle glyphs
var circles = p.circle({ field: "x" }, { field: "y" }, {
source: source,
radius: radii,
fill_color: colors,
fill_alpha: 0.6,
line_color: null
});
//remove old plot on update conditions
$('.bk-root').remove();
// Show the plot, appending it to the end of the current
// section of the document we are in.
Bokeh.Plotting.show(p, document.getElementById('myplot'));
//To Do: add in transition to improve the UI appearance
}
And here is the the datatable setting up the call back to the BokehJS script.
</script>
$('#table').DataTable({
responsive: {
details: {
display: $.fn.dataTable.Responsive.display.modal( {
header: function ( row ) {
var data = row.data();
return 'Details for '+data[0]+' '+data[1];
}
} ),
renderer: $.fn.dataTable.Responsive.renderer.tableAll( {
tableClass: 'table'
} )
}
},
paginate: false,
info: false,
paging: true,
autoWidth: true,
dom: '<"dt-buttons">Bf<"clear">lirtp',
"buttons": [ 'copy', 'csv', 'excel' ],
}).on( 'draw.dt', function () { eventFired( 'Draw' ); } );
Lastly, is there a good method to update the plot via a transition effect to improve the appearance of the re-plotting?
I eventually figured out all of the components in the process in a modified use case compared to the one shown above. In my present approach I needed to utilize my source data and emit a 'change' trigger.
source.data.factors = [my new factor list]
source.trigger('change');
The new factor list based on the jQuery datatable can be obtained as followed.
$('mytable').DataTable().columns(0, {search: 'applied'}).data()[0]
Also in my use case I am working with a categorical axis. In my specific use case, my factors will dynamically change upon redraw of the graph. This can be achieved using the plot's attributes.
p.attributes.x_range.factors = [ my new factor list from my updated source data]
There is also no need to remove the old plot, and the added plus is the redraw is very fast for my simple plots.

Getting rid of Chart.js canvas subpixels

I'm working on a really small widget which displays a simple bar chart:
I'm using Chart.js for that specific task.
var canvas = this.$(".chart-canvas")[0];
if (canvas) {
var ctx = canvas.getContext("2d");
ctx.translate(0.5, 0.5);
window.barChart = new Chart(ctx).Bar(barChartData, {
responsive: true,
maintainAspectRatio: false,
showScale: false,
scaleShowGridLines: false,
scaleGridLineWidth: 0,
barValueSpacing: 1,
barDatasetSpacing: 0,
showXAxisLabel: false,
barShowStroke: false,
showTooltips: false,
animation: false
});
As you can see, I've tried
ctx.translate(0.5, 0.5);
but that didn't really help.
Is there any way to get rid of the subpixel rendering?
I've read about Bresenham's line algorithm, but don't know how to implement it there.
Any ideas/suggestions appreciated.
Thank you in advance!
Assuming you have only one color, you can do this by extending the chart and overriding the draw to do a getImageData, "rounding" (if the pixel has a R, G or B value, set it to the color) the pixel colors and a putImageData.
You could do this for multiple colors too but it becomes a tad complicated when there are two colors close by.
However the difference in bar value spacing you are seeing is because of the way Chart.js calculates the x position for the bars - there's a rounding off that happens.
You can extend the chart and override the method that calculates the x position to get rid of the rounding off
Chart.types.Bar.extend({
// Passing in a name registers this chart in the Chart namespace in the same way
name: "BarAlt",
initialize: function (data) {
Chart.types.Bar.prototype.initialize.apply(this, arguments);
// copy paste from library code with only 1 line changed
this.scale.calculateX = function (index) {
var isRotated = (this.xLabelRotation > 0),
innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
valueWidth = innerWidth / Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1),
valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
if (this.offsetGridLines) {
valueOffset += (valueWidth / 2);
}
// the library code rounds this off - we don't
return valueOffset;
}
// render again because the original initialize call does a render
// when animation is off this is the only render that happens
this.render();
}
});
You'd call it like so
var ctx = document.getElementById('canvas').getContext('2d');
var myBarChart = new Chart(ctx).BarAlt(data, {
...
Fiddle - http://jsfiddle.net/gf2c4ue4/
You can see the difference better if you zoom in.
The top one is the extended chart

Dygraphs - Highlight two series at the same time

I know the question was already asked before but I am very new to Dygraphs and struggling to find the answer.
I have the following datastructure in javascript:
x , Label1, Label2, label3.... label1_2, label1_3, etc...
new Date(...), 1.23,1.45,.... , .... , ....,
new Date(...), null, null, ......., 1.23,1.434
new Date(....), 1.4656, 1.6765.......,null, null,null
The whole idea is to have a plot on which a certain part of the line is dashed and the remaining part is not. I initially have 7 time series, I splitted each time serie in two (the dashed part and the non-dashed part), now I would like to highlight the whole time series ( so 2 distinct series in terms of Dygraphs the dashed serie, and the non-dashed that I splitted in two) when I pass the mouse over either the dashed region either the non dashed region.
I ve seen that people were stipulating using HihlightCallback but I am struggling to put it in practice.
What I have for the moment:
data =[new Date(), ..,..,.,,.,,.]
labels= {'A','B', ..... }
series= {'A': {strokePattern: [10, 20] }, 'B': .......}
g = new Dygraph( demo, data, {width: 1000,height: 700,labelsDivStyles: { 'textAlign': 'right' }, labels: labels,series:series, visibility: visibility, gridLineColor: 'red', gridLinePattern: [5,5], highlightCircleSize: 2,strokeWidth: 1, strokeBorderWidth: 1,highlightSeriesOpts: { strokeWidth: 3,strokeBorderWidth: 1,highlightCircleSize: 5}});
I believe my structure should be as follows:
g.updateOptions({ highlightCallback: function(event, x, points, row, seriesName) {
//1)here I need to somehow reference the other series whose label is situated N columns from the highlighted serie ( I can also reference it by its name).
// 2) Hilight the other serie
}});
I tried many different syntaxe but nothing seems to be working properly.
Could anyone please help me on this I am lost.
Here is what I would like to achieve :
http://www.google.co.uk/publicdata/explore?ds=k3s92bru78li6_#!ctype=l&strail=false&bcs=d&nselm=h&met_y=ggxwdn_ngdp&scale_y=lin&ind_y=false&rdim=world&idim=world:Earth&idim=country:AR:DZ:AU:AZ&ifdim=world&tstart=343382400000&tend=1574064000000&hl=en_US&dl=en_US&ind=false
Thanks a lot!
If I understand correctly, you've set up something like this: jsbin
Typically you style the highlighted series using highlightSeriesOpts, but that comes with the assumption that there's only a single highlighted series.
If you want to model the data this way (as separate series for actual & projected), you'll need to style the series yourself using highlightCallback. There are a few gross things about this which I'll mention below, but this is doable.
Demo: jsbin
g = new Dygraph(document.getElementById("graph"),
"X,Y,Y projected,Z,Z projected\n" +
"2006,0,,3,\n" +
"2008,2,,6,\n" +
"2010,4,,8,\n" +
"2012,6,,9,\n" +
"2014,8,8,9,9\n" +
"2016,,10,,8\n" +
"2018,,12,,6\n" +
"2020,,14,,3\n",
{
colors: ['blue', 'blue', 'red', 'red'],
series: {
'Y': { },
'Y projected': { strokePattern: [5, 5] },
'Z': { },
'Z projected': { strokePattern: [5, 5] }
},
highlightCallback: function(_, _, _, row, seriesName) {
update(seriesName, row);
},
unhighlightCallback: function() {
update();
},
highlightSeriesOpts: {},
highlightSeriesBackgroundAlpha: 1.0
});
function update(selectedSeries, row) {
var newOptions = {};
var seriesNames = g.getLabels().slice(1);
seriesNames.forEach(function(label) {
newOptions[label] = {strokeWidth: 1};
});
if (selectedSeries == 'Y' || selectedSeries == 'Y projected') {
newOptions['Y'] = newOptions['Y projected'] = {strokeWidth: 3};
} else if (selectedSeries == 'Z' || selectedSeries == 'Z projected') {
newOptions['Z'] = newOptions['Z projected'] = {strokeWidth: 3};
}
g.updateOptions({series: newOptions});
if (typeof(row) !== 'undefined') {
g.setSelection(row);
}
}
The idea is that you call updateOptions in your highlightCallback, setting the strokeWidth property for each series according to whether it (or its paired series) is selected.
There are a few gross things about this:
You have to set highlightSeriesOpts for the seriesName parameter to be passed to highlightCallback.
You need to counteract the default fading behavior of highlightSeriesOpts by setting highlightSeriesBackgroundAlpha.
Calling updateOptions clears the selection, so you have to call setSelection explicitly to re-select.
If you're willing to model the measured & projected values as a single series, then you can accomplish this more cleanly by writing a custom plotter which switches from solid to dashed lines at some point.
Here's a demo: jsbin
g = new Dygraph(document.getElementById("graph"),
"X,Y,Z\n" +
"2004,0,3\n" +
"2006,2,6\n" +
"2008,4,8\n" +
"2010,6,9\n" +
"2012,8,9\n" +
"2014,10,8\n" +
"2016,12,6\n" +
"2018,14,3\n",
{
plotter: function(e) {
var ctx = e.drawingContext;
ctx.beginPath();
ctx.moveTo(e.points[0].canvasx, e.points[0].canvasy);
for (var i = 1; i < e.points.length; i++) {
var p = e.points[i];
ctx.lineTo(p.canvasx, p.canvasy);
if (p.xval == 2014) {
ctx.stroke();
ctx.beginPath();
ctx.moveTo(p.canvasx, p.canvasy);
ctx.setLineDash([5]);
}
}
ctx.stroke();
ctx.setLineDash([]);
},
highlightSeriesOpts: {
strokeWidth: 3
}
});
Because your data is a single series, you no longer need to highlight multiple series simultaneously and hence you can use highlightSeriesOpts.

Categories

Resources