I am creating a scattered Graph using NVD3 using the code they have provided in their limited documentation. I have created a Scatter graph function that loops over a JSON and pushes the values to the data array.
Now I have 2 values for x axis in my Json , x and run Number. What i want is that the graph should be plotted for the value "x" (which have equal gaps) but it should display values of Run Number on the x axis (which have unequal gaps). We want to do this to make the graph more symmetric, as it is not important for us to display the gaps in graph accurately.
What i did was create a new Array xAxisValue and push the Run Numbers onto it while we loop the JSON to get values. values for x are pushed onto the data array ,
Then using
chart.xAxis.axisLabel('Run No.').tickFormat(d3.format('0d')).tickValues(
xAxisValue);
I set the Tick Values to the xAxisValue (run Number) and then pass the data variable to the draw chart function
d3.select('#chart svg').datum(myData).call(chart);
But this does not seem to work. My Axis is blank and hovering over a value displays the tool tip displays values of x instead of run number.
Because we are dynamically updating the graph i have separated the add Graph and upgrade graph function
Here is the code
function addGraph() {
var jsons = [];
chart = nv.models.scatterChart().showDistX(true).showDistY(true)
.transitionDuration(350).color(d3.scale.category10().range());
chart.tooltipContent(function(key) {
return '<h3>' + key + '</h3>';
});
chart.scatter.onlyCircles(false);
var myData = scatterData(2, 11, jsons);
d3.select('#chart svg').datum(myData).call(chart);
// add zoom handler
nv.utils.windowResize(chart.update);
return chart;
}
Upgrade Graph Function
function upgradeGraph() {
minValue = 1000000, maxValue = 0, minValueY = 100000000, maxValueY = 0;
var jsons = [];
d3.select('svg').text('');
if ($("#check2").is(':checked')) {
jsons.push("charge_ONTk_Barrel_L2_mpv.json");
}
if ($("#check1").is(':checked')) {
jsons.push("charge_ONTk_Barrel_L1_mpv.json");
}
if ($("#check3").is(':checked')) {
jsons.push("charge_ONTk_Barrel_L3_mpv.json");
}
var myData = scatterData(2, 11, jsons);
chart.xAxis.axisLabel('Run No.').tickFormat(d3.format('0d')).tickValues(
xAxisValue);
chart.yAxis.axisLabel('S/N (mpv)').tickFormat(d3.format('.04f'));
for (var i = 0; i < xAxisValue.length; i++) {
console.log("Run Number: " + xAxisValue[i]);
}
console.log("Min Y: " + minValueY + " Max Y " + maxValueY);
chart.forceX([ minValue - 2, maxValue + 2 ]);
chart.forceY([ minValueY - 3, maxValueY + 3 ]);
d3.select('#chart svg').datum(myData).call(chart);
// add zoom
addZoom({
xAxis : chart.xAxis,
yAxis : chart.yAxis,
yDomain : chart.yDomain,
xDomain : chart.xDomain,
redraw : function() {
chart.update();
},
svg : chart.svg
});
nv.utils.windowResize(chart.update);
return chart;
}
And the ScatterData Function
function scatterData(groups, points, jsons) {
var data = [];
data.push({
key : 'Error',
values : [],
color : '#FBEC5D'
});
data.push({
key : 'Bin Content ',
values : [],
color : '#0D4F8B'
});
for (var i = 0; i < jsons.length; i++) {
xAxisValue = [];
var jsonURL = jsons[i];
var xmlhttp = new XMLHttpRequest();
var url = "alljsons/" + jsons[i];
var parameters = location.search;
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
var myArr = JSON.parse(xmlhttp.responseText);
var jsonName = jsonURL.split(".");
var temp = jsonName[0];
var value = myArr[temp];
// $(".title" + loop).html(temp);
for ( var i in value) {
if (value[i].run > maxValue) {
maxValue = value[i].x;
}
if (value[i].run < minValue) {
minValue = value[i].x;
}
if (value[i].y > maxValueY) {
maxValueY = value[i].y;
}
if (value[i].y < minValueY) {
minValueY = value[i].y;
}
xAxisValue.push(value[i].run);
data[1].values.push({
x : value[i].x,
y : value[i].y,
size : 6 // Configure the size of each scatter point
,
shape : "circle"
});
var err = value[i].y - value[i].yErr;
if (err < 0) {
err = 0;
console.log("error: " + err);
}
data[0].values.push({
x : value[i].x,
y : err,
size : 6 // Configure the size of each scatter point
,
shape : "triangle-down"
});
}
}
};
xmlhttp.open("GET", url, false);
xmlhttp.send();
}
return data;
}
Here is the Output i am getting
If I understand your question correctly:
For the x-axis ticks, I would use D3's axis.tickformat function. You could create a function mapXToRunNumber(x) that takes an x value and returns a run number (you seem to be close to having this already). Then, you would use: chart.xAxis.tickFormat(mapXtoRunNumber);
For the tooltip to also show the same value as the x-axis, you would use the nvD3 function chart.interactiveLayer.tooltip.headerFormatter(mapXToRunNumber).
Related
I'm using Javascript and D3.js to create a Custom Widget in Thingsboard. I've managed to make a Chart work that plots a line from a given datasource. But I'm facing an issue when changing the datasource.
When user clicks on a new datasource, it should replaced the data on the graph. My D3.js chart takes data from an Array of point objects, with X and Y coordinates, that's generated from the datasource.
But I'm having the following issue: The dataset array gets the data correctly only the first time that it gets the points pushed. Whenever I try to clear the array or remove the dataset, and reapply the points, it acts as if the points were already there, but hiden. The problem will be clearer if you look at the Console log output.
Notice that the Array acts as if it was always the same/It contained the same points, even when I renew it with:
dataset = [];
I'm new to Javascript so this could be a very dumb problem, but I cannot find any similar issues nor know how to look for the issue.
My dataset is coded in a string like this:
"25,351,-442,0,112,3447,28,45,..."
Here's the code of my updating function:
var dataset = [];
function addDataPoint (value) {
//widget = this;
widget.string = value + ''; //Prevents compile error from .split()
var pointsArray = [];
var pointsArray = widget.string.split(',');
var newList = []; //As dataset is referenced by the D3.js chart, I wanted to test with an Unreferenced array to check if it had something to do with the reference.
console.log("PointsArrayLength " + pointsArray.length);
for (var index = 0; index < pointsArray.length; index++){
var number = parseInt(pointsArray[index],10);
var point = {
x: index % self.ctx.settings.xRangeMax,
y: number
};
if (widget.lastX && widget.lastX > point.x) {
//widget.data.length = 0;
}else{
if(!isNaN(number)){
widget.lastX = point.x;
newList.push(point);
dataset.push(point);
}
}
}
console.log("UnreferencedList Length: " +newList.length + " Index: "+index);
console.log("Referenced length: " + dataset.length);
//Redraw the graph
widget.line.attr('d', widget.lineGenerator);
}
//The function that gets called when the dataset changes.
self.onDataUpdated = function() {
var xValueString;
try{
var xValueString = self.ctx.defaultSubscription.data[0].data[0][1];
console.log("Size of data: " + xValueString.length);
dataset = [];//NOTE
/* I've tried several methods here to renew the Array.
dataset.length = 0 ; a while loop with dataset.pop() until length = 0, etc.
None of the methods make a difference*/
addDataPoint(xValueString);
console.log("Size of data: " + dataset.length);
} catch(err){
//For other debugging purposes
//console.log(err);
}
And here's the output from the Console Log:
//First run on dataset number 1:
Size of data: 55411
PointsArrayLength 3001
UnreferencedList Length: 3000 Index: 3001
Referenced length: 3000
Size of data: 3000
//First run on dataset Number 2:
//Note: Dataset Number 2 contains dataset number 1, and adds 12879 more points
Size of data: 69021
PointsArrayLength 15879
UnreferencedList Length: 12879 Index: 15879
Referenced length: 12879
Size of data: 12879
//Second Run on dataset number 1:
Size of data: 55411
PointsArrayLength 3001
UnreferencedList Length: 0 Index: 3001
Referenced length: 0
Size of data: 0
//Second Run on dataset Number 2:
Size of data: 69021
PointsArrayLength 15879
UnreferencedList Length: 1 Index: 15879
Referenced length: 1
Size of data: 1
I think that you are having scope issues. Define newlist as a global var.
let dataset = [];
let newlist = [];
function addDataPoint (value) {
//widget = this;
widget.string = value + ''; //Prevents compile error from .split()
let workArray = []
let pointsArray = [];
pointsArray = widget.string.split(',');
console.log("PointsArrayLength " + pointsArray.length);
for (var index = 0; index < pointsArray.length; index++){
var number = parseInt(pointsArray[index],10);
var point = {
x: index % self.ctx.settings.xRangeMax,
y: number
};
if (widget.lastX && widget.lastX > point.x) {
//widget.data.length = 0;
}else{
if(!isNaN(number)){
widget.lastX = point.x;
newList.push(point);
//dataset.push(point);
workArray.push(point);
}
}
}
console.log("UnreferencedList Length: " +newList.length + " Index: "+index);
console.log("Referenced length: " + dataset.length);
//Redraw the graph
widget.line.attr('d', widget.lineGenerator);
return workArray;
}
//The function that gets called when the dataset changes.
self.onDataUpdated = function() {
try{
let xValueString = self.ctx.defaultSubscription.data[0].data[0][1];
console.log("Size of data: " + xValueString.length);
//dataset = [];//NOTE
/* I've tried several methods here to renew the Array.
dataset.length = 0 ;
None of the methods make a difference*/
dataset = addDataPoint(xValueString);
console.log("Size of data: " + dataset.length);
} catch(err){
}
I have existing charts that displays data for a full day 12:00am - 12:00am.
Now required to change one chart forward to display 4:00am - 4:00am.
I have managed to shift the x axis labels (.add(4, 'hours')) but the chart data is still in the same position.
How do I shift the charted data forward 4 hours?
Limited scope to change global variables as this will impact other charts.
var getChartSeries = function(response, chart_series_data) {
var lines = response.graph_data.lines;
for (var i=0; i<lines.length; i++) {
var series = lines[i];
var dateFormat = graphDateFormat;
if (chartIntraday) dateFormat = 'HH:mm:ss';
var currSeriesData = [];
for (var j=0; j<series.data.length; j++) {
var row = series.data[j];
var yValue = parseFloat(row[1]);
var point = {
x: moment(row[0], dateFormat).add(4, 'hours').valueOf(),
y: yValue,
displayValue: row[3]
};
currSeriesData.push(point);
}
// Set the series name, legend label, and the line identifier
var name = formatLegendLabel(series.display_name, response);
var label = response.label;
if (response.display_name != undefined && response.display_name != '') label = series.display_name + ' : ' + label;
By default chart adjusts extremes to the provided data. To display these few hours before the first point use xAxis.min property.
Live demo: http://jsfiddle.net/kkulig/xqdqooh9/
API reference: https://api.highcharts.com/highcharts/xAxis.min
I have the following code:
http://jsfiddle.net/eM2Mg/7027/
g = new Dygraph(
// containing div
document.getElementById("graphdiv"),
// CSV or path to a CSV file.
("Response Time,Loop\n" +
"09/02/2015,175\n" +
"09/04/2015,170\n" +
"09/06/2015,180\n" +
"09/08/2015,177\n" +
"09/11/2015,179\n"+
"09/30/2015,165\n"),
{
strokeWidth: 3,
}
);
Is it possible in dygraphs to set the same distance between x-axis values?
I want to set the same line length among all points no matter of the date time space.
I doubt you can get it to fully work as your requirement is against how data is arranged on graphs, but you can use parse & format callbacks to come close:
var i = 0;
var labels = [];
g = new Dygraph(
// containing div
document.getElementById("graphdiv"),
// CSV or path to a CSV file.
("Response Time,Loop\n" +
"09/02/2015,175\n" +
"09/04/2015,170\n" +
"09/06/2015,180\n" +
"09/08/2015,177\n" +
"09/11/2015,179\n" +
"09/30/2015,165\n"), {
strokeWidth: 3,
xValueParser: function (str) {
console.log(str);
labels[++i] = str;
return i;
},
axes: {
x: {
valueFormatter: function (i) {
return labels[i];
},
axisLabelFormatter: function (i) {
return labels[i];
}
}
}
});
See fiddle
SOLUTION: I realized that the current Regional Setting of test environment is set to Turkish, and it uses comma for decimal symbol. In my local, it is set to UK, and that's the reason that the code works in my local and doesn't work in test. I guess I'll replace all commas with dots beforehand. Thanks for all the replies.
I'm trying to fill a bar chart with following data:
var oneToTen = [0,1,2,3,4,5,6,7,8,9,10];
var ticks = [[0, 'Atmosfer'],[1, 'Servis'],[2, 'Yemeklerimiz']];
var labels = ['Atmosfer','Servis','Yemeklerimiz'];
var mainQuest_d1 = 8,16666666666667;
var mainQuest_d2 = 7,95833333333333;
var mainQuest_d3 = 8,125;
var d_main_quest_bar = [[0, 8,16666666666667],[1, 7,95833333333333],[2, 8,125]];
I get this error:
Uncaught SyntaxError: Unexpected number
I can't see what's wrong the code above. It works fine in localhost, but when I publish it to the test server, it gives this error.
Complete code that's not yet rendered by Razor:
int i = 0;
int j = 0;
int m = 0;
#Html.Raw("var oneToTen = [0,1,2,3,4,5,6,7,8,9,10];");
#Html.Raw("var ticks = [");
if (Model.MainQuestionsRatingList != null)
{
foreach (var item in Model.MainQuestionsRatingList)
{
j++;
#Html.Raw("["+(j-1)+", '"+item.QuestionText+"']")
if (j != Model.MainQuestionsRatingList.Count) { #Html.Raw(","); }
}
}
#Html.Raw("];");
#Html.Raw("var labels = [");
if (Model.MainQuestionsRatingList != null)
{
foreach (var item in Model.MainQuestionsRatingList)
{
m++;
#Html.Raw("'"+item.QuestionText+"'")
if (m != Model.MainQuestionsRatingList.Count) { #Html.Raw(","); }
}
}
#Html.Raw("];");
if (Model.MainQuestionsRatingList != null)
{
foreach (var item in Model.MainQuestionsRatingList)
{
i++;
#Html.Raw("var mainQuest_d" + i + " = " + item.Avg + ";");
}
}
i = 0;
#Html.Raw("var d_main_quest_bar = [");
if (Model.MainQuestionsRatingList != null)
{
foreach (var item in Model.MainQuestionsRatingList)
{
i++;
#Html.Raw("[" + (i-1) + ", "+item.Avg+"]");
if (i != Model.MainQuestionsRatingList.Count) { #Html.Raw(","); }
}
}
#Html.Raw("];");
}
data.push({
label: labels,
data: d_main_quest_bar,
bars: {
show: true,
barWidth: 0.2,
order: 1
}
});
EDIT: I ran the same code in my local, and figured out that the commas are automatically replaced with dots and that's why it works in my local as #T.J. Crowder said. But it doesn't happen when I run it in test. How is that possible?
You can't use localized decimal separator characters in JavaScript source code. You must use .:
var mainQuest_d1 = 8.16666666666667;
var mainQuest_d2 = 7.95833333333333;
var mainQuest_d3 = 8.125;
See What is the decimal separator symbol in JavaScript?
It should be obvious that , has another meaning already. How many elements do you expect the array
[0, 8,16666666666667]
to contain?
You shouldn't use commas in your numbers. Use a decimal place instead. Commas are special characters reserved for other uses, such as separators in arrays and function parameters.
For example:
8,16666666666667
should be
8.16666666666667
You have a few instances so here is the full code written correctly:
var oneToTen = [0,1,2,3,4,5,6,7,8,9,10];
var ticks = [[0, 'Atmosfer'],[1, 'Servis'],[2, 'Yemeklerimiz']];
var labels = ['Atmosfer','Servis','Yemeklerimiz'];
var mainQuest_d1 = 8.16666666666667;
var mainQuest_d2 = 7.95833333333333;
var mainQuest_d3 = 8.125;
var d_main_quest_bar = [[0, 8.16666666666667],[1, 7.95833333333333],[2, 8.125]];
(there are 6 changes in total across the last 4 lines)
You shouldn't use commas for integers:
<script type="text/javascript">
var oneToTen = [0,1,2,3,4,5,6,7,8,9,10];
var ticks = [[0, 'Atmosfer'],[1, 'Servis'],[2, 'Yemeklerimiz']];
var labels = ['Atmosfer','Servis','Yemeklerimiz'];
var mainQuest_d1 = 8.16666666666667;
var mainQuest_d2 = 7.95833333333333;
var mainQuest_d3 = 8.125;
var d_main_quest_bar = [[0, 8.16666666666667],[1, 7.95833333333333],[2, 8.125]];
</script>
Source: http://en.wikipedia.org/wiki/JavaScript_syntax#Number
This is incorrect:
var mainQuest_d1 = 8,16666666666667;
var mainQuest_d2 = 7,95833333333333;
var mainQuest_d3 = 8,125;
You can't use commas there.
I'm trying create a gantt chart with highcharts and I need to put the data in the JSON format. I'm getting real close, but the problem i'm having is that the data I am pushing is being surrounded by quotes. I'm guessing there is just something that I am doing wrong, but I can't figure it out.
I can tell that the issue is with quotes being added because i have some static data that works just fine and I printed the object in the firebug console for the static data and my dynamic data.
So here is the very basic of my options var:
var options = {
series : [],
test : [ {
data : [ {
low : Date.UTC(2012, 0, 1),
y : Date.UTC(2012, 0, 15)
}, {
low : Date.UTC(2012, 0, 10),
y : Date.UTC(2012, 4, 28)
} ]
}
}
Then I have this function which gets called at load time:
function loadData() {
var chartData = $('#hiddenDate').val();
console.log('hiddenDate = '+chartData);
var goodData = chartData.split('|');
console.log('goodData = '+goodData);
var series = {
data : []
};
try {
$.each(goodData, function(index, value) {
var goodData2 = value.split(",");
var startYear = goodData2[0].substr(0, 4);
var endYear = goodData2[1].substr(0, 4);
var startMonth = goodData2[0].substr(5, 2);
var endMonth = goodData2[1].substr(5, 2);
var startDay = goodData2[0].substr(8, 2);
var endDay = goodData2[1].substr(8, 2);
/*series.data.push({
low : 'Date.UTC('+startYear+','+startMonth+','+startDay+')',
y : 'Date.UTC('+endYear+','+endMonth+','+endDay+')'
});*/
var start = "{low : Date.UTC("+startYear+","+startMonth+","+startDay+")";
var end = "y : Date.UTC("+endYear+","+endMonth+","+endDay+")}";
series.data.push(start);
series.data.push(end);
//series.data.y.push('Date.UTC('+endYear+','+endMonth+','+endDay+')');
console.log('series.data = '+series.data.toSource());
console.log('options.test = '+options.test.toSource());
});
options.series.push(series);
console.log('options.series = '+options.series.toSource());
} catch (err) {
console.log("ERROR ..." + err.description + ' message:'+ err.message);
}
}
And here is the firebug output where I can see that the quotes are causing an issue for options.series:
series.data = ["{low : Date.UTC(2011,05,27)", "y : Date.UTC(2011,02,17)}", "{low : Date.UTC(2011,07,05)", "y : Date.UTC(2010,12,23)}"]
options.test = [{data:[{low:1325376000000, y:1326585600000}, {low:1326153600000, y:1338163200000}]}]
options.series = [{data:["{low : Date.UTC(2011,05,27)", "y : Date.UTC(2011,02,17)}", "{low : Date.UTC(2011,07,05)", "y : Date.UTC(2010,12,23)}"]}]
shouldn't your start and end variables be object literals instead of strings?