block by renecnielsen c547fe16dd41854bd9f3

Multi-Part Sankey

Full Screen

hello world

forked from emeeks‘s block: Multi-Part Sankey

index.html

<html xmlns="//www.w3.org/1999/xhtml">
<head>
  <title>Mutli-Part Sankey</title>
  <meta charset="utf-8" />
</head>
<style>

  body, html {
    margin: 0;
    padding: 0;
  }
  .viz {
    position: fixed;
    left:0;
    top:0;
    bottom:0;
    right:0;
    background:rgb(188,188,188);
  }

  path.domain {
    fill: none;
  }

</style>
<script>

expData = "";
nodes = [];
edges = [];
nodeHash = {};
edgeHash = {};

function makeVizSankey() {
  d3.select(".viz")
  .append("svg")
  .attr("height", 1000)
  .attr("width", 1000)
  .style("background", "#FDFBF5")

  d3.csv("energy.csv", createData);

}

function createData(data) {

  //Build the network
  data.forEach(function (datum) {
    if (!nodeHash[datum.source]) {
      nodeHash[datum.source] = {id: datum.source};
      nodes.push(nodeHash[datum.source]);
    }
    if (!nodeHash[datum.target]) {
      nodeHash[datum.target] = {id: datum.target};
      nodes.push(nodeHash[datum.target]);
    }

    // Each link will have a segments array made up of the 5-year values
    var newEdge = {source: nodeHash[datum.source], target: nodeHash[datum.target]};
    edgeHash[datum.source+datum.target] = newEdge;
    newEdge.weight = parseFloat(datum.total);
    newEdge.value = parseFloat(datum.total);
    newEdge.segments = [];
    newEdge.segments.push(parseFloat(datum["2010"]));
    newEdge.segments.push(parseFloat(datum["2015"]));
    newEdge.segments.push(parseFloat(datum["2020"]));
    newEdge.segments.push(parseFloat(datum["2025"]));
    newEdge.segments.push(parseFloat(datum["2030"]));
    newEdge.segments.push(parseFloat(datum["2035"]));
    newEdge.segments.push(parseFloat(datum["2040"]));
    newEdge.segments.push(parseFloat(datum["2045"]));
    newEdge.segments.push(parseFloat(datum["2050"]));

    edges.push((newEdge));
  })

  buildSankey();
}

function buildSankey() {

    var sankey = d3.sankey()
    .nodeWidth(20)
    .nodePadding(15)
    .size([660, 460]);

  var path = sankey.link();

  sankey
      .nodes(nodes)
      .links(edges)
      .layout(200);


  d3.select("svg").append("g").attr("transform", "translate(80,20)").attr("id", "sankeyG");

  d3.select("#sankeyG").selectAll("g.link")
      .data(edges)
    .enter().append("g")
      .attr("class", "link")
      .append("path")
      .attr("class", "link overall")
      .attr("d", sankey.link())
      .style("fill", "black")
      .style("fill-opacity", .2)

  d3.selectAll("g.link").each(function (link) {
    var linkElement = this;
    var offset = 0;
    link.segments.forEach(function (segment, i) {
      var adjustment = segment / link.value;
      var adjustedPath = sankey.linkAdjusted();
      d3.select(linkElement)
        .append("path")
        .attr("class", "link adjusted")
        .style("fill", "green")
        .style("fill-opacity", i / 10)
        .attr("d", adjustedPath(link, adjustment, offset));
        offset = offset + (link.dy * adjustment);
    })
  })

  d3.select("#sankeyG").selectAll(".node")
      .data(nodes)
    .enter().append("g")
      .attr("class", "node")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

  d3.selectAll(".node").append("rect")
      .attr("height", function(d) { return d.dy; })
      .attr("width", 20)
      .style("fill", "darkgreen")
      .style("stroke", "none")

  d3.selectAll(".node").append("text")
      .attr("x", 0)
      .attr("y", function(d) { return d.dy / 2; })
      .attr("text-anchor", "middle")
      .text(function(d) { return d.id; })
      .style("opacity", .8)
}
</script>
<body onload="makeVizSankey()">
<div class="viz"></div>
<footer>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8" type="text/javascript"></script>
<script src="sankey.js" charset="utf-8" type="text/javascript"></script>
</footer>
</body>
</html>

energy.csv

source,target,2010,2015,2020,2025,2030,2035,2040,2045,2050,total
Coal reserves,Coal,127.93,127.93,127.93,127.93,63.965,63.965,63.965,63.965,63.965,831.545
Coal imports,Coal,349.7879708,296.3632186,211.2161187,77.82581145,35.20638477,19.10842823,22.86599313,26.79703903,31.37680448,1070.547769
Oil reserves,Oil,802.5479528,646.8288435,501.7889501,388.2747242,300.4395801,232.47442,179.8842746,139.1910227,107.70336,3299.133128
Oil imports,Oil,65.64315528,208.35818,357.8050143,457.5236318,528.0501593,614.9478991,678.4226006,733.685649,772.3784493,4416.814739
Gas reserves,Natural Gas,645.7728959,495.8875831,383.1206459,296.4514526,229.3884829,177.4964354,137.3433582,106.2736724,82.23254189,2553.967068
Gas imports,Natural Gas,355.6589677,584.2856578,819.597827,1092.709052,1345.782246,1550.934934,1723.772025,1892.529552,2034.326024,11399.59629
UK land based bioenergy,Bio-conversion,3.027913952,4.692845238,6.402563082,8.158190817,9.960892754,11.81187653,13.71239565,15.66375217,17.66729961,91.09772981
Agricultural 'waste',Bio-conversion,9.282517755,14.61107771,30.99950457,31.97585802,32.98811297,34.0375862,35.12564273,36.2536977,37.42321811,262.6972158
Other waste,Bio-conversion,28.71472764,28.44079879,27.91963931,30.12533828,32.73194046,34.35747868,36.16491136,38.1607047,40.34778662,296.9633259
Other waste,Solid,7.120255333,7.788686663,8.479830435,9.738108471,11.083322,11.83909104,12.61845869,13.42142496,14.24798984,96.33716744
Biomass imports,Solid,4.089432558,3.578253488,3.067074419,2.555895349,2.044716279,1.533537209,1.02235814,0.51117907,0,18.40244651
Coal,Solid,477.7179708,424.2932186,339.1461187,205.7558115,99.17138477,83.07342823,86.83099313,90.76203903,95.34180448,1902.092769
Oil,Liquid,868.1911081,855.1870236,859.5939643,845.798356,828.4897394,847.422319,858.3068752,872.8766716,880.0818093,7715.947867
Natural Gas,Gas,1001.431864,1080.173241,1202.718473,1389.160505,1575.170729,1728.431369,1861.115383,1998.803225,2116.558565,13953.56335
Solar,Solar PV,0.028059966,0.013604832,0,0,0,0,0,0,0,0.041664798
Solar PV,Electricity grid,0.028059966,0.013604832,0,0,0,0,0,0,0,0.041664798
Bio-conversion,Solid,15.69522779,16.8073649,17.95786943,21.31595717,23.85040188,25.35724667,26.91643317,28.52796138,30.1918313,206.6202937
Bio-conversion,Liquid,1.069127005,1.681261069,2.309670538,3.528739363,4.329131458,5.150427938,5.993130385,6.857757484,7.74484597,38.66409121
Bio-conversion,Gas,18.29875011,20.75020481,31.20578182,34.73401889,35.3876884,36.21199755,37.18458852,38.31187901,39.59732328,291.6822324
Bio-conversion,Losses,5.962054444,8.505890961,13.84838517,10.6806717,12.11372446,13.48726925,14.90879767,16.38055669,17.90430379,113.7916541
Solid,Thermal generation,434.145135,381.0784209,294.538574,160.8682199,52.95223532,33.60482625,33.60482625,33.20882816,32.82867,1456.829736
Liquid,Thermal generation,8.534858112,0,0,0,0,0,0,0,0,8.534858112
Gas,Thermal generation,343.3066404,391.9936816,491.9769445,640.9853217,783.690216,901.421775,993.9226489,1086.044761,1152.794637,6786.136626
Nuclear,Thermal generation,160.71,134.9964,77.1408,25.7136,25.7136,0,0,0,0,424.2744
Thermal generation,District heating,9.042140031,9.487279287,9.968747932,10.73757753,11.59832328,12.55911459,13.62952357,14.82061794,16.14504632,107.9883705
Thermal generation,Electricity grid,366.4405941,361.9932623,364.3163394,376.1008348,410.1991759,453.1120284,498.3667387,543.4265396,576.0327661,3949.988279
Thermal generation,Losses,571.2138994,536.5879609,489.3712311,440.7287293,440.5585522,469.3554583,515.5312129,561.0064317,593.4454949,4617.798971
Wind,Electricity grid,14.4406701,29.3428701,45.35726512,57.69377964,48.16934532,32.30288532,15.20918532,0.08783532,0.08783532,242.6916716
Tidal,Electricity grid,0.005003425,0.020013699,0.050034247,0.125085616,0.125085616,0,0,0,0,0.325222603
Wave,Electricity grid,0,0.003002055,0.158441781,0.396104452,0.396104452,0,0,0,0,0.95365274
Hydro,Electricity grid,5.329728,5.329728,5.329728,5.329728,5.329728,5.329728,5.329728,5.329728,5.329728,47.967552
Electricity grid,Over generation / exports,1.13687E-13,0,0,0,0,0,0,0,0,1.13687E-13
Electricity grid,Losses,26.94051694,27.66999195,28.96101727,30.66526914,32.37929876,34.22943122,36.19366126,38.28186775,40.55615154,295.8772058
District heating,Industry,9.042140031,9.487279287,9.968747932,10.73757753,11.59832328,12.55911459,13.62952357,14.82061794,16.14504632,107.9883705
Electricity grid,Heating and cooling - homes,28.7767749,23.94325074,28.18933662,32.84705757,37.9224739,42.61889891,47.89118557,53.84879587,60.65817298,356.6959471
Solid,Heating and cooling - homes,13.14794248,10.7501536,9.93526176,8.879384012,7.579707236,5.910818211,4.105860803,2.144741614,0,62.45386972
Liquid,Heating and cooling - homes,11.7924845,9.641890339,8.91100797,7.963983598,6.798294119,5.301455508,3.682576184,1.923634231,0,56.01532645
Gas,Heating and cooling - homes,354.8435738,382.9695521,408.4682642,433.285271,457.2265205,470.0987089,484.2897767,500.170154,517.9434691,4009.29529
Electricity grid,Heating and cooling - commercial,31.40903798,35.16946485,36.74416003,37.59493963,37.73848109,37.18693674,35.9477411,34.02338939,31.41118474,317.2253355
Liquid,Heating and cooling - commercial,9.357802772,9.360191566,8.461869294,7.44926515,6.305083178,5.010234677,3.543657659,1.882119325,0,51.37022362
Gas,Heating and cooling - commercial,80.65151402,85.39821393,91.44410327,98.05380686,105.2778409,113.1711026,121.793249,131.2091076,141.4891226,968.4880609
Electricity grid,Lighting & appliances - homes,87.37770782,89.47851986,91.46434105,93.16411259,94.56743589,96.68001201,98.8234386,101.0623803,103.4015595,856.0195076
Gas,Lighting & appliances - homes,8.015463096,8.015547174,8.015233023,8.014845996,8.014544549,8.031450005,8.032515957,8.03358205,8.034648285,72.20783013
Electricity grid,Lighting & appliances - commercial,73.04774089,75.15818753,77.34780373,79.61979666,81.97751212,84.42444093,86.96422545,89.60066659,92.33773101,740.4781049
Gas,Lighting & appliances - commercial,8.987057559,8.99526583,9.003481597,9.011704868,9.01993565,9.028173949,9.036419773,9.044673128,9.052934021,81.17964638
Electricity grid,Industry,126.2492384,132.7476228,139.7553308,150.8393381,162.7076996,176.492717,191.8296813,208.8798113,227.8261992,1517.327639
Solid,Industry,56.47800845,59.7818278,63.31457849,68.75029543,74.74457766,81.40888456,88.79327436,96.97920674,106.0575425,696.308196
Liquid,Industry,137.4335097,139.6002686,142.2999648,148.4089907,155.8459842,164.6053674,174.7162779,186.2385821,199.2602412,1448.409187
Gas,Industry,210.4929841,209.2079849,209.2640189,216.6542247,227.307703,241.1112516,257.9195694,277.7250409,300.5983185,2150.281096
Electricity grid,Agriculture,4.259002504,4.285606784,4.312393687,4.33936525,4.366523528,4.393870604,4.421408582,4.449139588,4.477065773,39.3043763
Solid,Agriculture,0.851800501,0.857121357,0.862478737,0.86787305,0.873304706,0.878774121,0.884281716,0.889827918,0.895413155,7.86087526
Liquid,Agriculture,3.513677065,3.535625597,3.557724792,3.579976331,3.602381911,3.624943249,3.64766208,3.67054016,3.693579263,32.42611045
Gas,Agriculture,2.023026189,2.035663222,2.048387002,2.061198494,2.074098676,2.087088537,2.100169076,2.113341304,2.126606242,18.66957874
Electricity grid,Road transport,0,0.264318023,0.538634784,2.789636606,4.920208328,7.234926083,9.532878505,11.57811039,13.8422602,50.70097292
Liquid,Road transport,470.2870297,444.8333119,423.8675334,389.6077924,351.198255,343.6312398,333.6424088,328.0204659,322.0183307,3407.106368
Electricity grid,Rail transport,8.184036114,7.985518411,7.898790629,7.786017019,7.639806008,7.483408182,7.301431687,7.119941726,6.940004428,68.3389542
Liquid,Rail transport,9.540451289,9.197930429,9.06515471,8.882304526,8.638272423,8.377036331,8.073455426,7.774544525,7.482590654,77.03174031
Liquid,Domestic aviation,9.55109733,10.16371642,11.07874205,11.92797975,12.65784724,13.33107712,13.86025128,14.34440942,14.78544909,111.7005697
Liquid,National navigation,26.57289571,25.38306456,24.58984379,23.99670496,23.68879172,23.38482946,23.0847675,22.78855577,22.49614487,215.9855983
Liquid,International aviation,125.0236042,141.9277504,160.7246469,170.5797952,178.7278412,190.5888908,194.9306323,196.4187558,188.5816831,1547.5036
Liquid,International shipping,57.28499215,62.90268135,69.07127281,76.70040745,85.17220349,94.57973548,105.0263583,116.6268428,129.5086365,796.8731304
Gas,Losses,11.41035458,12.30753698,13.70382224,15.82815018,17.9475581,19.69381594,21.20562289,22.77444364,24.11615269,158.9874572

readme.md

This sankey diagram of energy generation and consumption shows the total energy flow from 2010 to 2050, split into 5-year segments. 2010 is in grey, 2050 is in dark green, and each 5-year step between is in order between those. You can see, for instance, the decline in oil reserves over the 40 year period, and corresponding increase in oil imports.

This relies on a [modification of the original d3.sankey to draw adjusted links](https://github.com/emeeks/d3-plugins/blob/master/sankey/sankey.js). This version of d3.sankey also differs in that it draws links as areas rather than relying on stroke-width.

The sankey.adjustedLink function works like the regular sankey.link function but takes an additional adjustment and offset value. The adjustment is the percent value of the overall link, and the offset is the y-pixel offset of the link. In this case, those both correspond to the percent and order of the segments, but don't have to.

The sankey diagram itself is laid out using the parent links as you would normally use d3.sankey (in this case the total energy flow over the forty year span) and then the adjusted links are drawn afterward as appropriate.

**Source: [https://www.gov.uk/government/publications/2050-pathways-calculator-with-costs](https://www.gov.uk/government/publications/2050-pathways-calculator-with-costs)**

sankey.js

d3.sankey = function() {
  var sankey = {},
      nodeWidth = 24,
      nodePadding = 8,
      size = [1, 1],
      nodes = [],
      links = [];

  sankey.nodeWidth = function(_) {
    if (!arguments.length) return nodeWidth;
    nodeWidth = +_;
    return sankey;
  };

  sankey.nodePadding = function(_) {
    if (!arguments.length) return nodePadding;
    nodePadding = +_;
    return sankey;
  };

  sankey.nodes = function(_) {
    if (!arguments.length) return nodes;
    nodes = _;
    return sankey;
  };

  sankey.links = function(_) {
    if (!arguments.length) return links;
    links = _;
    return sankey;
  };

  sankey.size = function(_) {
    if (!arguments.length) return size;
    size = _;
    return sankey;
  };

  sankey.layout = function(iterations) {
    computeNodeLinks();
    computeNodeValues();
    computeNodeBreadths();
    computeNodeDepths(iterations);
    computeLinkDepths();
    return sankey;
  };

  sankey.relayout = function() {
    computeLinkDepths();
    return sankey;
  };

  sankey.linkAdjusted = function() {
    var curvature = .5;

    function link(d, adjustment, segmentOffset) {
      var x0 = d.source.x + d.source.dx,
          x1 = d.target.x,
          xi = d3.interpolateNumber(x0, x1),
          x2 = xi(curvature),
          x3 = xi(1 - curvature),
          y0 = d.source.y + d.sy + segmentOffset,
          y1 = d.target.y + d.ty + segmentOffset,
          y2 = d.target.y + d.ty + (d.dy * adjustment) + segmentOffset,
          y3 = d.source.y + d.sy + (d.dy * adjustment) + segmentOffset;

      return "M" + x0 + "," + y0
           + "C" + x2 + "," + y0
           + " " + x3 + "," + y1
           + " " + x1 + "," + y1
           + "L" + x1 + "," + y2
           + "C" + x3 + "," + y2
           + " " + x2 + "," + y3
           + " " + x0 + "," + y3
           + "Z";
    }

    link.curvature = function(_) {
      if (!arguments.length) return curvature;
      curvature = +_;
      return link;
    };

    return link;
  };

  sankey.link = function() {
    var curvature = .5;

    function link(d) {
      var x0 = d.source.x + d.source.dx,
          x1 = d.target.x,
          xi = d3.interpolateNumber(x0, x1),
          x2 = xi(curvature),
          x3 = xi(1 - curvature),
          y0 = d.source.y + d.sy,
          y1 = d.target.y + d.ty,
          y2 = d.target.y + d.ty + d.dy,
          y3 = d.source.y + d.sy + d.dy;

      if (y3 - y0 < 30000) {

      return "M" + x0 + "," + y0
           + "C" + x2 + "," + y0
           + " " + x3 + "," + y1
           + " " + x1 + "," + y1
           + "L" + x1 + "," + y2
           + "C" + x3 + "," + y2
           + " " + x2 + "," + y3
           + " " + x0 + "," + y3
           + "Z";
         }
         else {

          var offset = (x1 - x0) /4;
      return "M" + x0 + "," + y0
           + "C" + x2 + "," + y0
           + " " + x3 + "," + (y1)
           + " " + (x1 - offset) + "," + (y1 + 0)
           + "L" + (x1 - 6) + "," + ((y2 + y1)/2)
           + "L" + (x1 - offset) + "," + (y2 + 0)
           + "C" + x3 + "," + (y2)
           + " " + x2 + "," + y3
           + " " + x0 + "," + y3
           + "Z";

         }
    }

    link.curvature = function(_) {
      if (!arguments.length) return curvature;
      curvature = +_;
      return link;
    };

    return link;
  };

  // Populate the sourceLinks and targetLinks for each node.
  // Also, if the source and target are not objects, assume they are indices.
  function computeNodeLinks() {
    nodes.forEach(function(node) {
      node.sourceLinks = [];
      node.targetLinks = [];
    });
    links.forEach(function(link) {
      var source = link.source,
          target = link.target;
      if (typeof source === "number") source = link.source = nodes[link.source];
      if (typeof target === "number") target = link.target = nodes[link.target];
      source.sourceLinks.push(link);
      target.targetLinks.push(link);
    });
  }

  // Compute the value (size) of each node by summing the associated links.
  function computeNodeValues() {
    nodes.forEach(function(node) {
      node.value = Math.max(
        d3.sum(node.sourceLinks, value),
        d3.sum(node.targetLinks, value)
      );
    });
  }

  // Iteratively assign the breadth (x-position) for each node.
  // Nodes are assigned the maximum breadth of incoming neighbors plus one;
  // nodes with no incoming links are assigned breadth zero, while
  // nodes with no outgoing links are assigned the maximum breadth.
  function computeNodeBreadths() {

    var remainingNodes = nodes,
        nextNodes,
        x = 0;

    while (remainingNodes.length) {
      nextNodes = [];
      remainingNodes.forEach(function(node) {
        node.x = x;
        node.dx = nodeWidth;
        node.sourceLinks.forEach(function(link) {
          if (nextNodes.indexOf(link.target) < 0) {
            nextNodes.push(link.target);
          }
        });
      });
      remainingNodes = nextNodes;
      ++x;
    }

    moveSinksRight(x);
    scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
  }

  function moveSourcesRight() {
    nodes.forEach(function(node) {
      if (!node.targetLinks.length) {
        node.x = d3.min(node.sourceLinks, function(d) { return d.target.x; }) - 1;
      }
    });
  }

  function moveSinksRight(x) {
    nodes.forEach(function(node) {
      if (!node.sourceLinks.length) {
        node.x = x - 1;
      }
    });
  }

  function scaleNodeBreadths(kx) {
    nodes.forEach(function(node) {
      node.x *= kx;
    });
  }

  function computeNodeDepths(iterations) {
    var nodesByBreadth = d3.nest()
        .key(function(d) { return d.x; })
        .sortKeys(d3.ascending)
        .entries(nodes)
        .map(function(d) { return d.values; });

    //
    initializeNodeDepth();
    resolveCollisions();
    for (var alpha = 1; iterations > 0; --iterations) {
      relaxRightToLeft(alpha *= .99);
      resolveCollisions();
      relaxLeftToRight(alpha);
      resolveCollisions();
    }

    function initializeNodeDepth() {
      var ky = d3.min(nodesByBreadth, function(nodes) {
        return (size[1] - (nodes.length - 1) * nodePadding) / d3.sum(nodes, value);
      });

      nodesByBreadth.forEach(function(nodes) {
        nodes.forEach(function(node, i) {
          node.y = i;
          node.dy = node.value * ky;
        });
      });

      links.forEach(function(link) {
        link.dy = link.value * ky;
      });
    }

    function relaxLeftToRight(alpha) {
      nodesByBreadth.forEach(function(nodes, breadth) {
        nodes.forEach(function(node) {
          if (node.targetLinks.length) {
            var y = d3.sum(node.targetLinks, weightedSource) / d3.sum(node.targetLinks, value);
            node.y += (y - center(node)) * alpha;
          }
        });
      });

      function weightedSource(link) {
        return center(link.source) * link.value;
      }
    }

    function relaxRightToLeft(alpha) {
      nodesByBreadth.slice().reverse().forEach(function(nodes) {
        nodes.forEach(function(node) {
          if (node.sourceLinks.length) {
            var y = d3.sum(node.sourceLinks, weightedTarget) / d3.sum(node.sourceLinks, value);
            node.y += (y - center(node)) * alpha;
          }
        });
      });

      function weightedTarget(link) {
        return center(link.target) * link.value;
      }
    }

    function resolveCollisions() {
      nodesByBreadth.forEach(function(nodes) {
        var node,
            dy,
            y0 = 0,
            n = nodes.length,
            i;

        // Push any overlapping nodes down.
        nodes.sort(ascendingDepth);
        for (i = 0; i < n; ++i) {
          node = nodes[i];
          dy = y0 - node.y;
          if (dy > 0) node.y += dy;
          y0 = node.y + node.dy + nodePadding;
        }

        // If the bottommost node goes outside the bounds, push it back up.
        dy = y0 - nodePadding - size[1];
        if (dy > 0) {
          y0 = node.y -= dy;

          // Push any overlapping nodes back up.
          for (i = n - 2; i >= 0; --i) {
            node = nodes[i];
            dy = node.y + node.dy + nodePadding - y0;
            if (dy > 0) node.y -= dy;
            y0 = node.y;
          }
        }
      });
    }

    function ascendingDepth(a, b) {
      return a.y - b.y;
    }
  }

  function computeLinkDepths() {
    nodes.forEach(function(node) {
      node.sourceLinks.sort(ascendingTargetDepth);
      node.targetLinks.sort(ascendingSourceDepth);
    });
    nodes.forEach(function(node) {
      var sy = 0, ty = 0;
      node.sourceLinks.forEach(function(link) {
        link.sy = sy;
        sy += link.dy;
      });
      node.targetLinks.forEach(function(link) {
        link.ty = ty;
        ty += link.dy;
      });
    });

    function ascendingSourceDepth(a, b) {
      return a.source.y - b.source.y;
    }

    function ascendingTargetDepth(a, b) {
      return a.target.y - b.target.y;
    }
  }

  function center(node) {
    return node.y + node.dy / 2;
  }

  function value(link) {
    return link.value;
  }

  return sankey;
};