Area fill for realtime graph - javascript

I have implemented a realtime graph with javascript and d3.js. The data is generated randomly and it changes based on the random number. I want to fill the area under the line chart but I do not know how to fill it since the data is moving! The following code are correct for static charts but how I can use it for dynamic moving data
//Css part
.area {
fill: lightsteelblue;
stroke-width: 0;
}
//script
var area = d3.svg.area()
.x(function(d, i) { return x(i); })
.y0(height)
.y1(function(d, i) { return y(d); });
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
And that is how my data is generated:
var n = 100,
random = d3.random.normal(0, 50),
data = d3.range(n).map(random);
Thanks,

In order to move the area in real time, you will have to do quite a bit of work. Fortunately Mike Bostock wrote a very good tutorial for path transitions with d3.js.
The key code is:
// push a new data point onto the back
data.push(random());
// redraw the line, and then slide it to the left
path
.attr("d", area)
.attr("transform", null)
.transition()
.ease("linear")
.attr("transform", "translate(" + x(-1) + ")");
// pop the old data point off the front
data.shift();
Also note that you will certainly have to use selections at one point, to do so you can have a look at the following tutorial: A Bar Chart, Part 2.
Add to that the example of area chart that you already use and you are nearly done! The only difference is that you write
Now, you can also get inspiration from the following question: Smooth update to x axis in a D3 line chart?
Finally, here is a jsFiddle that provides you a working example of what you are looking for.

Related

D3 pie chart transition

I'm studying transitions in D3js, trying to get them working with a simple pie chart that is derived from a Mike Bostock example.
I want to transition the data in the pie from data to data2, but the chart does not update.
This is the transition statement:
path.selectAll("path").data(pie(data2)).transition().duration(2000);
What am I missing?
EDIT
I've got it working with the below. Now I'd like to understand why this is working. I understand the .attr("d",arc) part, but why do I need to selectAll("path")?
path.selectAll("path").data(pie(data2)).transition().duration(2000).attr("d",arc);
END EDIT
The complete code (JSFiddle here):
var width = 200,
height = 200,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var data = [2, 19, 18, 99, 100];
var data2 = [100, 1200, 20, 88, 12];
var pie, arc, svg, path, data;
var chartCanvas = d3.select("#chart")
.append("svg")
.attr("width", width)
.attr("height", height);
var path = chartCanvas
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
function setupPieChart() {
pie = d3.layout.pie()
.value(function (d) {
return d;
})
.sort(null);
arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20)
path
.selectAll("path")
.data(pie(data))
.enter()
.append("path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc)
.each(function (d) {
this._current = d;
}); // store the initial angles
console.log(path);
}
setupPieChart();
console.log(path);
path.selectAll("path").data(pie(data2)).transition().duration(2000);
.transition starts a transition for the attributes change(s) declared after it. You don't set / do anything after .transition, so there is nothing to interpolate over. In the original example from Mike, you'll see he sets the d attribute after starting the transition, specifying a custom interpolator (arcTween)
I immediately see you are missing some important part of the update process. You copied the original code, but you forgot the update part :-). I can tell, because I see in your code you store the initial angles.
Look again at the code here and try to understand the function arcTween. More information can be found here. You need an arc tween function to calculate the new angles based on the initial angles (which is why you stored the initial angles in the first place :-).
I won't do the fiddle at the moment, cause in my experience, you learn more if you try to understand the arc tween function (as I did here . This is a link to a personal project of mine, but feel free to copy code as you see fit).
You need to .selectAll("path") as those are the actual elements that will update. When doing d3, try to think of the chart elements as following: Elements that are not visible yet (which is enter collection), elements that are visible now (which can be seen as the update collection) and elements that can be removed (the exit collection).
You need to see those elements based on the data you want to visualize. D3 is data driven documents, so everything is in relation to the data you want to show on the screen: if you have data but no elements yet, you do an "enter" of elements. So you do a selection of elements that are not in the DOM yet, but will soon be, because you will bind them to the data you have.
If you already have elements on the screen, and the number of elements matches the data you need to show (for example: var data = [20 ,30 , 40], you got 3 pieces of data here for 3 div's on the screen, you got a matching amount), then d3 will update your selection (hence the update collection or update selection) so the elements properties match the data.
If you have more elements on the screen then there is data to show, then you can do an exit of elements (again, hence the exit selection or collection). I hope that made it a bit more clear and that it made sense as well :-)

D3 Sankey Chord Colors

So I'm developing a Sankey diagram, using D3's Sankey API, and I'm trying to figure out how to change the color of the bands, or cords, going to and from the nodes. An example of what I'm trying to do can be found here:
http://tamc.github.io/Sankey/
I want to be able to individually choose each band and choose that individual band's color. I can't find any documentation for D3's Sankey API so I have no idea how to actually pull this off. I tried the setColors function that I found by searching through the code of the Sankey in the link that I provided. However, that doesn't seem to work with my code. I started my Sankey off using this code as a base:
http://tamc.github.io/Sankey/examples/simple.html
Can someone please give me an idea of how to change the color of a band using this as a reference?
P.S. If someone could also fill me in on how to change the color of a node, as well, that would be great!
The example you've linked to uses a different API on top of the Sankey plugin. I'll explain for this example. The Sankey plugin doesn't draw the visual elements, it only computes their positions, so you're free to set the colors as you like.
The relevant code for the links in the example is this:
var link = svg.append("g").selectAll(".link")
.data(energy.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
To change the color, simply set either a different class or set stroke explicitly:
.style("stroke", "red")
This can of course be a function as well so that you can set different colors for different paths. The nodes are similar:
node.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
In the example, the fill color is set based on the name -- you can adjust this as you like.

D3 Brush (multiple brushes)

I have several bars I'd like to draw and allow the user to use a brush to select a portion of a bar. The code is simple.
I have a Fiddle setup at:
http://jsfiddle.net/N32CS/
I'm not sure if it is my scale's that are wrong or the brushes themselves...
currentG.append("g")
.attr("id", "g_" + val.curNum)
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", yScale(arrayData[i].curNum))
.attr("height", 10);
It explains the problem I'm having of the user being able to at times drag outside the area of one bar or being limited to the area of another bar.
Thanks!
I updated your code to work as intended:
http://jsfiddle.net/N32CS/2/
var brushG = currentG.append("g")
.attr("id", "g_" + val.curNum)
.attr("class", "x brush");
var brush = d3.svg.brush();
brushG.datum({brush: brush});
...
brush.on("brushstart", function (p) {
d3.selectAll(".x.brush")
.filter(function(d) { console.log(d, d.brush != brush);return d.brush != brush; })
.each(function(d) { d3.select(this).call(d.brush.clear()) });
})
Basically I'm storing the brush function as data on each of the brush groups.
when you start brushing it clears the brushes for all the other bars and not it's own.
This is a pretty common thing, where it really helps to get used to binding data to the elements. If you bind stuff rather than keeping around global variables you can do everything with d3 selections and callbacks!

Controlling d3js pie animations and place labels inside pie

I'm a complete noop to D3 and partly SVG, so I got a few basic questions.
First off, my code in question can be viewed at http://dotnetcarpenter.github.io/d3-test/ and I've used Simple Pie Chart example with D3.js and Pie Chart Update, II as examples to get a running start.
As you can see, the animation gets skewed in the end when the low path values switch to the higher values. This is obviously not what I want. I think I'm getting the order of calculations wrong but I'm not sure what to do. I'm using the code from the last example:
function change() {
//...
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
// where arcTween is
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
Another issue is placing labels on the sectors. I've put the update stuff in the change function and is able to read out and only render them if the value is between 0 and 100. I can't however place them in any way. Looking at the first example, I figure that I could do something like this:
text.data(data)
.text(setText)
.attr("transform", function (d) {
// we have to make sure to set these before calling arc.centroid
d.innerRadius = 0;
d.outerRadius = radius;
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle") //center the text on it's origin
Where text is a d3 selection and arc is: d3.svg.arc().outerRadius(radius)
But I get "Unexpected value translate(NaN,NaN) parsing transform attribute." warning in Firefox and the labels are written on top of each other.
I appreciate any help and hints. Thanks!
I finally figured it out.
Maintain sector order throughout an animation.
You'd be forgiven for thinking that object contancy had something do with it. I did. But it turns out to be much simpler than that.
Every pie chart is by default sorted by value. If you don't want to sort by value but e.g. by data list order, you just have to disable sorting.
var pie = d3.layout.pie() // get a pie object structure
.value(function(d) { // define how to get your data value
return d.value; // (based on your data set)
})
.sort(null); // disable sort-by-value
Positioning labels according to your chart
Basically, you need to calculate your label positions depending on the type of chart or graph, your trying to connect them to. In my case, it's a pie chart. So if I want d3 to help with the calculations, I need to tell centroid the inner and outer radius and, most importantly to my issue, the start and end angles. The latter was missing from my code. Getting these values is as simple as, calling our pie layout above with our dataset and then do a transform.
Note that you don't have to call .data() again if you created the SVG with d3 and already supplied your data wrapped in .pie() structure. That is, that you didn't select any existing SVG from your page.
var svg = d3.select("svg")
// do stuff with your svg
var pie = d3.layout.pie()
// set stuff on your layout
var text = svg.selectAll("text")
.data(pie(dataset)) // where dataset contains your data
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
});
I have to give credit to Philip Pedruco for helping me along the way.
Bonus info
Use viewBox if you want to position your SVG cross browser, not transform/translate.
// move pie to center
d3.select("svg").attr("viewBox", -radius + ","+ -radius +"," + size + "," + size)

Real time line graph with nvd3.js

I am trying to create a real time graph using nvd3.js which would be updated periodically and with the impression that the data is processed in real time.
For now I have been able to create a function which would update the graph periodically but I cannot manage to have a smooth transition between "states" like the line doing a transition to the left.
Here is what I did using nvd3.js , here the interesting code is:
d3.select('#chart svg')
.datum(data)
.transition().duration(duration)
.call(chart);
Now, I have been able to produce what I want using d3.js but I would prefer to be able to use all the tools provided by nvd3.js. Here is what I would like to produce using nvd3
The interesting code for the transition using d3.js is:
function tick() {
// update the domains
now = new Date();
x.domain([now - (n - 2) * duration, now - duration]);
y.domain([0, d3.max(data)]);
// push the accumulated count onto the back, and reset the count
data.push(Math.random()*10);
count = 0;
// redraw the line
svg.select(".line")
.attr("d", line)
.attr("transform", null);
// slide the x-axis left
axis.transition()
.duration(duration)
.ease("linear")
.call(x.axis);
// slide the line left
path.transition()
.duration(duration)
.ease("linear")
.attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
.each("end", tick);
// pop the old data point off the front
data.shift();
}
You probably want to look at:
D3 Real-Time streamgraph (Graph Data Visualization),
especially the link of the answer:
http://bost.ocks.org/mike/path/
In general, I see two ways to deal with the vertical transition problem:
oversampling having some linear interpolation between the real point, and the smaller the interval between points, the more "horizontal" the vertical transition will look (but the drawback is that you get a lot of "false" points, that may be inacceptable depending on the use of the chart)
extend the chart by adding at the end, and translate the chart on the left, making sure the still available left part is not shown until you remove it (clipping it or doing any other trick), that's best, and the solution mentioned above does that

Categories