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.

Answers:

Answer

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
    }, { ...
Answer

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!

Answer

Let us call the x-coordinate layer. layer=0 will be left edge, and layer=n will be the right edge. In your JSON file, in the nodes field, add the key-value pair layer: your_desired_x_as_integer.

Then go the sankey.js file and find the function componentsByBreadth.forEach. Replace the line node.x = component.x + node.x; with:

if (node.layer) node.x=node.layer;
else node.x = component.x + node.x;

You can defined density of layers like this also, e.g. nodes placed on layers 0,1,2 or 0,4,8 will have a central node and two on the edges of the width of the sankey, but 0,1,5 will not have.

If you need more help, this feature, among many other is included in my D3 Sankey app: http://sankey.csaladen.es

Answer

might not be the clean answer you're after, but you can explicitly change the source and target x attributes, then update the links

http://bl.ocks.org/danharr/af796d91926d254dfe99

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.