Flot Chart - Graphic data as an object array with multiple attributes - arrays

I would like to have an array object instead of the following json format;
[1409558400000, 7.45],[1409562000000, 5.71], [1409565600000, 7.50],
... .;
My purpose is to show all data on the graph based on their hh:mm parameter (which is done in the link already), and I ended up such json array;
[10, 7.45],[09, 5.71], [11, 7.50], ...
But I also would like to keep their timestamp in order to have more information about each data point so that I can provide the timestamp when user clicks on a point.
I simply need to have something like this [10, 7.45,1409562000000] ; hour value, age, and timestamp respectively.
How can I have such data array for flot chart ?
var d = [
[1409558400000, 7.45],
[1409562000000, 5.71],
[1409565600000, 7.50],
[1409569200000, 7.63],
[1409576400000, 3.14],
[1409644800000, 7.45],
[1409648400000, 5.71],
[1409652000000, 7.50],
[1409655600000, 7.63],
[1409662800000, 3.14],
[1409731200000, 7.45],
[1409734800000, 5.71],
[1409738400000, 7.50],
[1409742000000, 7.63],
[1409749200000, 3.14]
];
$.each(d, function (index, datapoint) {
datapoint[0] = (new Date(datapoint[0])).getHours();
});
$.plot("#placeholder", [d], {
series: {
lines: {
show: true
},
points: {
show: true
}
},
grid: {
hoverable: true,
clickable: true,
markings: [{
yaxis: {
from: 0,
to: 4
},
color: "#F2CDEA"
}, {
yaxis: {
from: 4,
to: 7
},
color: "#D7EEE1"
}, {
yaxis: {
from: 7,
to: 12
},
color: "#F2CDEA"
}]
},
xaxis: {
},
yaxis: {
min: 0,
max: 12
}
});
$("#placeholder").bind("plotclick", function(event, pos, item) {
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
if (item) {
//window.location="pagex.html";
alert("x: " + x);
//plot.highlight(item.series, item.datapoint);
}
});
<!-- basic time series flot chart -->
<h>Create a custom green range</h>
<div style="height: 400px; width: 600px;" id="placeholder"></div>
http://jsfiddle.net/shamaleyte/wzLaqzf5/1/

Basically I adapt the answer given in the following link for my scenario.
Link for Referenced answer
Link : displaying custom tooltip when hovering over a point in flot
My Code:
var data = [
[1409558400000, 7.45],
[1409562000033, 5.71],
];
$.each(data, function (index, datapoint) {
datapoint[2] = datapoint[0]; // copy the timestamp and paste it as the 3rd object
datapoint[0] = (new Date(datapoint[0])).getHours(); // to put hours on y axis
});
$.plot("#placeholder", [d2], {
series: {
lines: {
show: true
},
points: {
show: true
}
},
grid: {
hoverable: true,
clickable: true,
markings: [{
yaxis: {
from: 0,
to: 4
},
color: "#F2CDEA"
}, {
yaxis: {
from: 4,
to: 7
},
color: "#D7EEE1"
}, {
yaxis: {
from: 7,
to: 12
},
color: "#F2CDEA"
}]
},
xaxis: {
},
yaxis: {
min: 0,
max: 12
}
});
$("#placeholder").bind("plotclick", function(event, pos, item) {
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
var tooltip = item.series.data[item.dataIndex][2];
if (item) {
//window.location="pagex.html";
alert("x: " + tooltip);
//plot.highlight(item.series, item.datapoint);
}
});
My Fiddle : http://jsfiddle.net/shamaleyte/wzLaqzf5/2/

Related

set different bar width for each bar using ECHARTS

Im using the e-charts libary for creating a bar chart for my data .
like so :
option.series = [
{
name: 'buy',
type: 'bar',
stack: 'one',
data: Object.values(chartData?.data || {}).map(elem => -elem.buy?.total),
color: colors.bought,
label: {
show: true,
position: "bottom",
formatter: (value) => Math.round(Math.abs(value.data))
},
tooltip: {
formatter: (data) => Math.abs(data.value).toString()
},
// barWidth:this.data
},
{
name: 'sell',
type: 'bar',
stack: 'one',
data: Object.values(chartData?.data || {}).map(elem =>elem.sell?.total),
color: colors.sold,
label: {
show: true,
position: "top",
formatter: (value) => Math.round(Math.abs(value.data))
},
tooltip: {
formatter: (data) => Math.abs(data.value).toString()
},
},
{
name: 'profite',
type: 'bar',
stack: 'two',
barGap: '-100%',
z: 100,
data: Object.values(chartData?.data || {}).map(elem => elem.sell?.profit),
color: colors.profit,
label: {
show: true,
position: "insideTop",
textBorderWidth: -1,
offset: [0, -20],
formatter: (value) => Math.round(Math.abs(value.data))
},
tooltip: {
formatter: (data) => Math.abs(data.value).toString()
},
},
]
im trying to set a different width for each bar depending on the value of each bar .
on data property i get a list of numbers rendering .
When i try to add a barWidth property all i can do is change all of the bars in the same chrts like so for example:
barWidth: ${getBarWidth((Object.values(chartData?.data || {}).map((elem) => elem.sell?.amount)))}%
so i returne a different value of my data each time but it didnt changed each bar according to its value (either buy, sell and so on).
Thanks in adavance.
As you pointed out, barWidth sets the width of all the bars of a bar series. And I don't think there is a way to set the width of each bar in the same bar series.
What you should use instead is a custom series.
Custom series have a parameter called renderItem, where you write the render logic of the chart. It's where you'll be able to display custom shapes (custom sized bar in your case), using graphics.
Here is an example I found on their website, doing pretty much what you're looking for.
var container = document.getElementById('main');
var chart = echarts.init(container);
const colorList = [
'#4f81bd',
'#c0504d',
'#9bbb59',
'#604a7b',
'#948a54',
'#e46c0b'
];
const data = [
[10, 16, 3, 'A'],
[16, 18, 15, 'B'],
[18, 26, 12, 'C'],
[26, 32, 22, 'D'],
[32, 56, 7, 'E'],
[56, 62, 17, 'F']
].map(function (item, index) {
return {
value: item,
itemStyle: {
color: colorList[index]
}
};
});
chart.setOption({
title: {
text: 'Profit',
left: 'center'
},
tooltip: {},
xAxis: {
scale: true
},
yAxis: {},
series: [
{
type: 'custom',
renderItem: function (params, api) {
var yValue = api.value(2);
var start = api.coord([api.value(0), yValue]);
var size = api.size([api.value(1) - api.value(0), yValue]);
var style = api.style();
return {
type: 'rect',
shape: {
x: start[0],
y: start[1],
width: size[0],
height: size[1]
},
style: style
};
},
label: {
show: true,
position: 'top'
},
dimensions: ['from', 'to', 'profit'],
encode: {
x: [0, 1],
y: 2,
tooltip: [0, 1, 2],
itemName: 3
},
data: data
}
]
});
#main {
width: 600px;
height: 400px;
}
<html>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.3.2/echarts.min.js"></script>
<div id="main"></div>
</body>
</html>

custom ticks based on selected range from rangeSelector in Highstock

I am trying to get my Highcharts/Highstock chart to display a custom tick when the rangeSelector is set to All, I have it setup this way, but is throwing me errors when I try to use the interactive portion of the graph
Received below answer from Highstock Change tick interval on range selector change
componentDidMount() {
// Timezone Offset for PST standard is UTC timezone
Highcharts.setOptions({
global: {
timezoneOffset: 8 * 60
},
lang: {
thousandsSep: ','
}
});
Highcharts.stockChart('chart', {
chart: {
backgroundColor:'rgba(255, 255, 255, 0.0)',
height: 400,
zoomType: 'xy'
},
title: {
text: 'Bitcoin Chart',
style: {
color: 'white'
}
},
navigator: {
trigger: "navigator",
triggerOp: "navigator-drag",
rangeSelectorButton: undefined,
handles: {
backgroundColor: 'white',
borderColor: 'black'
}
},
scrollbar: {
enabled: false
},
rangeSelector: {
buttons: [{
type: 'day',
count: 1,
text: '1d'
}, {
type: 'day',
count: 7,
text: '7d'
}, {
type: 'month',
count: 1,
text: '1m'
}, {
type: 'month',
count: 3,
text: '3m'
}, {
type: 'year',
count: 1,
text: '1y'
}, {
type: 'all',
text: 'All'
}],
selected: 6,
// styles for the buttons
buttonTheme: {
fill: 'black',
// stroke: 'none',
'stroke-width': 0,
r: 8,
style: {
color: 'white',
fontWeight: 'bold'
},
states: {
hover: {
},
select: {
fill: 'white',
style: {
color: 'black'
}
}
}
},
// Date Selector box
inputBoxBorderColor: 'black',
inputBoxWidth: 120,
inputBoxHeight: 18,
inputStyle: {
color: 'black',
fontWeight: 'bold'
},
labelStyle: {
color: 'silver',
fontWeight: 'bold'
},
},
series: [{
name: 'Bitcoin Price',
color: 'black',
data: this.props.data.all_price_values,
type: 'area',
threshold: null,
tooltip: {
valuePrefix: '$',
valueSuffix: ' USD',
valueDecimals: 2
}
}],
plotOptions: {
series: {
fillColor: {
linearGradient: [0, 0, 0, 0],
stops: [
[0, '#FF9900'],
[1, Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0).get('rgba')]
]
}
}
},
xAxis: {
events: {
setExtremes: function(e) {
if (e.trigger === "rangeSelectorButton" && e.rangeSelectorButton.text === "All") {
var range = e.max - e.min;
// ticks spaced by one day or one hour
var ticksSpacing = range >= 86400 * 1000 ? 86400 : 3600;
this.update({
tickPositioner: function() {
var positions = [],
info = this.tickPositions.info;
for (var x = this.dataMin; x <= this.dataMax; x += ticksSpacing * 1000) { // Seconds * 1000 for ticks
positions.push(x);
};
positions.info = info;
return positions;
}
}, false);
}
}
},
title: {
enabled: true,
text: 'Date (Timezone: PST)',
style: {
color: 'white'
}
},
labels: {
style: {
color: 'white'
}
},
type: 'datetime',
dateTimeLabelFormats: {
day: '%b %e, %Y'
},
tickInterval: 1
},
yAxis: {
floor: 0,
labels: {
formatter: function () {
return '$' + this.axis.defaultLabelFormatter.call(this);
},
format: '{value:,.0f}',
align: 'left',
style: {
color: 'white'
},
},
},
// Mobile Design
responsive: {
rules: [{
condition: {
maxWidth: 600
},
chartOptions: {
chart: {
height: 400
},
subtitle: {
text: null
},
navigator: {
enabled: false
}
}
}]
}
});
}
I am talking about the blue highlighted section, when I move it, it throws an error
I am trying to get the charts to have 2 plots per day on ALL rangeSelector, displaying the first point in the day and the last point in a day. What am I doing wrong?
EDIT 1 : Updated to full config, X Axis is now being disrupted by the original answer, ticks on custom range selector is still in works. Added images to show what's going on
The setExtremes event is raised each time you change the range to be displayed on the graph. It can have several origins:
A button click in the range selector
An handle drag in the navigator
etc..
The actual properties of the event depend on its origin. If you output the event with console.log(e), you'll see that it's not the same for these two origins:
Button click in the range selector
{
trigger: "rangeSelectorButton",
rangeSelectorButton: {
text: "2Hour",
type: "hour",
count: 2,
},
...
}
Handle drag in the navigator
{
trigger: "navigator",
triggerOp: "navigator-drag",
rangeSelectorButton: undefined
}
If you drag the handle in the navigator, there's no rangeSelectorButton attached to the event, because it doesn't make sense: in that case, no button is pressed.
To fix your error, you can add a check on the trigger property:
xAxis: {
events: {
setExtremes: function(e) {
if (e.trigger === "rangeSelectorButton" && e.rangeSelectorButton.text === "All") {
...
}
}
}
How to solved the actual issue
Now, the REAL issue. You want to change the ticks based on what is displayed: either the beginning and end of each day, or hours if not a complete day. You can do that with e.min and e.max, that represent the selected time range.
Like so:
setExtremes: function(e) {
var range = e.max - e.min;
// ticks spaced by one day or one hour
var ticksSpacing = range >= 86400 * 1000 ? 86400 : 3600;
this.update({
tickPositioner: function() {
var positions = [],
info = this.tickPositions.info;
for (var x = this.dataMin; x <= this.dataMax; x += ticksSpacing * 1000) { // Seconds * 1000 for ticks
positions.push(x);
};
positions.info = info;
return positions;
}
}, false);
}

Integrating Highcharts Sparkline with Angular JS UI Grid

I am trying to integrate highcharts sparkline with angular ui-grid directive but unable to plot the sparkline. When we try to dynamically plot the sparklines using ui-grid nothing gets plotted. I have made necessary changes to the sparkline code as well yet unable to find what is the issue. We need age column to have highcharts sparkline. Any pointer will be of great help.
$scope.gridOptions = {
enableFiltering: false,
enableSorting: true,
}
$scope.gridOptions.columnDefs = [{
name: 'id'
}, {
name: 'name'
}, {
name: 'age',
cellTemplate: '<div id="table-sparkline" data-sparkline="71, 78, 39, 66"></div>'
}, {
name: 'address.city'
}];
$http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/500_complex.json')
.success(function(data) {
$scope.gridOptions.data = data;
console.log(JSON.stringify(data));
/**
* Create a constructor for sparklines that takes some sensible defaults and merges in the individual
* chart options. This function is also available from the jQuery plugin as $(element).highcharts('SparkLine').
*/
Highcharts.SparkLine = function(a, b, c) {
var hasRenderToArg = typeof a === 'string' || a.nodeName,
options = arguments[hasRenderToArg ? 1 : 0],
defaultOptions = {
chart: {
renderTo: (options.chart && options.chart.renderTo) || this,
backgroundColor: null,
borderWidth: 0,
type: 'area',
margin: [2, 0, 2, 0],
width: 120,
height: 20,
style: {
overflow: 'visible'
},
// small optimalization, saves 1-2 ms each sparkline
skipClone: true
},
title: {
text: ''
},
credits: {
enabled: false
},
xAxis: {
labels: {
enabled: false
},
title: {
text: null
},
startOnTick: false,
endOnTick: false,
tickPositions: []
},
yAxis: {
endOnTick: false,
startOnTick: false,
labels: {
enabled: false
},
title: {
text: null
},
tickPositions: [0]
},
legend: {
enabled: false
},
tooltip: {
backgroundColor: null,
borderWidth: 0,
shadow: false,
useHTML: true,
hideDelay: 0,
shared: true,
padding: 0,
positioner: function(w, h, point) {
return {
x: point.plotX - w / 2,
y: point.plotY - h
};
}
},
plotOptions: {
series: {
animation: false,
lineWidth: 1,
shadow: false,
states: {
hover: {
lineWidth: 1
}
},
marker: {
radius: 1,
states: {
hover: {
radius: 2
}
}
},
fillOpacity: 0.25
},
column: {
negativeColor: '#910000',
borderColor: 'silver'
}
}
};
options = Highcharts.merge(defaultOptions, options);
return hasRenderToArg ?
new Highcharts.Chart(a, options, c) :
new Highcharts.Chart(options, b);
};
var start = +new Date(),
$tds = $('div[data-sparkline]'),
fullLen = $tds.length,
n = 0;
// Creating 153 sparkline charts is quite fast in modern browsers, but IE8 and mobile
// can take some seconds, so we split the input into chunks and apply them in timeouts
// in order avoid locking up the browser process and allow interaction.
function doChunk() {
var time = +new Date(),
i,
len = $tds.length,
$td,
stringdata,
arr,
data,
chart;
for (i = 0; i < len; i += 1) {
$td = $($tds[i]);
stringdata = $td.data('sparkline');
arr = stringdata.split('; ');
data = $.map(arr[0].split(', '), parseFloat);
chart = {};
if (arr[1]) {
chart.type = arr[1];
}
$td.highcharts('SparkLine', {
series: [{
data: data,
pointStart: 1
}],
tooltip: {
headerFormat: '<span style="font-size: 10px">' + $td.parent().find('div').html() + ', Q{point.x}:</span><br/>',
pointFormat: '<b>{point.y}.000</b> USD'
},
chart: chart
});
n += 1;
// If the process takes too much time, run a timeout to allow interaction with the browser
if (new Date() - time > 500) {
$tds.splice(0, i + 1);
setTimeout(doChunk, 0);
break;
}
}
}
doChunk();
Plunker

Integrating Sparkline High Chart in Angular Nested UI Grid

I am trying to integrate spark line high chart in Angularjs nested UI grid. I have implemented Highcharts.SparkLine function within row expand event in UI grid. Is there any function like rowExpandComplete in Angular UiGrid?
gridApi.expandable.on.rowExpandedStateChanged($scope, function (row) {
if (row.isExpanded) {
var str = "10,12,45,34";
$scope.template = "<table id='table-sparkline'><tbody id='tbody-sparkline'><tr><td data-sparkline=" + str + "> </td> </tr> </tbody></table>";
row.entity.subGridOptions = {
columnDefs: [
{ name: 'name' },
{ name: 'gender', cellTemplate: $scope.template },
{ name: 'company' }
]
};
$http.get('https://cdn.rawgit.com/angular-ui/ui-grid.info/gh-pages/data/100.json')
.success(function (data) {
row.entity.subGridOptions.data = data;
/**
* Create a constructor for sparklines that takes some sensible defaults and merges in the individual
* chart options. This function is also available from the jQuery plugin as $(element).highcharts('SparkLine').
*/
Highcharts.SparkLine = function (a, b, c) {
var hasRenderToArg = typeof a === 'string' || a.nodeName,
options = arguments[hasRenderToArg ? 1 : 0],
defaultOptions = {
chart: {
renderTo: (options.chart && options.chart.renderTo) || this,
backgroundColor: null,
borderWidth: 0,
type: 'area',
margin: [2, 0, 2, 0],
width: 120,
height: 20,
style: {
overflow: 'visible'
},
// small optimalization, saves 1-2 ms each sparkline
skipClone: true
},
title: {
text: ''
},
credits: {
enabled: false
},
xAxis: {
labels: {
enabled: false
},
title: {
text: null
},
startOnTick: false,
endOnTick: false,
tickPositions: []
},
yAxis: {
endOnTick: false,
startOnTick: false,
labels: {
enabled: false
},
title: {
text: null
},
tickPositions: [0]
},
legend: {
enabled: false
},
tooltip: {
backgroundColor: null,
borderWidth: 0,
shadow: false,
useHTML: true,
hideDelay: 0,
shared: true,
padding: 0,
positioner: function (w, h, point) {
return { x: point.plotX - w / 2, y: point.plotY - h };
}
},
plotOptions: {
series: {
animation: false,
lineWidth: 1,
shadow: false,
states: {
hover: {
lineWidth: 1
}
},
marker: {
radius: 1,
states: {
hover: {
radius: 2
}
}
},
fillOpacity: 0.25
},
column: {
negativeColor: '#910000',
borderColor: 'silver'
}
}
};
options = Highcharts.merge(defaultOptions, options);
return hasRenderToArg ?
new Highcharts.Chart(a, options, c) :
new Highcharts.Chart(options, b);
};
var start = +new Date(),
$tds = $('td[data-sparkline]'),
fullLen = $tds.length,
n = 0;
// Creating 153 sparkline charts is quite fast in modern browsers, but IE8 and mobile
// can take some seconds, so we split the input into chunks and apply them in timeouts
// in order avoid locking up the browser process and allow interaction.
function doChunk() {
var time = +new Date(),
i,
len = $tds.length,
$td,
stringdata,
arr,
data,
chart;
for (i = 0; i < len; i += 1) {
$td = $($tds[i]);
stringdata = $td.data('sparkline');
arr = stringdata.split('; ');
data = $.map(arr[0].split(', '), parseFloat);
chart = {};
if (arr[1]) {
chart.type = arr[1];
}
$td.highcharts('SparkLine', {
series: [{
data: data,
pointStart: 1
}],
tooltip: {
headerFormat: '<span style="font-size: 10px">' + $td.parent().find('th').html() + ', Q{point.x}:</span><br/>',
pointFormat: '<b>{point.y}.000</b> USD'
},
chart: chart
});
n += 1;
// If the process takes too much time, run a timeout to allow interaction with the browser
if (new Date() - time > 500) {
$tds.splice(0, i + 1);
setTimeout(doChunk, 0);
break;
}
// Print a feedback on the performance
if (n === fullLen) {
$('#result').html('Generated ' + fullLen + ' sparklines in ' + (new Date() - start) + ' ms');
}
}
}
doChunk();
});
}
});
But it is triggering only for second time when we expand the row. Can any one tell me where I am going wrong. I have created the plunker for the same.

AngularJS : link between directive and controller

I'm trying to call a function of my directive in my controller. I would like to reshape a curve. At the moment, I can do within the Directive. But I need to outsource the call. Do you know how can I proceed?
My directive :
.directive('drawCharts', function () {
return function (scope, element, attrs) {
scope.$watch('curveArray', function () {
drawPlot();
}, true);
var drawPlot = function () {
/* Define var for interactive chart */
var tickInterval = scope.intervalMillisecond;
var typeLine = 'line';
var chart = function (curveArrayShow, typeLine, optionLineBool, yAxisCurveArrayShow, pointStart, pointEnd) {
var optionLine = { dataLabels: { enabled: optionLineBool, y: -20 }};
new Highcharts.Chart({
chart: {
pinchType: 'x',
renderTo: 'container',
type: typeLine,
backgroundColor:'rgba(255, 255, 255, 0.95)',
events: {
load: function(event) {
//alert ('Chart loaded : ' + event);
//scope.isLoading = false;
}
}
},
legend: {
enabled: false
},
title: {
text: ''
},
xAxis: [{
type: 'datetime',
labels: {
format: '{value: <b>%d/%m</b> %H:%M}',
staggerLines: 1
},
opposite: true,
plotLines: [{
color: '#FF0000', // Red
width: 2,
value: new Date().getTime() // Position, you'll have to translate this to the values on your x axis
}],
min: pointStart,
max: pointEnd,
minPadding: 0,
maxPadding: 0,
startOnTick: false, // allow to start not from tick
endOnTick: false,
tickInterval: tickInterval
}],
yAxis: yAxisCurveArrayShow,
tooltip: {
style:{ zindex: 10 },
xDateFormat: '%d/%m %H:%M',
shared: true,
},
plotOptions: {
column: { borderWidth: 1, borderColor: '#000000' },
spline: {
marker: {
radius: 4,
lineColor: '#666666',
lineWidth: 1
}
},
series: {
dataLabels: {
enabled: optionLineBool,
style: {font: 'bold 11px Arial', textShadow: '0 0 3px white, 0 0 3px white'}
},
marker: {
lineColor: null,
radius: 6,
states: {
hover: {
radius: 10
}
}
}
},
line: optionLine
},
series: curveArrayShow
}}
var pointStart = scope.intervalResearchTime.startDateTime + scope.intervalMillisecond; //remove padding that adding for serie type column
var pointEnd = scope.intervalResearchTime.endDateTime - scope.intervalMillisecond; //remove padding that adding for serie type column
chart(scope.curveArray, typeLine, false, scope.yAxisCurveArray, pointStart, pointEnd);
}
};
In my controller i need to update my chart. So i would like to call the function :
$scope.redrawCurve = function(_index) {
/* My algorithm ...... */
chart(curveArrayShow, typeLine, false, $scope.yAxisCurveArray, pointStart, pointEnd);
};
Thx,
Alex
My suggestion is to use event to notify the directive to redraw the plot, like this:
Controller:
$scope.redrawCurve = function(_index) {
/* My algorithm ...... */
$scope.$broadcast('redraw', arg1, arg2, /* other necessary arguments */);
};
Directive:
scope.$on('redraw', function(arg1, arg2, /* other necessary arguments */) {
drawPlot();
});

Resources