D3 Sankey Chord Colors - javascript

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.

Related

Map didn't render in 3d.js

I have a project that is about data visualization, however I am encountering problems. I need to render a map of a country (Brazil) using d3.js. When I move the mouse through the state it should appear the acronym of the state with the income per capita. In addition each state must be in a color tint (in the case I chose green) based on per capita income. I am sending my code because I am not getting my map to render the correct colors and are not showing the acronym and the income. If anyone can help, I appreciate it.
Here´s a link of the code
I see a couple problems with the code that builds each country's path -- try updating this section:
svg.append("g")
.selectAll("path")
.data(topojson.feature(br, br.objects.states).features)
.enter().append("path")
.attr("class", "states")
.attr("fill", function(d) { return color(valueByÚF.get(d.UF)); })
.attr("d", path)
.append("title")
.text(function(d) { return "UF: "+ d.UF; });
Honestly, I didn't check to see if the quantize scale is the right one to use for your data, so you may want to try other scales...

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.

How to align text vertically on a node within a Sankey diagram (D3.JS)?

I have modified Mike Bostok example of a Sankey diagram in D3.Js from http://bost.ocks.org/mike/sankey/ to display the value of each node as this:
Sankey http://uweb.cs.uvic.ca/~maleh2/sankey.png
node.append("text")
.attr("class","nodeValue");
///align vertically??
node.selectAll("text.nodeValue")
.attr("x", sankey.nodeWidth() / 2)
.attr("y", function (d) { return (d.dy / 2) })
.text(function (d) { return formatNumber(d.value); })
.attr("text-anchor", "middle");
//.attr("transform", "rotate(-90)")
All code from Mike's example and my modification is at jsfiddle
(http://jsfiddle.net/4xNK5/). My question is how can I display the value of the node vertically? I assumed a simple .attr("transform", "rotate(-90)"); would do it. Indeed, I get the value of the node aligned vertically BUT out of place. I cannot make sense of the logic behind it. Any ideas would be appreciated...
I am going to walk you through several experiments (all with accompanied jsfiddles) that will guide you to the solution of your problem.
Starting Point
(click for jsfiddle)
This is slightly modified example from your question:
It has been modified so that it contains data in JavaScript instead of in JSON file, and also it contains code from Sankey plugin. This is done just in order to have working example in JsFiddle. You can of course adjust it to suit your needs with respect to data etc...
The key code is here, as you already mentioned:
node.selectAll("text.nodeValue")
.attr("x", sankey.nodeWidth() / 2)
.attr("y", function (d) { return (d.dy / 2) })
.text(function (d) { return formatNumber(d.value); })
.attr("text-anchor", "middle");
Experiment 1
(click for jsfiddle)
I was really bothered that values and names of nodes are not aligned, so I added this code that slightly moves values downwards:
.attr("dy", 5)
The result is here:
Experiment 2
(click for jsfiddle)
Now lets try to add this code (I used 45 degrees on purpose to easier spot how rotation works):
.attr("transform", "rotate(-45)")
Surprisingly, we get this:
And if we examine the html/svg in firebug or similar tool, we'll notice that the reason for such rotation behavior is that value labels are actually part of other container: (and center of rotation is not center of label, but origin of that container)
Experiment 3
(click for jsfiddle)
In this experiment, I wanted to avoid using rotation at all. I wanted to use svg text property writing-mode, like this:
.style("writing-mode", "tb")
And I got this:
However, I couldn't get value labels to be oriented with its top to the left. Also, I heard this method has some Firefox compatibility problems, so I gave it up.
Solution
(click for jsfiddle)
After experiments above, my conclusion was that rotation should be done first, and that translation should be applied so that already rotated value labels are moved to the center of nodes. Since this translation is data-dependent, the code should look like this:
node.selectAll("text.nodeValue")
.text(function (d) { return formatNumber(d.value); })
.attr("text-anchor", "middle")
.attr("transform", function (d) {
return "rotate(-90) translate(" +
(-d.dy / 2) +
", " +
(sankey.nodeWidth() / 2 + 5) +
")";});
The result is:

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