Related
I am trying to write a real-time data display for scientific data. I have a sever that is logging the data to a file so I don't care about keeping values past a window of about 15 minutes. I have not found a way to both toss the data points older than 15 minutes ago, and not reset the entire chart. Resetting the chart isn't an option as being able to see recent values is critical to the operation of this equipment. Things I have tried:
Note: This uses websockets in "push" mode.
This started as attempting to have a preview window with a fixed width (15 minutes) that the user could drag across the data. But I quickly found the using previews seems to be incompatible with real-time data streaming.
I tried fiddling with the refresh object options, but those would just change when the chart would reset.
My current try was to attempt to catch the "addnode" event when a node is added by the websocket handler, but from what I can tell the event never gets tripped.
The current iteration of my chart object is the following:
let chartConfig = {
graphset: [
{
timezone: -1 * (today.getTimezoneOffset() / 60), // Weird timezone stuff
type: 'line',
borderColor: '#cccccc',
borderRadius: '2px',
borderWidth: '1px',
utc: true,
title: {
text: title,
adjustLayout: true,
marginTop: '20px'
},
legend: {
backgroundColor: 'transparent',
borderWidth: '0px',
draggable: true,
header: {
text: test,
backgroundColor: '#f0f0f0'
},
item: {
margin: '5 17 2 0',
padding: '3 3 3 3',
cursor: 'hand',
fontColor: '#fff'
},
marker: {
visible: false
},
verticalAlign: 'middle'
},
plot: {
aspect: 'segmented',
},
plotarea: {
margin: 'dynamic'
},
scrollX: {},
scaleX: {
autoFit: false,
itemsOverlap: false,
maxItems: 15,
maxLabels: 15,
step: "15minute",
transform: {
type: 'date',
all: '%M-%dd-%Y<br>%G:%i',
},
guide: {
alpha: 0.5,
lineColor: "#cccccc",
lineWidth: 2,
lineStyle: "solid",
visible: true
},
zooming: true
},
scaleY: {
guide: {
lineStyle: 'solid'
},
offsetStart: "5%",
offsetEnd: "5%",
label: {
text: 'Data'
},
},
crosshairX: {
lineColor: '#555',
marker: {
borderColor: '#fff',
borderWidth: '1px',
size: '5px'
},
plotLabel: {
backgroundColor: '#fff',
borderRadius: '2px',
borderWidth: '2px',
multiple: true
},
scaleLabel: {
transform: {
type: "date",
all: "%G:%i:%s"
}
}
},
tooltip: {
visible: false
},
refresh: {
type: "feed",
transport: "websockets",
url: "wss://zingchart-websockets.glitch.me",
method: "push",
maxTicks: 15*60,
resetTimeout: 15*60*10,
adjustScale: false,
curtain: {
text: 'Starting Feed...',
color: '#424242',
fontSize: 45,
backgroundColor: '#b3e5fc',
}
},
series: [
{
text: 'plot0',
values: [],
legendItem: {
backgroundColor: '#29A2CC',
borderRadius: '2px',
}
},
]
}
],
gui: {
contextMenu: {
alpha: 0.9,
button: {
visible: true
},
docked: true,
item: {
textAlpha: 1
},
position: 'right'
}
}
};
// RENDER CHARTS
// -----------------------------
zingchart.render({
id: 'PlotArea',
data: chartConfig,
});
// Callbacks
// --------------------------
zingchart.bind('PlotArea', 'addnode', function(e) {
var cur_data = zingchart.exec('PlotArea', 'getseriesvalues', {});
if (length(cur_data) > 2*60) {
zingchart.exec('PlotArea', 'removenode', {
graphid: 0,
plotindex: 0,
nodeindex: 0
})
}
});
I am currently grabbing data from the websocket endpoint at glitchme because the webserver hasn't been written yet as I would like to get the hard part, the UI, ready first. Thanks for any help that can be provided.
In your refresh object, add 'preserve-data': false. This seems unintuitive, but this will preserve the data after the max ticks threshold is reached instead of clearing the chart.
i am using highcharts solid gauge. below is sample i have created
now i want to remove these grey bullets as these are not coming in my local but in test its there, i havn't got anything helpful from api. here are options passing to this.and thus i am not sure what i am missing can anyone guide me as i have tried looking into api for solid gauge but i am not sure which option is there to help me
options() {
return {
credits: {
enabled: false
},
chart: {
type: "solidgauge",
},
title: {
text: "",
style: {
fontSize: "24px"
}
},
legend: {
layout: "horizontal",
align: "right",
itemDistance: 100,
verticalAlign: "middle",
itemMarginTop: 5,
itemStyle: {
fontSize: "28px",
},
itemMarginBottom: 5,
labelFormatter: function() {
const formattedTotal = Number(
this.userOptions.data[0].total
).toLocaleString("en-US", {
minimumFractionDigits: 0,
maximumFractionDigits: 0
});
return (
'<span style="text-weight:bold;color:' +
this.userOptions.data[0].color +
'">' +
this.name +
'</span><br/><span style="text-align: center">' +
formattedTotal +
"</span>"
);
},
symbolWidth: 0
},
tooltip: {
borderWidth: 0,
backgroundColor: "none",
shadow: false,
style: {
fontSize: "16px"
},
useHTML: true,
pointFormat:
'<table><tr><td style="text-align: center"><span style="font-size: 14px">{series.name}</span><br><span style="font-size:22px; color: {point.color}; font-weight: bold">{point.y}%</span></td></tr></table>',
positioner: function(labelWidth) {
return {
x: (this.chart.chartWidth - labelWidth - 600) / 2,
y: this.chart.plotHeight / 2 - 15
};
}
},
pane: {
startAngle: 0,
endAngle: 360,
background: [
{
// Track for Move
outerRadius: "112%",
innerRadius: "88%",
backgroundColor: Highcharts.Color(this.graphColors && this.graphColors[0])
.setOpacity(0.3)
.get(),
borderWidth: 0
},
{
// Track for Exercise
outerRadius: "87%",
innerRadius: "63%",
backgroundColor: Highcharts.Color(this.graphColors && this.graphColors[1])
.setOpacity(0.3)
.get(),
borderWidth: 0
},
{
// Track for Stand
outerRadius: "62%",
innerRadius: "38%",
backgroundColor: Highcharts.Color(this.graphColors && this.graphColors[2])
.setOpacity(0.3)
.get(),
borderWidth: 0
}
]
},
yAxis: {
min: 0,
max: 100,
lineWidth: 0,
tickPositions: [],
},
plotOptions: {
solidgauge: {
dataLabels: {
enabled: false,
},
linecap: "round",
stickyTracking: false,
rounded: true
}
},
series: [
{
name: "Passerby",
showInLegend: true,
data: [
{
color: this.graphColors && this.graphColors[0],
radius: "112%",
innerRadius: "88%",
y: this.passerby,
total: this.passerbyTotal
}
]
},
{
name: "Visitor",
showInLegend: true,
data: [
{
color: this.graphColors && this.graphColors[1],
radius: "87%",
innerRadius: "63%",
y: this.visitor,
total: this.visitorTotal,
}
]
},
{
name: "Connected",
showInLegend: true,
data: [
{
color: this.graphColors && this.graphColors[2],
radius: "62%",
innerRadius: "38%",
y: this.connected,
total: this.connectedTotal
}
]
}
],
}
}
I think you can just apply a style to the class highcharts creates to hide these markers:
.highcharts-legend-item rect {
display: none;
}
I'm not familiar with highcharts, but those bullets looks like the work of a <ul> element.
You can remove bullets from all ul elements using css, like this:
ul
{
list-style-type:none;
}
See list-style-type. Of course it is better to specify the #id of the particular ul element, but (above) is just an example.
It's not possible to remove legend symbols and keep series markers. As a workaround, you can remove them manually in the chart load event callback.
Code:
chart: {
type: "solidgauge",
events: {
load: function() {
var chart = this,
series = chart.series;
series.forEach(function(serie, i) {
if (serie.legendSymbol) {
serie.legendSymbol.destroy()
}
});
}
}
}
Demo:
https://jsfiddle.net/BlackLabel/e1usLm08/1/
API:
https://api.highcharts.com/class-reference/Highcharts.SVGElement#destroy
I'm using angular6 and the office highcharts angular wrapper (https://github.com/highcharts/highcharts-angular) in version 2.4.0.
Everything is working good except the labels that can be placed manually on the chart.
I just don't manage to get them refreshed.
I have a graph that can be filtered through the back-end. When I get the new generated data I update the data in my chartOptions which are used in the html as [options]="chartOptions" for the angular component.
The chartOptions are instanciated from my default class like this: this.chartOptions = new GraphConfigSeqAtteptedVsAchived().chartOptions;
The Class looks like this:
`import * as Highcharts from 'highcharts';
export class GraphConfigSeqAtteptedVsAchived{
public chartOptions: any;
constructor(){
this.chartOptions = {
chart: {
height: 600,
width: 620,
events:{load: function(){
this.myTooltip = new Highcharts.Tooltip(this, this.options.tooltip);
}
},
backgroundColor: 'transparent',
},
credits: {
text: 'charname',
href: ''
},
labels: {
},
exporting:{
chartOptions:{
title: {
text:'not set',
style:{
color: '#000000'
},
margin: 0
}
}
},
tooltip: {
enabled: false
},
xAxis: {
min: -10,
max: 0,
width: 535,
title: {
text: 'Rainfall (mm)',
style: {
fontWeight: "bold",
color: '#000000'
}
},
reversed: true,
tickInterval: 1
},
yAxis: {
min: -10,
max: 0,
title: {
text: 'Rainfall (mm)',
style: {
fontWeight: "bold",
color: '#000000'
}
},
reversed: true,
tickInterval: 1
},
title: {
text: ' as',
margin: 0,
style:{
color: 'transparent'
}
},
legend: {
layout: 'vertical',
align: 'left',
verticalAlign: 'top',
x: 100,
y: 90,
floating: true,
backgroundColor: '#FFFFFF',
borderWidth: 1,
navigation: {
enabled: false
}
},
plotOptions: {
scatter: {
stickyTracking: false,
allowPointSelect: true,
marker: {
radius: 3,
states: {
hover: {
enabled: true,
lineColor: 'rgb(100,100,100)'
}
}
},
tooltip: {
headerFormat: '<b>{series.name}</b><br>',
pointFormat: '',
hideDelay: 500
},
symbol: "circle",
events: {
mouseOut: function() {
this.chart.myTooltip.hide();
this.chart.myTooltip.options.enabled = false;
},
mouseOver: function() {
if(this.halo) {
this.halo.attr({
'class': 'highcharts-tracker'
}).toFront();
}
},
click: function(event) {
this.chart.myTooltip.options.enabled = true;
this.chart.myTooltip.refresh(event.point, event);
}
}
},
line:{
events: {
legendItemClick: function () {
return false;
}
}
},
series:{
allowPointSelect: true
}
},
series: [{
type: 'line',
data: [[30, 30], [-30, -30]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 1
}
},
showInLegend: false,
color: "rgba(0, 0, 0, 0.6)",
enableMouseTracking: false
},{
type: 'line',
data: [[30, 29.5], [-29.5, -30]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(125, 162, 159, 0.35)",
enableMouseTracking: false
},{
type: 'line',
data: [[29.5, 30], [-30, -29.5]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(125, 162, 159, 0.35)",
enableMouseTracking: false
},{
type: 'line',
data: [[29, 30], [-30, -29]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(197, 144, 161, 0.35)",
enableMouseTracking: false
},{
type: 'line',
data: [[30, 29], [-29, -30]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(197, 144, 161, 0.35)",
enableMouseTracking: false
}]
}
}
}`
The generated data is in the typescript dynamically added and all of that works just fine. Everything is refreshed after I set the updateFlag for [(update)]="updateGraph". The axis titles are also fine.
The only thing that's not refreshed for me are the labels i dynamically added to the chart like this:
let labelTotalNumberOfEyes = {
html : this.translate.instant('GRAPHS.TOTAL_NUMBER_OF_EYES') + ': ' + (scatterSeries.data.length + scatterSeriesRetreatment.data.length),
style : {
left : '330px',
top : '368px',
fontSize : '14px',
backgroundColor: '#FFFFFF',
borderWidth: 1,
}
}
this.chartOptions.labels.items.push(labelTotalNumberOfEyes);
Does someone have any idea what I'm might doing wrong? I don't see any further option than just setting this update flag and it works fine for everything but the labels.
Please let me know if you're missing any information.
Thanks in advance.
This issue is actually a Highcharts bug. I've reported it here: https://github.com/highcharts/highcharts/issues/10429.
As a workaround, you can use Highcharts.SVGRenderer to add a custom label and store reference to it in your component. Next, when updating, call attr() method on the label (Highcharts.SVGElement) and set new options. Check demo and code posted below.
Add a label on chart callback:
constructor() {
const self = this;
this.chartCallback = chart => {
self.chart = chart;
self.label = chart.renderer
.text("test", 100, 100)
.css({
fill: "#555",
fontSize: 16
})
.add()
.toFront();
};
}
call attr() on the label while updating:
update_chart() {
const self = this,
chart = this.chart;
chart.showLoading();
setTimeout(() => {
chart.hideLoading();
self.chartOptions.series = [
{
data: [10, 25, 15],
name: "updatedSerieName"
}
];
self.chartOptions.yAxis.title.text = "updatedData";
self.label
.attr({
text: "test1",
x: 150,
y: 120
})
.css({
fill: "red",
fontSize: 20
});
self.updateFromInput = true;
}, 2000);
}
Demo:
https://codesandbox.io/s/8zo0yvqqwj
API reference:
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#text
https://api.highcharts.com/class-reference/Highcharts.SVGElement#attr
Is it possible to render charts and animate them step by step?
Right now I got three area-datasets in my chart which I need to animate step by step. Dataset/area 1 shall start and after this is finished, Dataset/area 2 need to start and then 3.
I cannot find an option for that, is this even possible with Highcharts?
Right now the Animation goes from left,bottom to right,up - is there an option for an animation direction? I want to start the animation from the full width (fully expanded on the xAxis) to go upwarts the yAxis.
My Options:
var $chart = $('#chart');
var chart = new Highcharts.Chart({
chart: {
renderTo: $chart[0],
type: 'area',
style: {
fontFamily: 'Arial, sans-serif'
}
},
legend: {
layout: 'vertical',
align: 'center',
verticalAlign: 'bottom',
itemMarginBottom: 15,
borderWidth: 0,
itemStyle: {
color: '#9a9a9a',
fontWeight: '300',
fontSize: '16px',
useHTML: true,
}
},
title: false,
xAxis: {
categories: ['0', '5', '10', '15', '20', '25', '30'],
lineColor: '#9a9a9a',
minTickInterval: 5,
title: {
text: 'Years',
style: {
color: '#000',
fontSize: '14px',
fontWeight: 'bold'
}
},
labels: {
style: {
color: '#9a9a9a',
fontSize: '14px'
}
}
},
yAxis: {
min: 0,
tickAmount:5,
minorTickInterval: 0,
minorGridLineColor: '#9a9a9a',
gridLineWidth: 1,
maxPadding: 0.1,
title: {
text: 'Eur',
style: {
color: '#000',
fontSize: '14px',
fontWeight: 'bold'
}
},
labels: {
format: '{value:,.0f}',
style: {
color: '#9a9a9a',
fontSize: '14px'
}
}
},
tooltip: {
backgroundColor: '#FCFFC5'
},
plotOptions: {
series: {
borderWidth: 0,
pointPadding: 0.2,
groupPadding: 0,
style: {
color: '#000',
fontSize: '16px',
fontWeight: '300'
}
}
},
series: [
{
name: '2',
legendIndex: 2,
color: '#83bd3f',
animation: {
duration: 3000
},
data: [1042,
2128,
3259,
4438,
5666,
6946,
7652
]
},
{
name: '1',
legendIndex: 1,
color: '#24356d',
animation: {
duration: 2000
},
data: [1024,2073,3146,
4246,
5372,
6525,
7705
]
},
{
name: 'Eingezahlte Rate',
legendIndex: 0,
color: '#9a9a9a',
animation: {
duration: 1000
},
data: [
1000,
2000,
3000,
4000,
5000,
6000,
7000
]
}
]
});`
Thanks
You can use series.afterAnimate event in which you can fire animation for another series.
In your first series set afterAnimate like this:
events: {
afterAnimate: function () {
this.chart.get('1').update({
visible: true,
animation: {
duration: 2000
},
events: {
afterAnimate: function () {
this.chart.get('2').update({
visible: true,
animation: {
duration: 3000
}
})
}
}
})
}
},
For the other series, initially you can disable animation or/and set their visibility to false:
series: [{
name: '2',
id: '2',
legendIndex: 2,
color: '#83bd3f',
animation: {
duration: 0
},
visible: false,
To change the direction of the animation you need to wrap series.animate
method and change how the clipping rect is animated.
(function(H) {
H.wrap(H.seriesTypes.area.prototype, 'animate', function(proceed, init) {
var series = this,
chart = series.chart,
clipRect,
animation = H.animObject(series.options.animation),
sharedClipKey,
newH;
if (init) {
return proceed.apply(this, Array.prototype.slice.call(arguments, 1));
} else {
sharedClipKey = this.sharedClipKey;
clipRect = chart[sharedClipKey];
if (clipRect) {
clipRect.attr({
y: chart.plotSizeY,
height: 0,
width: chart.plotSizeX
}).animate({
y: 0,
height: chart.plotSizeY
}, animation);
}
if (chart[sharedClipKey + 'm']) {
chart[sharedClipKey + 'm'].attr({
y: chart.plotSizeY,
height: 0,
width: chart.plotSizeX + 99
}).animate({
y: 0,
height: chart.plotSizeY
}, animation);
}
// Delete this function to allow it only once
series.animate = null;
}
});
})(Highcharts)
example: http://jsfiddle.net/0esjvmgL/
I have an ExtJS grid and a Jquery Gauge chart. I want to interact between the chart and the grid. If we select a row of a the grid then the corresponding changes will be reflected in the chart..
This is the code for the grid....
Ext.define('Gamma.view.EdgesGridChart' ,{
extend: 'Ext.grid.Panel',
alias : 'widget.edgesGridChart',
id: 'edgesGridChart',
height: 300,
width: Ext.getBody().getViewSize().width*0.4,
animate: true,
shadow: true,
title : 'Call Information',
store : 'Edges',
loadMask: true,
autoheight: true,
theme: 'Base',
dockedItems: [{
xtype: 'pagingtoolbar',
store: 'Edges',
dock: 'bottom',
displayInfo: true,
items: [
{
xtype: 'tbseparator'
}
]
}],
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 1
})
],
selModel: {
selType: 'cellmodel'
},
initComponent: function() {
this.columns = [{
header: 'SOURCE',
dataIndex: 'source',
flex: 1
},{
header: 'TARGET',
dataIndex: 'target',
flex: 1
}, {
header: 'DIR',
dataIndex: 'dir',
flex: 1
}, {
header: 'PEG',
dataIndex: 'peg',
flex: 1
}, {
header: 'WEIGHT',
dataIndex: 'weight',
flex: 1
}];
this.callParent(arguments);
}
});
This is the code for the gauge chart....
$(document).ready(function(){
var gradient1 = {
type: 'linearGradient',
x0: 0,
y0: 0.5,
x1: 1,
y1: 0.5,
colorStops: [{ offset: 0, color: '#C5F80B' },
{ offset: 1, color: '#6B8901'}]
};
var gradient2 = {
type: 'linearGradient',
x0: 0.5,
y0: 0,
x1: 0.5,
y1: 1,
colorStops: [{ offset: 0, color: '#FF3366' },
{ offset: 1, color: '#B2183E'}]
};
var anchorGradient = {
type: 'radialGradient',
x0: 0.35,
y0: 0.35,
r0: 0.0,
x1: 0.35,
y1: 0.35,
r1: 1,
colorStops: [{ offset: 0, color: '#4F6169' },
{ offset: 1, color: '#252E32'}]
};
$('#jqRadialGauge').jqRadialGauge({
background: '#F7F7F7',
border: {
lineWidth: 6,
strokeStyle: '#76786A',
padding: 16
},
shadows: {
enabled: true
},
anchor: {
visible: true,
fillStyle: anchorGradient,
radius: 0.05
},
tooltips: {
disabled: false,
highlighting: true
},
annotations: [
{
text: 'Wing Span',
font: '18px sans-serif',
horizontalOffset: 0.5,
verticalOffset: 0.8
}
],
scales: [
{
minimum: 0,
maximum: 150,
startAngle: 140,
endAngle: 400,
majorTickMarks: {
length: 12,
lineWidth: 2,
interval: 10,
offset: 0.84
},
minorTickMarks: {
visible: true,
length: 8,
lineWidth: 2,
interval: 2,
offset: 0.84
},
labels: {
orientation: 'horizontal',
interval: 10,
offset: 1.00
},
needles: [
{
value: 56,
type: 'pointer',
outerOffset: 0.8,
mediumOffset: 0.7,
width: 10,
fillStyle: '#252E32'
}
],
ranges: [
{
outerOffset: 0.82,
innerStartOffset: 0.76,
innerEndOffset: 0.68,
startValue: 0,
endValue: 100,
fillStyle: gradient1
},
{
outerOffset: 0.82,
innerStartOffset: 0.68,
innerEndOffset: 0.60,
startValue: 110,
endValue: 150,
fillStyle: gradient2
}
]
}
]
});
$('#jqRadialGauge').bind('tooltipFormat', function (e, data) {
var tooltip = '<b>Element: ' + data.elementType + '</b> ' + '</br>';
switch (data.elementType) {
case 'needle':
tooltip += 'Value: ' + data.value;
break;
case 'range':
tooltip += 'Start Value: ' + data.startValue + '<br/>End Value: ' + data.endValue;
}
return tooltip;
});
});
The jqRadialGauge plugin can be updated using the following code:
var scales = $('#jqRadialGauge').jqRadialGauge('option', 'scales');
scales[0].needles[0].value = Math.random() * 100;
$('#jqRadialGauge').jqRadialGauge('update');
In the case that you want to update the jqRadialGauge's needle when ExtJS grid row is clicked you can try using the following code:
$('#edgesGridChart').on('rowclick', function(grid, rowIndex, columnIndex, e) {
var scales = $('#jqRadialGauge').jqRadialGauge('option', 'scales');
scales[0].needles[0].value = rowIndex;
$('#jqRadialGauge').jqRadialGauge('update');
}, this);
The code in the second snippet shows how to set the jqRadialGauge first needle to the row index of the clicked row.
Best Regards,
Maxim Milev
Disclaimer: I work for jqChart.
Once you are done with catching click event, try using this method.
selectionchange : function(){
var grid = Ext.getCmp('edgesGridChart');
var selectedRecords= grid.getView().getSelectionModel().getSelection();
myData = selectedRecords[0].get('columnName');
var jquery= $('#jqRadialGauge').jqRadialGauge('option', 'scales');
jquery[0].needles[0].value = myData ;
$('#jqRadialGauge').jqRadialGauge('update');
},
This is high level explanation of how you can achieve...actual implementation will vary.
On row click of grid, set the values and update the data of the jquery model, after that you need to find a way to refresh the chart when the data model is updated.
All this could possibly be done using a jquery custom event that gets triggered from extjs row edit/click event.