D3js button onclick function to zoom the graph for specific timeframe - javascript

Expectation
the initial graph showed the full data of csv and the expected output is when i clicked on the button below the x-axis, let's say i click on the '1 hour' button, then the graph will rescale according to data for the first 1 hour, same goes for other buttons.
Problem
The current problem is when i clicked on the button, the graph will rescale to middle position(random data) instead of specific position( specific data, exp( first 1 hour, 3 hours, and etc) that stated on the button. Here is the code for me click on the button to zoom the graph to specific timeframe.
function transition(zoomLevel) {
svg.transition()
.call(zoom.scaleTo, zoomLevel);
}
function timezoom(){
x.domain([new Date(2017,1,11),new Date(2017,1,11)]).range([0,60]);
y.domain([95000,d3.max(data, function(d) { return d.AGV_Mileage; })]);
x2.domain(x.domain());
y2.domain(y.domain());
var svg = d3.select("body").transition();
//
}
<input type="button" id='1hr' onclick="timezoom()" value="1 Hour" />
<input type="button" id='3hr' onclick="transition(1.8)" value="3 Hours"/>
The zoom's behavior is demonstrated in the plunker editor - https://plnkr.co/edit/MY1nP4Mnv4WeCpHCxLvD?p=preview
Please enlighten me and give me a rough idea on this.

You have to specify the translate as well the scale on the transform call.
function transition(newRange) {
var rgh = d3.timeHour.count(initialDomain[0], initialDomain[1]);
var k = rgh/newRange;
var t = d3.zoomIdentity.translate(0,0).scale(k);
svg.transition()
.call(zoom.transform, t);
}
You should also save the initial domain in order to calculate the new scale:
initialDomain = d3.extent(data, function(d) { return d.Timestamp; });
Here is the updated plnkr

Related

How to make the circles disappear based on keyboard input?

based on this example: http://bl.ocks.org/d3noob/10633704 i wish to make an input on my keyboard (a number) and make the circles disappear with the help of array.slice(). Unfortunally it did not worked well. In my code, i created some circles based on the values of the array days. With the HTML part i am able to create a button, where i can make a number input. With the last part days.slice(nValue) i want that the input number is the same like the number inside the brackets of the slice() function, so the array days is getting shorter and automatically let circles based on the value of the array disappear. But unfortunally there is a mistake i made in this code. Can someone maybe be so kind and help? I am using D3 to solve this problem.
Thanks
<!DOCTYPE html>
<meta charset="utf-8">
<title>Input (number) test</title>
<p>
<label for="nValue"
style="display: inline-block; width: 120px; text-align: right">
angle = <span id="nValue-value"></span>
</label>
<input type="number" min="0" max="360" step="4" value="0" id="nValue">
</p>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var width = 600;
var height = 300;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var days = [7, 12, 20, 31, 40, 50];
console.log(days);
var circle = svg.selectAll("circle")
.data(days)
.enter().append("circle")
.attr("cy", 60)
.attr("cx", function(d, i) { return i * 100 + 40; })
.attr("r", function(d) { return Math.sqrt(d); });
d3.select("#nValue").on("input", function() {
update(+this.value);
});
// Initial update value
update(0);
function update(nValue) {
days.slice(nValue);
}
It took me a while to see what you're after here, and I might still be off a bit in my understanding.
The Problem
As I see understand it, you are modifying an array of data (with a select menu in this case), but the modified array does not appear to modify your visualization. Essentially, as "the array days is getting shorter ... let circles based on the value[s] of the array disappear."
Updating the visualization
To update the visualization you need to bind the new data to your selection. After this you can remove unneeded elements in the visualization, add new ones (not relevant to this question), or modify existing elements. Changing the data array by itself will not update the visualization. To have the visualization utilize the new information you need to bind that data to the selection:
circle.data(data);
Then you can remove the old items:
circle.exit().remove();
Then you can modify properties of the old items:
circle.attr('cx',function(d,i) {...
Your update function needs to at least update the data and remove unneeded elements.
Changing the Array
In the following snippet I append both a select menu and the circles with d3 based on the data in the array. Selecting an item in the menu will remove a circle:
var data = [10,20,30,40,50,60,70,80,90,100];
var color = d3.schemeCategory10; // color array built in
//// Add the select and options:
var select = d3.select('body')
.append('select')
.on('change',function() { update(this.value) });
var start = select.append('option')
.html("select: ");
var options = select.selectAll('.option')
.data(data)
.enter()
.append('option')
.attr('class','option')
.attr('value',function(d,i) { return i; })
.html(function(d) { return d; });
//// Add the circles (and svg)
var svg = d3.selectAll('body')
.append('svg')
.attr('width',500)
.attr('height',200);
var circles = svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx',function(d,i) { return i * 30 + 50; })
.attr('cy',50)
.attr('r',10)
.attr('fill',function(d,i) { return color[i]; });
// Update everything:
function update(i) {
data.splice(i,1); // remove that element.
// Update and remove option from the select menu:
options.data(data).exit().remove();
// Remove that circle:
circles.data(data).exit().remove();
circles.attr('cx',function(d,i) { return i * 30 + 50; })
.attr('fill',function(d,i) { return color[i]; });
// reset the select menu:
start.property('selected','selected');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
There is a problem here, only the last circle and menu item is removed each time. Why? Imagine a four element array, if you remove the second item, d3 does not know that you removed the second item, you might have modified elements two and three and removed element four.
Since all your items are appended with their increment (which position they are in the array), and this doesn't account for holes that were created when other items were removed, you need to change the approach a little.
A solution
Instead of relying on the increment of an item in the array (as this will change every time an element that is before another element is removed from the array), you could use an id property in your data.
This would require restructuring you data a little. Something like:
var data = [ {id:1,value:1},{id2....
As the id property won't change, this makes a better property to set attributes. Take a look at the following snippet:
var data = [{id:0,value:10},{id:1,value:20},{id:2,value:23},{id:3,value:40},{id:4,value:50},{id:5,value:60},{id:6,value:70},{id:7,value:77},{id:8,value:86},{id:9,value:90}];
var color = d3.schemeCategory10; // color array built in
//// Add the select and options:
var select = d3.select('body')
.append('select')
.on('change',function() { update(this.value); } ); // add an event listener for changes
// append a default value:
var start = select.append('option')
.html("Select:");
var options = select.selectAll('.option')
.data(data)
.enter()
.append('option')
.attr('class','option')
.attr('value',function(d,i) { return i; })
.html(function(d) { return d.value; });
//// Add the circles (and svg)
var svg = d3.selectAll('body')
.append('svg')
.attr('width',500)
.attr('height',200);
var circles = svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx',function(d) { return d.id * 30 + 50; })
.attr('cy',50)
.attr('r',10)
.attr('fill',function(d) { return color[d.id]; });
// Update everything:
function update(i) {
data.splice(i,1); // remove the element selected
// Update and remove option from the select menu:
options.data(data).exit().remove();
// Remove that circle:
circles.data(data).exit().remove();
// update the options (make sure each option has the correct attributes
options.attr('value',function(d,i) { return i; })
.html(function(d) { return d.value; })
// Make sure circles are in the right place and have the right color:
circles.attr('cx',function(d) { return d.id * 30 + 50; })
.attr('fill',function(d) { return color[d.id]; });
// reset the default value so the change will work on all entries:
start.property('selected', 'selected');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
Try changing your update function to this:
function update(nValue) {
days = days.slice(nValue);
}

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.

Strange functionality of NVD3 Graph when updating data

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.

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

How to change the viewfinder (focus chart) of a NVD3 line chart programmatically?

The example I'm talking about is here: http://nvd3.org/ghpages/lineWithFocus.html
What I would like to do is have preset ranges to programmatically change the visible range of the view finder. For example, I could have a buttons to only show the last 30-days of data or show ALL data. When the user clicks on either of the buttons, the viewfinder would change to reflect only data in the selected range.
Does anyone have any suggestions on how to go about doing this?
Thanks,
CZ
You can actually do this - took a little bit of digging but here's how:
nv.addGraph(function() {
window.chart = nv.models.lineWithFocusChart()
// Do whatever you need to do to set up the chart, and keep a reference to it
});
$("#preset-range").on("click", function() {
// Get the min and max
min = $(this).data("min")
max = $(this).data("max")
// Change the focus chart range programatically
chart.brushExtent([min, max]).update()
});
A suggestion for hide/unhide only, based on http://www.mkyong.com/jquery/jquery-toggle-example-to-display-and-hide-content/:
<button type="button" id="nv-toggle">Show View Finder</button>
<script>
$(document).ready(function() {
$('#nv-toggle').click(function() {
// make the collapse content to be shown or hide
var toggle_switch = $(this);
$('.nv-context').toggle(function() {
if($(this).css('display')=='none') {
toggle_switch.html('Show View Finder');
} else {
toggle_switch.html('Hide View Finder');
}
});
});
});
</script>
It's a bit tricky finding out the solution but once you get to know it, it's pretty straight forward.
You need simply update the chart calling d3.select('#chart svg').datum(sendyouNewData)
I have used the same code as in the NVD3 site the only additional code I added was the chart update function on a button click, oh and the added a width and height to the chart.
The following code is a working tested code. The live code is here
Your HTML:
<input type="button" id="change1" value="Change 1"/>
<input type="button" id="change2" value="Change 2"/>
<div id="chart">
<svg></svg>
</div>
Your JavaScript
var dynamic_lineWithFocusChart, lineWithFocusChart;
var width = 500,
height = 500;
nv.addGraph(function () {
var chart = nv.models.lineWithFocusChart().width(width).height(height);
chart.xAxis.tickFormat(d3.format(',f'));
chart.yAxis.tickFormat(d3.format(',.2f'));
chart.y2Axis.tickFormat(d3.format(',.2f'));
dynamic_lineWithFocusChart = d3.select('#chart svg').datum(testData());
dynamic_lineWithFocusChart.transition().duration(1000).call(chart).attr('width', width).attr('height', height);
nv.utils.windowResize(chart.update);
lineWithFocusChart = chart;
return chart;
});
/*
* Simple test data generator
*/
function testData() {
return stream_layers(3, 128, .1).map(function (data, i) {
return {
key: 'Stream' + i,
values: data
};
});
}
/*
* Update the Line Focus chart with the Button Click
*/
$("#change1,#change2 ").click(function () {
dynamic_lineWithFocusChart.datum(testData());
dynamic_lineWithFocusChart.transition().duration(1000).call(lineWithFocusChart);
dynamic_lineWithFocusChart.update
});

Categories