d3.js Sankey diagram: rectangles fill color
-
13-07-2021 - |
Question
So I am playing around with the d3.js Sankey diagram.
In this example (pictured above) color is defined using
var color = d3.scale.category20();
For each node there is a rectangle, and that rectangle is filled by altering the style:
.style("fill", function(d) {
return d.color = color(d.name.replace(/ .*/, ""));
})
I'm looking for suggestions on using custom colors. If I wanted to use only say 6 colors, but have the node rectangle colors chosen based on a value in the .json file.
For example, lets say I wanted to show a snakey chart of teams in the NFL. The colours each represent which division the teams play in. So if they move to a different division, the color changes. And the nodes are created for every season. Something along those lines.
So is it possible to run the
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);
})
.append("title")
.text(function(d) { return d.name + "\n" + format(d.value); });
with the color based on a value in the json file? I am thinking just an if statement, but is there an easier way? Could I just include the hex color code in the json?
Solution
Sounds like you want to include the colour in the JSON in this case. You can include it in any way that the browser recognises, e.g. as a name ("white") or hex ("#fff"). See the SVG spec for a full list of supported colour specifications.
OTHER TIPS
Alternatively, you could map the colors to the division explicitly with a d3 ordinal scale as mentioned in the documentation. See Colorbrewer at the bottom.
var color = d3.scale.ordinal()
.domain(["foo", "bar", "baz"])
.range(["#fff","#000","#333"]);
and then
.attr("fill", function(d) { return color(d.division); });
Replace const color = d3.scaleOrdinal(d3.schemeCategory20);
with:
const color = d3.scaleOrdinal()
.domain(["Crude oil","Natural gas",...])
.range(["#FD3E35","#FFCB06",...]);
And stay with:
.style('fill', (d,i) => {
d.color = color(d.name.replace(/ .*/, ''));
return d.color;})