Strange functionality of NVD3 Graph when updating data - javascript

I have some graphs using d3/nvd3. I am now wanting to be able to update the chart data with the click of a button, I have got it working 90% but when I update the data, the functionality becomes inconsistent.
By this I mean that the clickable legend stops working properly, usually you can double click one of them and it would single out the data.
I think somehow when the data updates, it still has the old data in it's memory and this is causing some issues?
Here is my javascript -
$( document ).ready(function() {
var negative_test_data = [{"key":"O1","values":[{"x":"NRW ","y":1},{"x":"WFW ","y":3}]},{"key":"O2","values":[{"x":"MED ","y":1},{"x":"FSEST ","y":1},{"x":"SW ","y":1},{"x":"LW ","y":4}]},{"key":"O3","values":[{"x":"SEEG ","y":1},{"x":"DLRW ","y":1},{"x":"SEM ","y":1},{"x":"DEN ","y":1},{"x":"LEW ","y":3}]},{"key":"O4","values":[{"x":"BUC ","y":2}]}];
var chart;
nv.addGraph(function() {
chart = nv.models.multiBarChart()
.color(d3.scale.category10().range())
.rotateLabels(0) //Angle to rotate x-axis labels.
.transitionDuration(200)
.showControls(false) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.24) //Distance between each group of bars.
;
chart.reduceXTicks(false).staggerLabels(true).groupSpacing(0.3);
chart.x (function(d) { return d.x; })
chart.yAxis
.tickFormat(d3.format(',.1f'))
.axisLabel('Defect Count')
.axisLabelDistance(40);
d3.select('#chart1M svg')
.datum(negative_test_data)
.call(chart);
return chart;
});
});
var update = function() {
var data = [{"key":"O1","values":[{"x":"NRW ","y":20},{"x":"WW ","y":3}]},{"key":"O2","values":[{"x":"ME ","y":1},{"x":"FST ","y":1},{"x":"SW ","y":1},{"x":"LEW ","y":4}]},{"key":"O3","values":[{"x":"SEEG ","y":1},{"x":"DLW ","y":1},{"x":"SEM ","y":1},{"x":"DE ","y":1},{"x":"LW ","y":3}]},{"key":"O4","values":[{"x":"BUDC ","y":2}]}];
var chart;
nv.addGraph(function() {
chart = nv.models.multiBarChart()
.color(d3.scale.category10().range())
.rotateLabels(0) //Angle to rotate x-axis labels.
.transitionDuration(250)
.showControls(false) //Allow user to switch between 'Grouped' and 'Stacked' mode.
.groupSpacing(0.24) //Distance between each group of bars.
;
chart.reduceXTicks(false).staggerLabels(true).groupSpacing(0.3);
chart.x (function(d) { return d.x; })
chart.yAxis
.tickFormat(d3.format(',.1f'))
.axisLabel('Defect Count')
.axisLabelDistance(40);
d3.select('#chart1M svg')
.datum(data)
.call(chart);
return chart;
});
};
Here is the JSFIDDLE - http://jsfiddle.net/kd82ep7p/8/ so that it can be demonstrated,
Before the data is updated you can play with the legend and select the data you want to see and even double click it.
After you click the update button, it becomes a problem,
If anyone could take a look I would greatly appreciate it.

Related

How to redraw chart : d3.js chaining function and crossfilter filtering

Edit : JSBin dont work go to JS fiddle to see my code
I'm training my self with d3 and crossfilter and i face some difficulties.
I'm trying to make a chaining function to create bar chart. The idea is to not write all the code again and again for each bar chart i want to create.
I was inspired by the example Fast Multidimensional Filtering for Coordinated Views which is what i wanted to do (you can find code Here).
But I want to bring some personal touches in my generic barChart function.
I decided to do it with chaining function like in the example.
I understood how to create as many graphics as you wish, but i dont understand, (when brushes event appears), how to redraw all the bar depending on the filter.
If i wanted to do it outside a function i would define again the all the properties like x, y, axis etc. depending on the new data which is the filtered data like this:
var updateRange = function(filt){
data = dimension.filter(filt) //assuming dimension is a crossfilter dimension
// Scale the range of the data again
x.domain([0, d3.max(data, function(d) { return d.key; })+1]);
// Select the section we want to apply our changes to
var chart = d3.select(".chart")
//Update all rects
chart.selectAll("rect.hidden")
.data(data)
.transition()
.delay(function(d, i) {
return i * 50;
})
.duration(500)
.attr("y", function(d) {
return y2(d.value);
})
.attr("height", function(d) {
return height - y2(d.value);
});
I made a JSBin to discuss on how we can make the chart updated.
And this is the brush functions i use.
brush.on("brushstart", function() {
var div = d3.select(this.parentNode.parentNode.parentNode);
div.select(".title a").style("display", null);
});
brush.on("brush", function() {
var g = d3.select(this.parentNode),
extent = brush.extent();
if (round) g.select(".brush")
.call(brush.extent(extent = extent.map(round)))
.selectAll(".resize")
.style("display", null);
g.select("#clip-" + id + " rect")
.attr("x", x(extent[0]))
.attr("width", x(extent[1]) - x(extent[0]));
dimension.filter(extent);
});
brush.on("brushend", function() {
if (brush.empty()) {
var div = d3.select(this.parentNode.parentNode.parentNode);
div.select(".title a").style("display", "none");
div.select("#clip-" + id + " rect").attr("x", null).attr("width", width);
dimension.filterAll();
}
)};
waiting for your comments,
Chris.
Edit : some clarification
To be clearer, when i render the chart and use the brush the data are correctly filtered. (if i put some console.log i see the data filtered depending on the brush)
But the chart are not updated depending on the brush. I know the issue come from th way i used the brush event (brush.on().
I think i need to call the render function in some way but dont know how to do it with chaining function to be applied to all the charts.
Edit : Chart updated with external brush
The chart are now successfully updated when the brush is set externally (by clicking on the link).
Just adding this lines
if (brushDirty) {
brushDirty = false;
g.selectAll(".brush").call(brush);
div.select(".title a").style("display", brush.empty() ? "none" : null);
if (brush.empty()) {
g.selectAll("#clip-" + id + " rect")
.attr("x", 0)
.attr("width", width);
} else {
var extent = brush.extent();
g.selectAll("#clip-" + id + " rect")
.attr("x", x(extent[0]))
.attr("width", x(extent[1]) - x(extent[0]));
}
}
In order to update charts you can remove it and then redraw with new filters.
Something like this :
d3.selectAll(".chart").selectAll("svg").remove();
Or
$('#chart'+chart_id+' svg').remove();
and then redraw it by calling again your drawing function with updated data.
Hope this will help you. Sorry If I misunderstood you. I need to train my english =P
EDIT :
I found these examples without remove. It might help you.
http://jsfiddle.net/sx9myywh/
https://bl.ocks.org/RandomEtc/cff3610e7dd47bef2d01
I found where the problem was. And i feel stupid.
The brush.onevent was placed incide the function which generate the chart and have to be outside.
function my(div){
}
brush.on(...
I have updated the JS Fiddle with the correct answer.
I will create a github repository it will be cleaner.

Select data for NVD3 chart

I'm a n00b and having trouble with NVD3, and there're some smart people on here, so I hope you can help.
I'm trying to create a drop box that will select data to display.
I can call a function with but I cannot get the chart to change its data location.
HTML:
<select name="slct" id="name" onchange="data(this.value)">
<option>Select power data</option>
<option value="Residence_supply_data">Average kwh residences supplied to the grid</option>
<option value="Residence_need_data">Average kwh supplied to residences</option>
</select>
NOTE: I've created JSON libraries with the values above as names.
Javascript:
function data(value) {
console.log(value);
var dat_select = value;
return dat_select;
};
var chart;
nv.addGraph(function() {
chart = nv.models.multiBarHorizontalChart().x(function(d) {
return d.label
}).y(function(d) {
return d.value
}).margin({
top : 30,
right : 105,
bottom : 30,
left : 100
})
//.showValues(true)
.tooltips(true).barColor(d3.scale.category20().range()).showControls(false);
chart.yAxis.tickFormat(d3.format(',.0f'));
d3.select('#chart1 svg').datum(dat_select).transition().duration(1000).call(chart);
nv.utils.windowResize(chart.update);
chart.dispatch.on('stateChange', function(e) {
nv.log('New State:', JSON.stringify(e));
});
return chart;
});
Everything works otherwise, and the function logs the results, just can't select the data I need to. If I set d3.select('#chart1 svg').datum(Residence_supply_data), it loads that data.
You have my gratitude.
At the point where d3.slect('#chart1 svg') assign it to a variable(global variable)
myChart = d3.select('#chart1 svg').datum(dat_select).transition()
.duration(1000).call(chart);
So now myChart contains the nvd3 multiBarHorizontalChart() chart.
So when you want the chart updated by calling a function updateMyChart() for example from a place of your choice you could update it like this :
function updateMyChart() {
// myChart is passed to your new data
// myChart also calls chart which hold the attributes you
// had set earlier for multiBarHorizontalChart
myChart.datum(Residence_supply_data).transition().duration(1000).call(chart);
// Update the chart during the window screen resizing
nv.utils.windowResize(myChart.update);
}
Hope this helps you.

nvd3 line chart issue

Here, I have used line chart from nvd3. I am trying to fix a few issues with this chart
1. This is an interactive chart, but even if i hover over a point where there are no points from the data, it still shows up the x and y-points.
2. x-axis and y-axis lines do NOT show up. Also the interactive guide doesn't come up in a rectangular box outlined.
3. The x-axis gets cut off at the end (see 14:29:19 being cut off at the end) no matter how much i increase the width to.
4. I am trying to remove the fill that we see above the line graph but styling changes made by me resulted in no success.
code that i use to fetch the line chart
var redraw = function(testData) {
var dataT = [{
key : 'Character Count',
values : testData,
color : '#00AEEF',
area : false
}];
var margin = {
top : 80,
right : 20,
bottom : 80,
left : 50
}, width = 1000 - (margin.left + margin.right), height = 600 - (margin.top + margin.bottom), x = d3.time.scale().range([0, width]), y = d3.scale.linear().range([height, 0]);
x.domain(d3.extent(dataT[0].values, function(d) {
return d3.time.format('%X')(new Date(d.TimeStamp));
}));
y.domain([0, d3.max(dataT[0].values, function(d) {
return d.CharCount;
})]);
nv.addGraph(function() {
chart = nv.models.lineChart().transitionDuration(350).useInteractiveGuideline(true)
.showLegend(true).showYAxis(true).showXAxis(true).x(function(d) {
return new Date(d.TimeStamp);
}).y(function(d) {
return d.CharCount;
});
chart.xAxis.axisLabel('Time').tickFormat(function(d) {
return d3.time.format('%X')(new Date(d));
}).scale(x).orient("bottom");
chart.yAxis.axisLabel('Character Count').tickFormat(d3.format(',')).scale(y);
chart.lines.forceY([0]);
d3.select('#chart2 svg')//.append("g")
.datum(dataT).attr('height', height).attr('width', width).call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
};
redraw(data)--data fetched froma service ith dateTimeStamp in x-axis and integer in y-axis points.
Please help!
This is an interactive chart, but even if i hover over a point where there are no points from the data, it still shows up the x and
y-points. 2. x-axis and y-axis lines do NOT show up. Also the
interactive guide doesn't come up in a rectangular box outlined.
Adding corresponding javascript file in nvd3 zip file should solve the problem(line.js, linechart.js and corresponding css file)
3.The x-axis gets cut off at the end (see 14:29:19 being cut off at the end) no matter how much i increase the width to.
This is because you have not set the width and height of the chart in your codes to generate the chart
you should do
chart.width(width).height(height)
4.I am trying to remove the fill that we see above the line graph but styling changes made by me resulted in no success
This I am not sure how to solve as I do not have your dataset, so I not able to reproduce the problem
Here is my test code

NVD3 Update Chart with d3.json

I've made a chart using NVD3, which populates 3 stacked bars. On clicking a bar, I want an request to be fired, and for the chart to be populated with new data altogether. Unfortunately, the chart does not REPLACE the data I previously had, but APPENDS a new bar to my chart, forming a chart with 4 stacked bars, when I only want 1 bar. How can I remove the old data, while bringing in new data?
Thanks!
var chartData = setChartData(data1);
var chart;
nv.addGraph(function() {
chart = nv.models.multiBarChart()
.barColor(d3.scale.category20().range())
.margin({bottom: 100})
;
chart.multibar
.hideable(true);
chart.reduceXTicks(false).staggerLabels(false);
chart.xAxis
.axisLabel("Current Index")
.showMaxMin(true)
.tickFormat(function(d,i){ return allBillsForChart[d][0][2]});
chart.yAxis
.tickFormat(d3.format(',.1f'));
d3.select('#chart1 svg')
.datum(chartData)
.call(chart);
nv.utils.windowResize(chart.update);
chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });
return chart;
},function(){
d3.selectAll(".nv-bar").on('click', function(d, i){
d3.json("fakeURLToFetchData", function(error, data){
arr = [];
arr.push(data)
handledData = setChartData(arr)
d3.select('#chart1 svg').datum(handledData).call(chart);
nv.utils.windowResize(chart.update);
return chart
})
});
});

NVD3 D3 Getting specific label on xaxis

I am using nvd3 library to generate grouped multi bar graph. This is my dataset (in JSON FORM)
[{"key":"Param 1","values":[{"y":2.0,"x":"2010-04-01"},{"y":0,"x":"2010-10-01"},{"y":0,"x":"2010-12-01"},{"y":0,"x":"2011-01-01"},{"y":0,"x":"2011-04-01"},{"y":0,"x":"2011-10-01"},{"y":0,"x":"2012-01-01"},{"y":0,"x":"2012-03-01"},{"y":0,"x":"2012-05-01"},{"y":0,"x":"2012-08-01"},{"y":0,"x":"2012-10-01"},{"y":0,"x":"2012-11-01"},{"y":25.0,"x":"2012-12-01"},{"y":0,"x":"2013-01-01"},{"y":0,"x":"2013-02-01"},{"y":0,"x":"2013-03-01"},{"y":0,"x":"2013-04-01"}]},{"key":"Param 2","values":[{"y":160.0,"x":"2010-04-01"},{"y":0,"x":"2010-10-01"},{"y":0.0,"x":"2010-12-01"},{"y":0,"x":"2011-01-01"},{"y":4.0,"x":"2011-04-01"},{"y":0,"x":"2011-10-01"},{"y":0.0,"x":"2012-01-01"},{"y":45.0,"x":"2012-03-01"},{"y":9.0,"x":"2012-05-01"},{"y":0,"x":"2012-08-01"},{"y":10.0,"x":"2012-10-01"},{"y":0,"x":"2012-11-01"},{"y":30.0,"x":"2012-12-01"},{"y":0.0,"x":"2013-01-01"},{"y":58.0,"x":"2013-02-01"},{"y":52.0,"x":"2013-03-01"},{"y":36.0,"x":"2013-04-01"}]},{"key":"Param 3","values":[{"y":80.0,"x":"2010-04-01"},{"y":12.0,"x":"2010-10-01"},{"y":0.0,"x":"2010-12-01"},{"y":0,"x":"2011-01-01"},{"y":2.0,"x":"2011-04-01"},{"y":0.0,"x":"2011-10-01"},{"y":0.0,"x":"2012-01-01"},{"y":33.0,"x":"2012-03-01"},{"y":16.0,"x":"2012-05-01"},{"y":20.0,"x":"2012-08-01"},{"y":0.0,"x":"2012-10-01"},{"y":150.0,"x":"2012-11-01"},{"y":65.0,"x":"2012-12-01"},{"y":0.0,"x":"2013-01-01"},{"y":44.0,"x":"2013-02-01"},{"y":116.0,"x":"2013-03-01"},{"y":24.0,"x":"2013-04-01"}]},{"key":"Param 4","values":[{"y":0,"x":"2010-04-01"},{"y":8.0,"x":"2010-10-01"},{"y":0,"x":"2010-12-01"},{"y":0,"x":"2011-01-01"},{"y":0,"x":"2011-04-01"},{"y":0.0,"x":"2011-10-01"},{"y":4.0,"x":"2012-01-01"},{"y":0,"x":"2012-03-01"},{"y":0,"x":"2012-05-01"},{"y":0,"x":"2012-08-01"},{"y":0,"x":"2012-10-01"},{"y":0,"x":"2012-11-01"},{"y":0.0,"x":"2012-12-01"},{"y":0,"x":"2013-01-01"},{"y":0,"x":"2013-02-01"},{"y":67.0,"x":"2013-03-01"},{"y":0,"x":"2013-04-01"}]},{"key":"Param 5","values":[{"y":0,"x":"2010-04-01"},{"y":0,"x":"2010-10-01"},{"y":0,"x":"2010-12-01"},{"y":15.0,"x":"2011-01-01"},{"y":0,"x":"2011-04-01"},{"y":0,"x":"2011-10-01"},{"y":0,"x":"2012-01-01"},{"y":0,"x":"2012-03-01"},{"y":0,"x":"2012-05-01"},{"y":0,"x":"2012-08-01"},{"y":0,"x":"2012-10-01"},{"y":0,"x":"2012-11-01"},{"y":0,"x":"2012-12-01"},{"y":0,"x":"2013-01-01"},{"y":250.0,"x":"2013-02-01"},{"y":120.0,"x":"2013-03-01"},{"y":100.0,"x":"2013-04-01"}]}]
And this is the code I am using:
var chart;
nv.addGraph(function() {
chart = nv.models.multiBarChart()
.barColor(d3.scale.category20().range());
chart.xAxis
.showMaxMin(true).tickFormat(function(d){return d3.time.format('%b-%y')(new Date(d));});
chart.yAxis
.tickFormat(d3.format(',1f'));
d3.select('#plot_container svg')
.datum(plot_data)
.transition().duration(500).call(chart);
nv.utils.windowResize(chart.update);
chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });
return chart;
The labels I am getting on the x-axis are gererated by the library itself. I want all the months on x-axis for which data is available. Also, all such months should be equally spaced, not linear in time.
I have tried stuff like tickValues to no avail.
How should this be done?

Categories