I am looking for making the link color same as node fill color in d3 sankey charts.
I am using following function to get the node color
function getNodeColor(d){
console.log(d3.rgb(d.color).darker(2));
return d3.rgb(d.color).darker(2);
}
Following is my code for painting the text color of the links
// add in the title for the nodes
node.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function(d) { return d.name; })
.filter(function(d) { return d.x < width / 2; })
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start")
.style("stroke", getNodeColor);
Above works perfectly fine and the text displays the node text with the node color.
Same is not true when I try to change the stroke value in link definition by same way :
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.style("stroke", getNodeColor)
.sort(function(a, b) { return b.dy - a.dy; });
I am new to d3 sankey and follow http://bl.ocks.org/d3noob/c9b90689c1438f57d649
Help appreciated...
Bottomline : I am looking for making the link/chord colors of sankey chart same as that of rect fill color...
You can use the following code to use the source node color for the links in your graph. You could also use target.color if you prefer to use the target node color.
svg.selectAll(".link")
.style('stroke', function(d){
return d.source.color;
})
This code would need to be placed after the code which sets the node colors.
Related
I was trying to add labels to circles that are constantly moving in D3. One of the ways I found was to insert the label and the circle to a g tag. However, this causes errors whenever I want to EXIT the elements and merge them (which is not happening right now).
Is there any other way to do it? My graph right now seems to be with lower frames per second because it is not using the merge to update from the current x/y position to the new one.
The code is as follow:
var t = d3.transition()
.duration(0);
// JOIN new data with old elements.
var circles = g.append("g").selectAll('g')
.data(data.values)
.enter()
.append('g')
// EXIT elements
d3.selectAll("circle").transition()
.attr("class", "exit")
.remove();
// EXIT elements
d3.selectAll(".label")
.attr("class", "exit")
.remove();
// ENTER new elements present in new data.
// Circles
circles.append("circle")
.attr("class", "enter")
.attr("fill", function (d) { return color(d.country); })
.attr("cy", function (d) { return y(d.active_cases); })
.attr("cx", function (d) { return x(d.total_deaths) })
.merge(circles)
.transition(t)
.attr("r", 15)
// Labels
circles.append("text")
.attr("class", "label")
.attr("font-size", 10)
.style("text-anchor", "middle")
.style("fill", "white")
.attr("x", function (d) { return x(d.total_deaths) })
.attr("y", function (d) { return 3 + y(d.active_cases) })
.merge(circles)
.text(function (d) { return d.country })
I'm working on a modified version of this D3 code for parallel coordinates. I would like to add a background color for the axis labels. I realize I cannot set a background color using CSS styles because this is SVG text, so I tried to append rectangles but to no avail. Here is what I wrote but it's not drawing anything:
d3.selectAll(".label")
.append("rect")
.attr("x", function(d){ return d3.select(this).node().getBBox().x - 5;})
.attr("y", function(d){ return d3.select(this).node().getBBox().y - 5;})
.attr("width", function(d){ return d3.select(this).node().getBBox().width;})
.attr("height", function(d) {return d3.select(this).node().getBBox().height;})
.style("stroke", "black")
.style("fill", "yellow");
What am I doing wrong?
EDIT:
based on comments, I appended to the parent g rather than the text label itself. Now the code looks like this:
// Add an axis and title.
var gsvg= g.append("svg:g")
.attr("class", "axis")
.attr("id", "gsvg")
.each(function(d) { d3.select(this).call(axis.scale(yscale[d])); })
.attr("transform", "translate(0,0)");
d3.selectAll("#gsvg")
.append("rect")
.attr("x", function(d){ return this.parentNode.getBBox().x - 5;})
.attr("y", function(d, i){ return i%2 === 0 ? this.parentNode.getBBox().y - 20: this.parentNode.getBBox().y - 35;})
.attr("width", function(d){ return this.parentNode.getBBox().width;})
.attr("height", function(d) {return 20;})
.style("stroke", "lightgrey")
.style("stroke-width", 2)
.style("fill", function(d){
return self.fcolorMap[d];
})
.style("opacity", 0.5);
gsvg.append("svg:text")
.style("text-anchor", "middle")
.attr("y", function(d,i) { return i%2 == 0 ? -14 : -30 } )
.attr("x",0)
.attr("class", "axis-label")
.text(function(d) {
var s = d.split("|");
return s[s.length-1]; });
The only problem is now I need to figure out how to get the bounding boxes of the labels rather than those of the axes.
I would like to make the text in the D3.js nodes submit a GET request upon click.
The jsfiddle for the code can be found here: https://jsfiddle.net/ft5107wo/2/
I would like the GET request to be on the id key in node's JSON.
I have attempted this solution, but it did not work.
The relevant code for creating the rectangles and appending node text is from lines 191 to 218, and is as follows:
// Create the node rectangles.
nodes.append("rect")
.attr("class", "node")
.attr("height", 40)
.attr("width", 40)
.attr("id", function (d) {
return d.id;
})
.attr("display", function (d) {
if (d.hidden) {
return "none"
} else {
return ""
};
})
.attr("x", kx)
.attr("y", ky);
// Create the node text label.
nodes.append("text")
.text(function (d) {
return d.name;
}).attr("transform", "translate(0," + 5 + ")")
.style("text-anchor", "middle")
.attr("x", tx)
.attr("y", ty)
.attr("dy", 0)
.call(wrap, 40);
How can this be achieved?
It turns out one needs to use this hack to have it work correctly and avoid problems with namespaces.
I am new to this kind of chord visualization. I am working on a sample http://bl.ocks.org/mbostock/4062006:
I would like to add "black", "blonde","brown","red" as labels in the respective chord. How is this possible.
I tried something like this but is not working:
var textLabel = d3.scale.ordinal().range(['Black','Blonde','Brown','Red']);
svg.append("g").selectAll("path")
.data(chord.groups)
.enter()
.append("path")
.style("fill", function(d) { return fill(d.index); })
.style("stroke", function(d) { return fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
svg.append("g").append("svg:text")
.attr("x", 6)
.attr("dy", 15)
.append("svg:textPath")
.text(function(d,i) { return textLabel(i+1); })
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
You have forgotten to use the enter selection and also do not link the svg:textPath to an actual path in the SGV. To do this the path you want the text to follow needs to have been given an id attribute and you need to reference this in the textPath with an href attribute.
So you want to add the id attribute to the path you want the text to follow
.attr("id", function(d, i){return "group-" + i;})
You then want to do something like this to add textPath elements
svg.append("g").selectAll("text")
.data(chord.groups)
.enter()
.append("sgv:text")
.attr("x", 6)
.attr("dy", 15)
.append("svg:textPath")
.attr("xlink:href", function(d, i){return "#group-" + i;})
.text(function(d,i) {return textLabel(i+1);});
Finally with the colour black you will probably want to invert the text so you don't end up with black on black text.
.filter(function(d, i){return i === 0 ? true : false;})
.attr("style", "fill:white;");
I am trying to add labels to the bilevel sunburst / partition shown here - http://bl.ocks.org/mbostock/5944371:
I have added labels to the first 2 levels and rotated them correctly. But I cant now add the new level's labels during a transition. To get started I have put ...
text = text.data(partition.nodes(root).slice(1), function(d) { return d.key; });
straight after the ...
path = path.data(partition.nodes(root).slice(1), function(d) { return d.key; });
line but it throws the following error ...
Uncaught TypeError: Cannot read property '__data__' of undefined
What am I doing wrong?
I used bilevel partition to add labels via a mouseover, and found that in this version (unlike the other sunburst partition), there are two different sections where "path" is defined and updated. The first is here:
var path = svg.selectAll("path")
and then again below the transition you highlighted in your code:
path.enter().append("path")
When i added my mouseover labels, I needed to reference my text function in both places or it wouldn't work after transition.
If you can post your code to JSFiddle or bl.ocks.org we can try to play with it and see, but that was where I got tripped up at first.
Hope that helps.
*NOTE: Comment didn't post:
I'm sorry I'm not able to help more, but I'm also a newbie at D3. Here's where I got:
copy and paste your svg.selectAll("text") snippet after the second "path.enter().append("path") snippet. This will cause it to appear on subsequent zooms as well.
Problems I see: there's no "g" element so you need separate transitions for text as well or they all pile up. Also, I can't understand why they're positioned at their original partition spot instead of where the arc exists now.
My approach was add labels on mouseOver. I've uploaded that here: https://github.com/johnnymcnugget/d3-bilevelLabels
In this, I'm calling the two functions in both sets of "path" snippets, and the transitions are handled within the single variable of "legend"
Another D3 newbie, but I managed to resolve this. Beggining from the original Bilevel Partition:
Add the text for the first time the chart is drawn:
var path = svg.selectAll("path")
.data(partition.nodes(root).slice(1))
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn);
var text = svg.selectAll("text")
.data(partition.nodes(root).slice(1))
.enter().append("text")
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) {return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.html(function(d) { return d.name; });
when zooming in or out you have to redraw labels, add the following in function zoom(root, p)
text.enter().append("text")
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) {return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) { return d.name; });
text.exit().transition()
.style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
.remove();
text.transition()
.style("fill-opacity", 1)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) {return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
Finally modify the computeTextRotation function as:
function computeTextRotation(d) {
return (d.x + (d.dx)/2) * 180 / Math.PI - 90;
}
Hope I haven't missed anything.
Indeed one modified line is missing in "2.". In function zoom(root, p), you should also add a line after path = path.data(partition ... .key; });:
path = path.data(partition.nodes(root).slice(1), function (d) {
return d.key;
});
text = text.data(partition.nodes(root).slice(1), function (d) {
return d.key;
});