I have a simple Highchart with a dataset of up to 1000 datas. There are only y values the x values are generated automatically. Also, the values come from my nodejs server so please don't be surprised about the notation.
Now I want 3 special values whose x and y values are known to be highlighted. In which way doesn't matter for now.
One possibility would be to show the point at the location, otherwise they are not displayed. The problem I have is that I don't know how to control a specific point.
var chart1 = new Highcharts.Chart({
chart: {
renderTo: 'chart-emg1',
type: 'line'
},
title: {
text: 'EMG 1'
},
xAxis: {
tickInterval: 1
},
yAxis: {
title: { text: 'Voltage'}
},
series: [{
data: [<%-data1 %>]
}]
});
You can use the load event and update specific points. For example:
events: {
load: function() {
this.series[0].points.forEach(point => {
const isPointToHighlight = pointsToHighlight.some(
p => p.x === point.x && p.y === point.y
);
if (isPointToHighlight) {
point.update({
color: 'red',
marker: {
enabled: true
}
}, false);
}
});
this.redraw();
}
}
Live demo: http://jsfiddle.net/BlackLabel/tLd3j78f/
API Reference:
https://api.highcharts.com/highcharts/chart.events.load
https://api.highcharts.com/class-reference/Highcharts.Point#update
Related
I'm trying to create a dynamic flag series to go onto my main chart which is an OHLC series. I want to be able to freely drag each flag and when released, it will snap to the closest y value (either 'High' or 'Low') of the current OHLC bar.
I have been able to acheive this behavior with the following code:
Highcharts.getJSON('https://www.highcharts.com/samples/data/aapl-ohlc.json', function (data) {
var lastDate = data[data.length - 1][0], // Get year of last data point
days = 24 * 36e5; // Milliseconds in a day
// create the chart
Highcharts.stockChart('container', {
tooltip: {
enabled: false
},
rangeSelector: {
selected: 0
},
xAxis :{
crosshair: {
width: 0
}
},
title: {
text: 'AAPL Stock Price'
},
series: [{
type: 'ohlc',
name: 'AAPL Stock Price',
tooltip: {
visible: false
},
data: data,
dataGrouping: {
units: [[
'week', // unit name
[1] // allowed multiples
], [
'month',
[1, 2, 3, 4, 6]
]]
}
},
{
type: 'flags',
align: 'right',
point: {
events: {
drop: snapFlag
}
},
dragDrop: {
draggableX: false,
draggableY: true,
},
data:[{
x:data[data.length - 20][0],
y: data[data.length - 20][2],
title: "A",
text: "1: test",
snapto:'closest',
title: 'A',
}]
}]
});
});
function snapFlag(){
/*
* on drop event, find the nearest OHLC point and snap flag to that point
* this will be the default unless "snapto" property is set to "none"
*/
const old_x = this.x;
const old_y = this.y;
let new_graphic = this.graphic;
let closest_y;
let ohlc_point;
// iterate x values in reverse to find matching ohlc point
ohlc_point = this.series.chart.series[0].points.reverse().filter(function( point){
return point.x === old_x;
})[0];
if(this.snapto === "closest"){
// find the OHLC point that is closest to drop location
closest_y = [ohlc_point.high,ohlc_point.low].reduce(function(prev, curr) {
return (Math.abs(curr - old_y) < Math.abs(prev - old_y) ? curr : prev);
});
}else if(this.snapto !== "float"){
closest_y = ohlc_point[this.snapto]
}else{
closest_y = this.y
}
if(closest_y === ohlc_point.low){
this.graphic.box.element.setAttribute('transform', `translate(0,${this.graphic.box.getBBox().height}) scale(1,-1) translate(0,-${this.graphic.box.getBBox().height})`);
}else{
this.graphic.box.element.removeAttribute('transform');
}
this.update({
y: closest_y
},true,false);
// override the default behavior
return false;
}
<script src="https://code.highcharts.com/stock/highstock.js"></script>
<script src="https://code.highcharts.com/stock/modules/data.js"></script>
<script src="https://code.highcharts.com/stock/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/draggable-points.js"></script>
<div id="container" style="height: 400px; min-width: 310px"></div>
And the JSFiddle here
What I'm having trouble with is the SVG transformation. As you can see I have successfully been able to mirror the flag's SVG path about the x-axis such that it does not overlap with the OHLC bar that its attached to. But I am having trouble getting the flag title to adhere to the same translation. I have tried similar methods of directly editing the element that hold the title but have not been able to get the changes to stick.
Any help on this or a different direction I could take to accomplish this would be appreciated. I have been stuck on this small detail for a while!
I think that you should be able to achieve it by translating the graphic.tex separately.
Part of the logic which has been changed:
if (closest_y === ohlc_point.low) {
this.graphic.box.element.setAttribute('transform', `translate(0,${this.graphic.box.getBBox().height}) scale(1,-1) translate(0,-${this.graphic.box.getBBox().height})`);
this.graphic.text.translate(0, this.graphic.box.getBBox().height + this.graphic.text.getBBox().height)
} else {
this.graphic.box.element.removeAttribute('transform');
this.graphic.text.element.removeAttribute('transform');
}
Demo: https://jsfiddle.net/BlackLabel/vy1gzwmd/
So, What I have is a condition in a MySQL to show the first 1000 data points first and then the other 2000 datapoints after that in Highcharts.
if lastindex==0:
cur.execute("SELECT data,value FROM table where id<1001")
else:
cur.execute("SELECT data,value FROM table where id>1001 and id<3000")
data = cur.fetchall()
//python Code to fetch SQL data
Now what I am doing is that I am rendering that data into the Highcharts, the data is being rendered. but the problem arises that after showing the first 1000 data points, the Highcharts value starts from 0 and then shows the other 2000 points
the data is not displaying continuously as it should plot the send array data just after the end of the first data.
I think the Highcharts is being called Twice, What can I do to append the 2nd set of data to the first set without reloading the whole chart.
Here's a snip of my Highchart's js
Highcharts.chart("chartcontainer", {
chart: {
type: 'line',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
events: {
load: function() {
var series = this.series[0],
chart = this;
setInterval(function() {
//some logic regarding the chart
//..
v = {
y: y,
x: x
};
console.log("V value", v);
series.addSeries(v, false, true);
counter++;
localcounter++;
} else
{
oldcounter=counter;
flagToreload=1;
}
}, 1000/130);
setInterval(function() {
chart.redraw(false);
}, 100);
}
}
},
time: {
useUTC: false
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'Value',
gridLineWidth: 1
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}],
gridLineWidth: 1
},
tooltip: {
headerFormat: '<b>{series.name}</b><br/>',
pointFormat: '{point.x:%Y-%m-%d %H:%M:%S}<br/>{point.y:.2f}'
},
exporting: {
enabled: false
},
series: [{
animation: false,
name: 'Random data',
data: (function() {
// generate an array of random data
var data = [],
time = counter,
i;
for (i = -1000; i <= 0; i += 1) {
data.push([
counter,
null
]);
}
return data;
}())
}]
});
What I want is just to append the event data rather than loading the whole chart.
How can I reload a particular Highchart value without reloading the whole chart ?
What do you think about updating the current series with new data, which will be an array of old data merged with the new one?
chart: {
events: {
load(){
let chart = this,
currentSeries = chart.series[0],
newData;
newData = [...currentSeries.userOptions.data, ...data1]
setTimeout(()=> {
chart.series[0].update({
data: newData
})
}, 5000)
}
}
},
See the demo
I have a highcharts chart that is dynamically updated. I add the values to the series using the "AddPoint" function on the series. Here is how I am initializing the chart.
var _trendToolChart = null;
var _trendSeries = null;
function createTrendTool() {
_trendToolChart = Highcharts.chart('trendToolContainer', {
chart: {
type: 'spline',
zoomType: 'x',
panning: true,
panKey: 'shift'
},
title: {
text: 'Trending Signals'
},
yAxis: {
title: {
text: 'Values'
}
},
xAxis: {
title: {
text: 'Time'
},
type: 'datetime'
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
plotOptions: {
series: {
label: {
connectorAllowed: true
}
}
},
responsive: {
rules: [{
condition: {
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
});
_trendToolChart.showLoading("Loading...");
}
I keep the _trendToolChart variable as a global reference for later. On the regular I query our API to get a value. The value return is an x value of a datetime and a y value of a decimal. Since I have a multi line series, I have a WebId associated with each line on the chart itself. I find the series by this GUID, then add a point to the series.
Here's inside the function when I call it on the second:
var xValue = Date.UTC(apiResponse.DateString); // convert str to date
var yValue = apiResponse.Value; // decimal value
var point = [xValue, yValue];
// add to series & graph
var chartSeries = $.grep(_trendToolChart.series, function (e) { return e.userOptions.WebId == apiResponse.elemId; })[0];
chartSeries.addPoint(point);
_trendToolChart.redraw();
When I add these values to the chart, highcharts instead adds the x value as integer counts, not as a date. Here's the check inside the response function on chrome's developer side:
So instead of a series of ["11/27/2017",22] I get [0, 22] in my x values. And it shows on my chart, my xaxis labels has a "0" or 1 and not a Date.
Here's what I tried, instead of addPoint([Date,number]);, I've done addPoint ({x:Date, y:number}); Now that showed in the points on the highchart series with a date value, but then on my chart visually I saw, ZERO points added after the redraw.
What am I doing wrong?
Alright, it seems that the object being put in the x value was not a date, despite what it seems. It was actually a string.
So this returns a string:
Date("2017-11-27")
As a result, highcharts didn't know how to interpret that as a datetime, since I told it to do datetime in the options. The solution was this:
moment("2017-11-27").ToDate()
This returned a proper date object, and highcharts was able to interpret.
I have a ASP.NET MVC project with SignalR.
I have a page with a HighChart and the script looks like this:
$(function () {
window.Highcharts.setOptions({
global: {
useUTC: false
}
});
var chart;
$(document).ready(function () {
chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'line',
marginRight: 10
},
title: {
text: 'GMAS Queues'
},
xAxis: {
type: 'datetime',
tickInterval: 500,
labels: {
enabled: false
}
},
yAxis: {
title: {
text: 'Queue Count'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'Processing Queues'
}]
});
});
$.connection.hub.logging = true;
// Reference the auto-generated proxy for the hub.
var chartData = $.connection.processingQueuesHub;
// Create a function that the hub can call back to display messages.
chartData.client.updateQueueCounts = function (data) {
//$.each(data, function(i, item) {
// // Add the message to the page.
// $('#chartDataLog').append('<li><strong>' + htmlEncode(item.QueueName)
// + '</strong>: ' + htmlEncode(item.Length) + '</li>');
//});
// set up the updating of the chart.
var series = chart.series[0];
$.each(data, function (i, item) {
if (item.QueueName == "Queue A") {
var x = Date.parse(item.Date),
y = item.Length;
series.addPoint([x, y], true, false);
}
});
};
However, I see the graph but not the points.
The strange part is the series data points are there:
Anyone know why HighCharts is not rendering the points?
Thanks, Bill N
I have to thank my good friend and co developer for figuring this out. He is a smarter and braver man than me. :) He went to the highcharts source and found that the highcharts breaks if you add to the graph series before the initial animation is completed. The animation is why the clip-rect is zero-width (it animates from zero to full width over 1s when you first create the chart). You end up adding a point to the series before this animation even really starts. This kills the animation but it doesn’t fix the width of the clip-rect. The fix is to add animation is false for the series.
series: [{ name: 'Processing Queues', data: [], animation: false }]
It looks like you are not defining what your chart.series is until it is created. The line in your ajax is as follows and its not waiting for DOM ready:
var series = chart.series[0];
But you do not define chart until $(document).ready(function () {.... Try keeping your chart object in scope of your ajax.
I'm using Highcharts to draw a spline. This works good.
I'm using secondary series (columns) for another data.
My problem is: how to set column height to be matched with spline.
As far i've done this: jsfiddle
Columns have height almost i need, but not all.
It is possible at all?
Relevant code:
$('#chart').highcharts({
title: {text: 'Chart'},
chart: {
type: 'spline'
},
xAxis: {
title: {
text: 'X-Axis'
}
},
yAxis: {
title: {
text: 'Y-Axis'
},
labels: {
enabled: false
}
},
series: [{
name: 'Chart',
data: chdata,
marker: {
enabled: false
},
}],
legend: {
enabled: false
}
});
var chartObj = Highcharts.charts[$('#chart').attr('data-highcharts-chart')];
var d = [];
for(var i = 0; i < ldata.length; i++) {
d.push([ldata[i],getYValue(chartObj,0,getClosest(ldata[i],bdata[0]))]);
}
chartObj.addSeries({
type: 'column',
pointWidth: 1,
borderWidth: 0,
data: d
});
I've tried even with plotLines, but it have height of whole chart.
Thanks,
Bartek
Rather than plotting the height of the closest point, try plotting the average height of the points either side of your bar.
You can get even closer if you work out a linear best fit for your bar on the line between the points either side. Something like:
function getY(xVal,points) {
var higher = 0;
var lower=0;
for(var i=0;i<points.length;i++){
if (points[i][0] > xVal) {
higher=i;
lower=i-1;
break;
}
}
return points[lower][1] + ((points[higher][1]-points[lower][1])*(xVal-points[lower][0])/(points[higher][0]-points[lower][0]));
};
http://jsfiddle.net/5h471o3d/