CanvasJS not properly rendering chart - javascript

I am using the following code to render an OHLC chart in CanvasJS:
<script>
var candleData = [];
var chart = new CanvasJS.Chart("chartContainer", {
title: {
text: 'Demo Stacker Candlestick Chart (Realtime)'
},
zoomEnabled: true,
axisY: {
includeZero: false,
title: 'Price',
prefix: '$'
},
axisX: {
interval: 1,
},
data: [{
type: 'ohlc',
dataPoints: candleData
}
]
});
function mapDataToPointObject(data) {
var dataPoints = [];
for(var i = 0; i < data.length; i++) {
var obj = data[i];
var newObj = {
x: new Date(obj.time),
y: [
obj.open,
obj.high,
obj.low,
obj.close
]
}
dataPoints.push(newObj);
}
return dataPoints;
}
function updateChart() {
$.ajax({
url: 'http://localhost:8080',
success: function(data) {
candleData = JSON.parse(data);
candleData = mapDataToPointObject(candleData);
chart.render();
}
});
}
$(function(){
setInterval(() => {
updateChart();
}, 500);
});
The data properly loads, parses into the correct format, and render() is called on the interval like it should. The problem is, while the chart axes and titles all render properly, no data shows up. The chart is empty.
What DOES work is setting the data directly to the chart using
chart.options.data[0].dataPoints = candleData;
Why does my above solution not work then? Is there a way I can update the chart's dataPoints without having to hardcode a direct accessor to the chart's dataPoints?

It's related to JavaScript pass by value and pass by reference.
After execution of the following line.
dataPoints: candleData
dataPoints will refer to the current value of candleData. ie. dataPoints = [];
Now if you redefine candleData to any other value.
candleData = JSON.parse(data);
candleData = mapDataToPointObject(candleData);
Then dataPoints won't be aware of this update and will still refer to the empty array (that you pointed earlier).
The following snippet will make it easy to understand
//pass by value
var a = "string1";
var b = a;
a = "string2";
alert("b is: " + b); //this will alert "string1"
//pass by reference
var c = { s: "string1" };
var d = c;
c.s = "string2";
alert("d.s is: " + d.s); //this will alert "string2"
For more, you can read about pass by value and pass by reference.
Javascript by reference vs. by value
Explaining Value vs. Reference in Javascript

Related

Highcharts 2 groups of series

I want to display a chart (that takes the data from a PHP file with JSON) with two dimensions of series : the first one is the technology used (5 in total), and the other one is the export or import.
So when the user is on the page, he can choose to diplay the technology, as export, import or both.
In first, to join one technology import with the same in export, I have used a "linkedto=previous", the result is a single item in the legend per technology.
But I would like to add two items in the legend : "Import" and "Export", with 0 data, that would permit to display or not the import or the export.
I have used this code, but I can't find how to display the choice of import, export, the both, or nothing.
Thank you very much if you take a bit of time to read my post. BR
$(function () {
var chart;
$(document).ready(function() {
var options = {
chart: {
renderTo: 'euro',
type: 'column'
},
title: {
text: 'Vision en euro'
},
xAxis: {
categories: []
},
yAxis: {
title: {
text: 'k€'
},
stackLabels: {
enabled: true,
rotation: 30,
}
},
tooltip: {
headerFormat: '{point.x}<b></b><br/>',
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: false,
},
}
},
series: []
};
Highcharts.setOptions(Highcharts.theme);
$.getJSON('SOURCE.php', function(json) {
options.xAxis.categories = json[0]['month'];
options.series[0] = {};
options.series[0].name = 'TECHNO 1';
options.series[0].data = json[1]['data'];
options.series[0].stack ='EXPORT';
options.series[0].color= '#808080';
options.series[1] = {};
options.series[1].name = 'TECHNO 1';
options.series[1].data = json[0]['data'];
options.series[1].stack = 'IMPORT';
options.series[1].linkedTo = ':previous';
options.series[1].color= 'url(#highcharts-default-pattern-0)';
options.series[2] = {};
options.series[2].name = 'TECHNO 2';
options.series[2].data = json[3]['data'];
options.series[2].stack = 'EXPORT';
options.series[2].color= '#FFC125';
options.series[3] = {};
options.series[3].name = 'TECHNO 2';
options.series[3].data = json[2]['data'];
options.series[3].stack = 'IMPORT';
options.series[3].linkedTo = ':previous';
options.series[3].color= 'url(#highcharts-default-pattern-1)';
options.series[4] = {};
options.series[4].name = 'TECHNO 3';
options.series[4].data = json[5]['data'];
options.series[4].stack = 'EXPORT';
options.series[4].color= '#2B99FF';
options.series[5] = {};
options.series[5].name = 'TECHNO 3';
options.series[5].data = json[4]['data'];
options.series[5].stack = 'IMPORT';
options.series[5].linkedTo = ':previous';
options.series[5].color= 'url(#highcharts-default-pattern-2)';
options.series[6] = {};
options.series[6].name = 'TECHNO 4';
options.series[6].data = json[7]['data'];
options.series[6].stack = 'EXPORT';
options.series[6].color= '#C72828';
options.series[7] = {};
options.series[7].name = 'TECHNO 4';
options.series[7].data = json[6]['data'];
options.series[7].stack = 'IMPORT';
options.series[7].linkedTo = ':previous';
options.series[7].color= 'url(#highcharts-default-pattern-3)';
options.series[8] = {};
options.series[8].name = 'TECHNO 5';
options.series[8].data = json[9]['data'];
options.series[8].stack = 'Sortie';
options.series[8].color= '#1CA154';
options.series[9] = {};
options.series[9].name = 'TECHNO 5';
options.series[9].data = json[8]['data'];
options.series[9].stack = 'EXPORT';
options.series[9].linkedTo = ':previous';
options.series[9].color= 'url(#highcharts-default-pattern-4)';
options.series[10] = {};
options.series[10].name = 'IMPORT';
options.series[10].data = json[10]['data'];
options.series[10].stack = 'IMPORT';
options.series[10].color= 'url(#highcharts-default-pattern-5)';
options.series[11] = {};
options.series[11].name = 'EXPORT';
options.series[11].data = json[11]['data'];
options.series[11].stack = 'IMPORT';
options.series[11].color= 'url(#highcharts-default-pattern-5)';
//options.series[1].color= '#C89B9B';
chart = new Highcharts.Chart(options);
});
});
});
First of all, you can hide or show a series in a chart by modifying the "visible" property of a series object to false or true respectively. For example:
options.series[10].visible = true; // or false
Secondly, you can achieve that in an event listener (the push of a button for example), using chart.update() method, and passing the changes as an argument. Have a look in here: Dynamic charts -> update options after render.
But the simplest solution is to just repeat the
chart = new Highcharts.Chart(options);
statement, after you have put in the series object the "visible" property with the value you like, for each series (import/export) you want to show or hide.
Finally, having 2 kinds of clickable labels in the legend can only be done with some custom jquery programming of your own. I think a couple of small buttons next to the chart would be much easier and faster to implement.

Re-compile JSON data to make less rows

If I have a JSON result that has many rows, sometimes up to 100
Label:Part1 Value:1000
Label:Part2 Value:700
Label:Part3 Value:600
Label:Part4 Value:500
... and so on
I would like to change the data so that it lists the top 5 results as normal, but instead of listing the rest, it sums the value and changes the label to 'others'.
Example
Label:Part1 Value:1000
Label:Part2 Value:700
Label:Part3 Value:600
Label:Part4 Value:500
Label:Part5 Value:500
Label:other Value:25650
Is this possible to do in javascript, before I pass to a chart.js pie? Or is there a better method to achieve this?
Current code for pie
function chart1(branch, apitime){
$.ajax({
url: jsonpath' + apitime + branch,
dataType: 'jsonp',
success: function (response) {
console.log (response);
var datachart = response;
var ctx2 = document.getElementById("chart-area2").getContext("2d");
var myChart = new Chart(ctx2).Pie(datachart);
}
});
}
Data is sorted and I could change the SQL on the server to do this also, I just don't know what the correct method is to do that.
JSON example here http://pastebin.com/p3Y9mSX3
You can modify your data just before creating the chart.
var top6 = datachart.slice(0,5)
top6[5] = {
label: 'other',
value: datachart.slice(5).reduce(function(sum, data) {
return sum + data.value
}, 0)
}
var ctx2 = document.getElementById("chart-area2").getContext("2d");
var myChart = new Chart(ctx2).Pie(top6);
This can be made more general to get the top N entries
var getTopN = function(dataArray, n)
var tops = dataArray.slice(0, n)
tops[n] = {
label: 'other',
value: dataArray.slice(n).reduce(function(sum, data) {
return sum + data.value
}, 0)
}
return tops
}
var top5 = getTopN(datachart, 5)
var top10 = getTopN(datachart, 10)

How to display custom tooltips on line chart using Rickshaw Library

I am developing a realtime graph system which will display the memory usage at particular time using data from json file . I am using Rickshaw Library which accepts tool tip in numeric type else the hard coded value supplied as a property to graph .
I have a json object as :
[
{
"memory": 444.08203125,
"memoryInfo": {
"rss": 444.08203125,
"vsize": 1271.125
},
"cpu": 0.2,
"url": [
"/admin/company/approved"
],
"time": "2/12/2016, 10:42:09 AM"
},
...
...
]
I want to show in tool tip at particular time what was the url served by server so that i can get proper information like which route is consuming more memory.
I will share my so far js code with you so that it will be better to understand .
script.js
$(function(){
var json = null;
console.log("Document Ready");
$.ajax({
url: 'data.json',
type: 'get',
success: function (data) {
console.log("Got data");
json = data
drawGraph()
}
});
var interval = 250;
//function to use from populating new values to graph
var getMemory = function(index) {
return json[index].memory
}
var getUrl = function(index) {
return json[index].url[0]
}
var getToolTip = function(){
console.log("getting tooltip")
return "api/login"
}
var drawGraph = function(){
// instantiate our graph!
graph = new Rickshaw.Graph( {
element: document.getElementById("chart"),
width: 900,
height: 400,
renderer: 'line',
interpolate:'basis',
series: new Rickshaw.Series.FixedDuration([{ name: 'memory' ,color:'steelblue',tooltip:"/api/login"}], undefined, {
timeInterval: interval,
maxDataPoints: 500,
timeBase: new Date().getTime() / 1000,
})
})
//tooltip is hardcoded should be dynamic when fetching each object from json
graph.render();
// get Recent log data using socket and feed it to graph
var i = 0;
var iv = setInterval( function() {
i++
var data = { memory: getMemory(i)};
graph.series.addData(data);
graph.render();
}, interval );
//hover details
var hoverDetail = new Rickshaw.Graph.HoverDetail( {
graph: graph,
formatter: function(series, x, y) {
var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>';
var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
var content = swatch + series.tooltip + ": " + parseInt(y) + '<br>' + date;
console.log(series)
return content;
}
});
}
});//jQuery
Can you give us some further information regarding your problem/error?
From a quick look that I had, your tooltips (Rickshaw.Graph.HoverDetail) won't be able to render because you are asking in formatter for inputs "series,x,y" and you haven't set each element of the data array to have a x and y value.
example:
data: [ { x: 0, y: 5 }, { x: 1, y: 10 } ]
Take a look at rickshaw example here.

HighCharts Loading from CSV File with Many Data Points

After much searching and wanting to bang my head against my desk, I'm posting my first question on stackoverflow.
I've got an ASP.NET web application that is generating some data on the server side and creating a CSV file on the server.
I'm using HighCharts to produce a graph. Everything is working great, however, depending on what kind of date range a user runs there can be a few data points or many data points.
The problem comes in when there are many data points. Look at the X-Axis, and you'll see what I mean. Is there anyway to "group" these where it doesn't show every single point on the X-Axis?
The dates are at random intervals.
I've created a JSFiddle with my client side code and the contents of my CSV file in a JavaScript variable.
Here is my code:
function parseDate(dateStr) {
var matches = dateStr.match(/([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})/)
return Date.UTC(matches[3], matches[1]-1, matches[2]);
}
var csv = 'Chart,3/4/2007,3/24/2007,4/13/2007,4/25/2007,9/19/2007,9/28/2007,11/5/2007,1/7/2008,1/14/2008,1/21/2008,1/27/2008,2/3/2008,2/10/2008,2/17/2008,2/24/2008,3/2/2008,3/23/2008,3/30/2008,4/5/2008,4/21/2008,5/3/2008,5/10/2008,5/17/2008,5/24/2008,5/31/2008,6/8/2008,6/15/2008,6/29/2008,7/4/2008,7/18/2008,7/25/2008,8/1/2008,8/8/2008,9/17/2010,11/25/2010,8/16/2012,1/17/2013,1/27/2013\nDates,180.00,175.50,167.00,166.50,170.00,171.50,174.00,163.00,162.50,164.00,166.50,166.50,167.50,170.00,170.00,171.00,169.00,166.50,166.00,166.50,162.00,160.00,160.50,162.50,164.00,164.00,165.00,165.50,166.00,169.00,171.00,170.00,171.00,165.00,165.00,189.00,177.00,175.50';
var options = {
chart: {
renderTo: 'chart',
defaultSeriesType: 'line'
},
title: {
text: 'Test Chart'
},
xAxis: {
type: 'datetime',
categories: []
},
yAxis: {
title: {
text: 'Pounds'
}
},
series: []
};
// Split the lines
var lines = csv.split('\n');
$.each(lines, function(lineNo, line) {
var items = line.split(',');
if (lineNo == 0) {
$.each(items, function(itemNo, item) {
if (itemNo > 0) options.xAxis.categories.push(parseDate(item));
});
}
else {
var series = {
data: []
};
$.each(items, function(itemNo, item) {
if (itemNo == 0) {
series.name = item;
} else {
series.data.push(parseFloat(item));
}
});
options.series.push(series);
}
});
var chart = new Highcharts.Chart(options);
Here is the link to JSFiddle:
http://jsfiddle.net/Q2hyF/6/
Thanks in Advance,
Robert
Check out HighStocks and its DataGrouping feature:
http://www.highcharts.com/stock/demo/data-grouping
It can handle much larger datasets than HighCharts can. However, there are drawbacks as the newest HighCharts features are not always immediately in HighStocks. There are generally only minor changes needed to your syntax to use HighStocks, if you want to test it.
I ended up getting this working and never posted the answer... Here is the answer.
Take a close look at:
series.data.push([parseDate(points[0]), parseFloat(points[1])]);
in the code below...
function parseDate(dateStr) {
var matches = dateStr.match(/([0-9]{1,2})\/([0-9]{1,2})\/([0-9]{4})/)
return Date.UTC(matches[3], matches[1] - 1, matches[2]);
}
var csv = 'Chart,11/1/2013|6,11/2/2013|4,11/3/2013|6,11/4/2013|3,11/5/2013|5,11/6/2013|5,11/7/2013|5,11/8/2013|6,11/9/2013|4,11/10/2013|13,11/11/2013|12,11/12/2013|3,11/13/2013|5,11/14/2013|7,11/15/2013|9,11/16/2013|0,11/17/2013|2,11/18/2013|3,11/19/2013|2,11/20/2013|16,11/21/2013|6,11/22/2013|9,11/23/2013|9,11/24/2013|20,11/25/2013|10,11/26/2013|10,11/27/2013|4,11/28/2013|9,11/29/2013|7,11/30/2013|7';
var options = {
chart: {
renderTo: 'chart',
type: 'line'
},
title: {
text: 'Sales'
},
xAxis: {
type: 'datetime'
},
series: []
};
var lines = csv.split('\n');
$.each(lines, function (lineNo, line) {
var items = line.split(',');
var series = {
data: []
};
$.each(items, function (itemNo, item) {
if (itemNo == 0) {
series.name = item;
} else {
var points = item.split('|');
series.data.push([parseDate(points[0]), parseFloat(points[1])]);
}
});
options.series.push(series);
});
var chart = new Highcharts.Chart(options);
http://jsfiddle.net/rswilley601/gtsLatyr/

Add another series to existing plot with flot

I need to know how I can easily add another series to an existing plot using Flot.
Here is how I currently plot a single series:
function sendQuery() {
var host_name = $('#hostNameInput').val();
var objectName = $('#objectNameSelect option:selected').text();
var instanceName = $('#instanceNameSelect option:selected').text();
var counterName = $('#counterNameSelect option:selected').text();
$.ajax({
beforeSend: function () {
$('#loading').show();
},
type: "GET",
url: "http://okcmondev102/cgi-bin/itor_PerfQuery.pl?machine=" + host_name + "&objectName=" + objectName + "&instanceName=" + instanceName + "&counterName=" + counterName,
dataType: "XML",
success: function (xml) {
var results = new Array();
var counter = 0;
var $xml = $.xmlDOM(xml);
$xml.find('DATA').each(function () {
results[counter] = new Array(2);
results[counter][0] = $(this).find('TIMESTAMP').text();
results[counter][1] = $(this).find('VALUE').text();
counter++;
});
plot = $.plot($("#resultsArea"), [{
data: results,
label: host_name
}], {
series: {
lines: {
show: true
}
},
xaxis: {
mode: "time",
timeformat: "%m/%d/%y %h:%S%P"
},
colors: ["#000099"],
crosshair: {
mode: "x"
},
grid: {
hoverable: true,
clickable: true
}
});
You can just add another results set:
// build two data sets
var dataset1 = new Array();
var dataset2 = new Array();
var $xml = $.xmlDOM(xml);
$xml.find('DATA').each(function(){
// use the time stamp for the x axis of both data sets
dataset1[counter][0] = $(this).find('TIMESTAMP').text();
dataset2[counter][0] = $(this).find('TIMESTAMP').text();
// use the different data values for the y axis
dataset1[counter][1] = $(this).find('VALUE1').text();
dataset2[counter][2] = $(this).find('VALUE2').text();
counter++;
});
// build the result array and push the two data sets in it
var results = new Array();
results.push({label: "label1", data: dataset1});
results.push({label: "label2", data: dataset2});
// display the results as before
plot = $.plot($("#resultsArea"), results, {
// your display options
});
At a high-level, the result of your call into itor_PerfQuery.pl will need to be extended to include the additional series data. You'll then want to make your "results" variable a multi-dimensional array to support the additional data and you'll need to update the current xml "find" loop which populates results accordingly. The remainder of the code should stay the same as flot should be able to plot the extended dataset. I think a review of the flot example will help you out. Best of luck.

Categories