Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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...

like image 209
mfroese Avatar asked Dec 09 '22 08:12

mfroese


1 Answers

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:

enter image description here

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:

enter image description 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:

enter image description here

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)

enter image description here


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:

enter image description here

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:

enter image description here

like image 175
VividD Avatar answered Dec 11 '22 11:12

VividD