Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3 sankey charts - manually position node along x axis

A problem I have come across using the d3 sankey implementation is that there's no way to specify where on the x axis a node is. I've been poking through the source and there isn't really a "clean" way to specify the x value on a reasonable scale (ie, 1-5 where the chart is 5 nodes wide). I am creating something that can be used like a course planner for education, so the x value will correspond with the semester. Supposing I had a course I couldn't take until my sophomore year of college, this would be at an x of 3 (1/2 are freshman, 3/4 sophomore, etc). The problem is, if there is nothing that links to this course beforehand, it will always be at an x of 1, so I would like to push it to the right two spaces.

I have noticed that the x value in the actual sankey chart does not reflect how many nodes across it is, so this is a bit difficult to do.

I've also come across this question, and I realize that by default the chart will not let me position a node. I have no problems tweaking the sankey.js example to accomplish this, but I'm stuck as to how to do so, currently.

like image 332
Seiyria Avatar asked Feb 03 '14 22:02

Seiyria


2 Answers

This is possible. See this JSFiddle.

enter image description here

The computeNodeBreadths function in sankey.js can be modified to look for an explicit x-position that has been assigned to a node (node.xPos):

  function computeNodeBreadths() {
    var remainingNodes = nodes,
        nextNodes,
        x = 0;

    while (remainingNodes.length) {
      nextNodes = [];
      remainingNodes.forEach(function(node) {

        if (node.xPos)
            node.x = node.xPos;
        else
            node.x = x;

        node.dx = nodeWidth;
        node.sourceLinks.forEach(function(link) {
          nextNodes.push(link.target);
        });
      });
      remainingNodes = nextNodes;
      ++x;
    }

    //
    moveSinksRight(x);
    scaleNodeBreadths((width - nodeWidth) / (x - 1));
  }

Then all you need to do is specify xPos on the desired nodes. In the above example I've set xPos = 1 on node2. See getData() in the JSFiddle example:

... }, {
        "node": 2,
        "name": "node2",
        "xPos": 1
    }, { ...
like image 54
Greg Ross Avatar answered Oct 12 '22 23:10

Greg Ross


In case anyone one will be looking for how-to fix the initial position of the deployed nodes (rects) you can do this:

sankey.layout = function(iterations) {
    computeNodeLinks();
    computeNodeValues();
    computeNodeBreadths();
    computeNodeDepths(iterations);
    computeAbsolutePositions(); // add this in sankey.js file
    computeLinkDepths();
    return sankey;
};

And add the positions themselves:

function computeAbsolutePositions() {
    nodes[0].x = 0;
    nodes[0].y = 0;
    nodes[1].x = 260;
    nodes[1].y = 0;
};

Please mind that you will have to yourself mind the positions of the rects when you hardcode them like that, as none collision check functions originally used by sankey.js are used in that example.

Hope that may help someone!

like image 29
Andrey Avatar answered Oct 12 '22 23:10

Andrey