D3 Brush (multiple brushes) - javascript

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!

Related

Is there a way to add different shapes depending on different data to my force-directed graph?

I'm sure this has been covered in another question on here however, after trying many different examples on my code i cannot seem to get this to work.
I am trying to make the nodes on my force-directed graph a different shape depending on the name of the node, for example, if the node is named 'Switch' it should be displayed as a square.
I have worked out the set up for changing the colour of the nodes based on the data and would like a similar thing for the shape but can't get it to work using d3.v4.
Any help?
var color = d3.scaleOrdinal(d3.schemeCategory10);
var shape = d3.symbolTypes;
var node = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 15)
.attr("fill", function(d) { return color(d.group); })
.attr("d", d3.symbol()
.type(function (d) { return shape(d.name);}))
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
Many Thanks
Faye
You can't use:
var shape = d3.symbolTypes;
....
shape(d.name);
since d3.symbolTypes is not defined in d3 v4 (it was in some versions of v3), in v4 use d3.symbols which is an array not a function.
But, as with color, you could create an ordinal scale for shape:
var color = d3.scaleOrdinal(d3.schemeCategory20);
var shape = d3.scaleOrdinal(d3.symbols);
Now all you have to do is append that shape:
.append('path')
.attr("d", d3.symbol().type( function(d) { return shape(d[property]);} ) );
Since you were appending circles, and are now appending paths, you'll need to change .append('circle'), and as circles have cx cy elements, you need to change to a transform where you set their position.
Here is a bl.ock which should show this in practice, based on MBostock's force directed graph (here)
Keep in mind there are only seven shapes in the d3.symbols array.
Edit:
If you want to specify which shapes get displayed for each node based on a property (rather than letting the ordinal scale set the shape), you could add a property to your data which contains the name of a shape (eg: d3.symbolCross), or create a function which takes in a data value and outputs the name of a symbol. But an ordinal scale is easiest.

How can I create overlapping, transparent bars in a D3 bar chart?

I want to create a chart like this example:
Population pyramid
But without the sliding; no need to adjust year or anything. This is roughly the dataset I have:
category,subcategory,benchmark,completes,difference
household income,"Less than $30,000",33.7,27.4,6.3
household income,"$30,000 to $74,000",31.6,36.3,4.7
household income,"$75,000 to $124,999",20.3,22.4,2.1
household income,"$125,000 Plus",14.2,13.9,0.3
I want to have a transparent bar for the benchmark, and a transparent bar for the completes (so that the disparity between them can be seen)
Where I have problems is putting two rects in each 'g' container
// G containers for subcategory bars
subcat = svg.selectAll('.subcat')
.data(data)
.enter().append('g')
.attr('class','subcat')
.attr('transform', function(d) {
return 'translate(' + x(d.subcategory) + ', 0)';
});
subcat.selectAll('.bar')
.data(data)
.enter().append('rect')
.attr('class','bar')
.attr('width', x.rangeBand())
.attr('y', function(d) { return y(d.benchmark); })
.attr('height', function(d) { return height - y(d.benchmark); });
I don't have a very good understanding of the data() method of a selection, hence my problem. I have read about keys, entries and rollups and I think that might be a possible place to look for a solution.
Most of the D3 examples I have worked with use one piece of data for each column, so the data binding makes sense. In this case, I want to put two bars in each column so the data binding process is unclear to me.
Any help would be appreciated.

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.js Dynamic connector between objects

I'm very new to both JS and D3, and I've googled this a tonne but only found examples that are a bit too advanced.
I'm making a simple decision graph implementation, and I'm stuck trying to connect 2 nodes with a line / path. The objects can be moved around with the mouse, and the path should always update to reflect the positions of the objects.
This is my base source of knowledge: https://github.com/mbostock/d3/wiki/SVG-Shapes, but I don't quite understand how to do something smart with it.
Here is what I have so far: http://jsbin.com/AXEFERo/5/edit
Don't need the fancy stuff, just need to understand how to create connectors and have them update dynamically when the objects are being dragged around. Big thanks!
To draw a line between the circles, you don't need anything special -- just the line element.
var line = svg.append("line")
.style("stroke", "black")
.attr("x1", 150)
.attr("y1", 100)
.attr("x2", 250)
.attr("y2", 300);
Updating the position dynamically is a bit more difficult. At the moment, you have no means of distinguishing which of the circles is being dragged. One way of doing this is to add a distinguishing class to the g elements.
var g1 = svg.append("g")
.attr("transform", "translate(" + 150 + "," + 100 + ")")
.attr("class", "first")
...
and similarly for the other one. Now you can switch on the class in your dragmove function and update either the start or the end coordinates of the line.
if(d3.select(this).attr("class") == "first") {
line.attr("x1", x);
line.attr("y1", y);
} else {
line.attr("x2", x);
line.attr("y2", y);
}
Complete example here. There are other, more elegant ways of achieving this. In a real application, you would have data bound to the elements and could use that to distinguish between the different circles.

Area fill for realtime graph

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.

Categories