Socket
Socket
Sign inDemoInstall

d3-sankey

Package Overview
Dependencies
Maintainers
2
Versions
33
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

d3-sankey - npm Package Compare versions

Comparing version 0.4.2 to 0.5.0

img/energy.png

257

build/d3-sankey.js

@@ -1,98 +0,112 @@

// https://github.com/d3/d3-sankey Version 0.4.2. Copyright 2017 Mike Bostock.
// https://github.com/d3/d3-sankey Version 0.5.0. Copyright 2017 Mike Bostock.
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-collection'), require('d3-interpolate')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-collection', 'd3-interpolate'], factory) :
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-collection'), require('d3-shape')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-collection', 'd3-shape'], factory) :
(factory((global.d3 = global.d3 || {}),global.d3,global.d3,global.d3));
}(this, (function (exports,d3Array,d3Collection,d3Interpolate) { 'use strict';
}(this, (function (exports,d3Array,d3Collection,d3Shape) { 'use strict';
function constant(x) {
return function() {
return x;
};
}
function ascendingSourceDepth(a, b) {
return a.source.y - b.source.y;
}
function ascendingTargetDepth(a, b) {
return a.target.y - b.target.y;
}
function ascendingDepth(a, b) {
return a.y - b.y;
}
function value(d) {
return d.value;
}
function nodeCenter(node) {
return node.y + node.dy / 2;
}
function weightedSource(link) {
return nodeCenter(link.source) * link.value;
}
function weightedTarget(link) {
return nodeCenter(link.target) * link.value;
}
function defaultNodes(graph) {
return graph.nodes;
}
function defaultLinks(graph) {
return graph.links;
}
var sankey = function() {
var sankey = {},
nodeWidth = 24,
nodePadding = 8,
size = [1, 1],
nodes = [],
links = [];
var x0 = 0, y0 = 0, x1 = 1, y1 = 1, // extent
dx = 24, // nodeWidth
py = 8, // nodePadding
nodes = defaultNodes,
links = defaultLinks,
iterations = 32;
function sankey() {
var graph = {nodes: nodes.apply(null, arguments), links: links.apply(null, arguments)};
computeNodeLinks(graph);
computeNodeValues(graph);
computeNodeBreadths(graph);
computeNodeDepths(graph, iterations);
computeLinkDepths(graph);
return graph;
}
sankey.update = function(graph) {
computeLinkDepths(graph);
return graph;
};
sankey.nodeWidth = function(_) {
if (!arguments.length) return nodeWidth;
nodeWidth = +_;
return sankey;
return arguments.length ? (dx = +_, sankey) : dx;
};
sankey.nodePadding = function(_) {
if (!arguments.length) return nodePadding;
nodePadding = +_;
return sankey;
return arguments.length ? (py = +_, sankey) : py;
};
sankey.nodes = function(_) {
if (!arguments.length) return nodes;
nodes = _;
return sankey;
return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes;
};
sankey.links = function(_) {
if (!arguments.length) return links;
links = _;
return sankey;
return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links;
};
sankey.size = function(_) {
if (!arguments.length) return size;
size = _;
return sankey;
return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
};
sankey.layout = function(iterations) {
computeNodeLinks();
computeNodeValues();
computeNodeBreadths();
computeNodeDepths(iterations);
computeLinkDepths();
return sankey;
sankey.extent = function(_) {
return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
};
sankey.relayout = function() {
computeLinkDepths();
return sankey;
sankey.iterations = function(_) {
return arguments.length ? (iterations = +_, sankey) : iterations;
};
sankey.link = function() {
var curvature = .5;
function link(d) {
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = d3Interpolate.interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
y0 = d.source.y + d.sy + d.dy / 2,
y1 = d.target.y + d.ty + d.dy / 2;
return "M" + x0 + "," + y0
+ "C" + x2 + "," + y0
+ " " + x3 + "," + y1
+ " " + x1 + "," + y1;
}
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) {
function computeNodeLinks(graph) {
graph.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];
graph.links.forEach(function(link) {
var source = link.source, target = link.target;
if (typeof source === "number") source = link.source = graph.nodes[link.source];
if (typeof target === "number") target = link.target = graph.nodes[link.target];
source.sourceLinks.push(link);

@@ -104,4 +118,4 @@ target.targetLinks.push(link);

// Compute the value (size) of each node by summing the associated links.
function computeNodeValues() {
nodes.forEach(function(node) {
function computeNodeValues(graph) {
graph.nodes.forEach(function(node) {
node.value = Math.max(

@@ -118,4 +132,4 @@ d3Array.sum(node.sourceLinks, value),

// nodes with no outgoing links are assigned the maximum breadth.
function computeNodeBreadths() {
var remainingNodes = nodes,
function computeNodeBreadths(graph) {
var remainingNodes = graph.nodes,
nextNodes,

@@ -128,3 +142,3 @@ x = 0;

node.x = x;
node.dx = nodeWidth;
node.dx = dx;
node.sourceLinks.forEach(function(link) {

@@ -141,8 +155,8 @@ if (nextNodes.indexOf(link.target) < 0) {

//
moveSinksRight(x);
scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
moveSinksRight(graph, x);
scaleNodeBreadths(graph, (x1 - x0 - dx) / (x - 1));
}
// function moveSourcesRight() {
// nodes.forEach(function(node) {
// function moveSourcesRight(graph) {
// graph.nodes.forEach(function(node) {
// if (!node.targetLinks.length) {

@@ -154,4 +168,4 @@ // node.x = min(node.sourceLinks, function(d) { return d.target.x; }) - 1;

function moveSinksRight(x) {
nodes.forEach(function(node) {
function moveSinksRight(graph, x) {
graph.nodes.forEach(function(node) {
if (!node.sourceLinks.length) {

@@ -163,13 +177,13 @@ node.x = x - 1;

function scaleNodeBreadths(kx) {
nodes.forEach(function(node) {
node.x *= kx;
function scaleNodeBreadths(graph, kx) {
graph.nodes.forEach(function(node) {
node.x = x0 + node.x * kx;
});
}
function computeNodeDepths(iterations) {
function computeNodeDepths(graph) {
var nodesByBreadth = d3Collection.nest()
.key(function(d) { return d.x; })
.sortKeys(d3Array.ascending)
.entries(nodes)
.entries(graph.nodes)
.map(function(d) { return d.values; });

@@ -180,4 +194,4 @@

resolveCollisions();
for (var alpha = 1; iterations > 0; --iterations) {
relaxRightToLeft(alpha *= .99);
for (var alpha = 1, n = iterations; n > 0; --n) {
relaxRightToLeft(alpha *= 0.99);
resolveCollisions();

@@ -190,3 +204,3 @@ relaxLeftToRight(alpha);

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

@@ -201,3 +215,3 @@

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

@@ -211,11 +225,6 @@ });

if (node.targetLinks.length) {
var y = d3Array.sum(node.targetLinks, weightedSource) / d3Array.sum(node.targetLinks, value);
node.y += (y - center(node)) * alpha;
node.y += (d3Array.sum(node.targetLinks, weightedSource) / d3Array.sum(node.targetLinks, value) - nodeCenter(node)) * alpha;
}
});
});
function weightedSource(link) {
return center(link.source) * link.value;
}
}

@@ -227,11 +236,6 @@

if (node.sourceLinks.length) {
var y = d3Array.sum(node.sourceLinks, weightedTarget) / d3Array.sum(node.sourceLinks, value);
node.y += (y - center(node)) * alpha;
node.y += (d3Array.sum(node.sourceLinks, weightedTarget) / d3Array.sum(node.sourceLinks, value) - nodeCenter(node)) * alpha;
}
});
});
function weightedTarget(link) {
return center(link.target) * link.value;
}
}

@@ -243,3 +247,3 @@

dy,
y0 = 0,
y = y0,
n = nodes.length,

@@ -252,11 +256,11 @@ i;

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

@@ -266,5 +270,5 @@ // Push any overlapping nodes back up.

node = nodes[i];
dy = node.y + node.dy + nodePadding - y0;
dy = node.y + node.dy + py - y;
if (dy > 0) node.y -= dy;
y0 = node.y;
y = node.y;
}

@@ -274,14 +278,10 @@ }

}
function ascendingDepth(a, b) {
return a.y - b.y;
}
}
function computeLinkDepths() {
nodes.forEach(function(node) {
function computeLinkDepths(graph) {
graph.nodes.forEach(function(node) {
node.sourceLinks.sort(ascendingTargetDepth);
node.targetLinks.sort(ascendingSourceDepth);
});
nodes.forEach(function(node) {
graph.nodes.forEach(function(node) {
var sy = 0, ty = 0;

@@ -297,24 +297,23 @@ node.sourceLinks.forEach(function(link) {

});
}
function ascendingSourceDepth(a, b) {
return a.source.y - b.source.y;
}
return sankey;
};
function ascendingTargetDepth(a, b) {
return a.target.y - b.target.y;
}
}
function horizontalSource(d) {
return [d.source.x + d.source.dx, d.source.y + d.sy + d.dy / 2];
}
function center(node) {
return node.y + node.dy / 2;
}
function horizontalTarget(d) {
return [d.target.x, d.target.y + d.ty + d.dy / 2];
}
function value(link) {
return link.value;
}
return sankey;
var sankeyLinkHorizontal = function() {
return d3Shape.linkHorizontal()
.source(horizontalSource)
.target(horizontalTarget);
};
exports.sankey = sankey;
exports.sankeyLinkHorizontal = sankeyLinkHorizontal;

@@ -321,0 +320,0 @@ Object.defineProperty(exports, '__esModule', { value: true });

@@ -1,2 +0,2 @@

// https://github.com/d3/d3-sankey Version 0.4.2. Copyright 2017 Mike Bostock.
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-array"),require("d3-collection"),require("d3-interpolate")):"function"==typeof define&&define.amd?define(["exports","d3-array","d3-collection","d3-interpolate"],t):t(n.d3=n.d3||{},n.d3,n.d3,n.d3)}(this,function(n,t,r,e){"use strict";var u=function(){function n(){v.forEach(function(n){n.sourceLinks=[],n.targetLinks=[]}),k.forEach(function(n){var t=n.source,r=n.target;"number"==typeof t&&(t=n.source=v[n.source]),"number"==typeof r&&(r=n.target=v[n.target]),t.sourceLinks.push(n),r.targetLinks.push(n)})}function u(){v.forEach(function(n){n.value=Math.max(t.sum(n.sourceLinks,y),t.sum(n.targetLinks,y))})}function o(){for(var n,t=v,r=0;t.length;)n=[],t.forEach(function(t){t.x=r,t.dx=h,t.sourceLinks.forEach(function(t){n.indexOf(t.target)<0&&n.push(t.target)})}),t=n,++r;c(r),i((g[0]-h)/(r-1))}function c(n){v.forEach(function(t){t.sourceLinks.length||(t.x=n-1)})}function i(n){v.forEach(function(t){t.x*=n})}function f(n){function e(){var n=t.min(f,function(n){return(g[1]-(n.length-1)*l)/t.sum(n,y)});f.forEach(function(t){t.forEach(function(t,r){t.y=r,t.dy=t.value*n})}),k.forEach(function(t){t.dy=t.value*n})}function u(n){function r(n){return s(n.source)*n.value}f.forEach(function(e){e.forEach(function(e){if(e.targetLinks.length){var u=t.sum(e.targetLinks,r)/t.sum(e.targetLinks,y);e.y+=(u-s(e))*n}})})}function o(n){function r(n){return s(n.target)*n.value}f.slice().reverse().forEach(function(e){e.forEach(function(e){if(e.sourceLinks.length){var u=t.sum(e.sourceLinks,r)/t.sum(e.sourceLinks,y);e.y+=(u-s(e))*n}})})}function c(){f.forEach(function(n){var t,r,e,u=0,o=n.length;for(n.sort(i),e=0;e<o;++e)t=n[e],r=u-t.y,r>0&&(t.y+=r),u=t.y+t.dy+l;if(r=u-l-g[1],r>0)for(u=t.y-=r,e=o-2;e>=0;--e)t=n[e],r=t.y+t.dy+l-u,r>0&&(t.y-=r),u=t.y})}function i(n,t){return n.y-t.y}var f=r.nest().key(function(n){return n.x}).sortKeys(t.ascending).entries(v).map(function(n){return n.values});e(),c();for(var a=1;n>0;--n)o(a*=.99),c(),u(a),c()}function a(){function n(n,t){return n.source.y-t.source.y}function t(n,t){return n.target.y-t.target.y}v.forEach(function(r){r.sourceLinks.sort(t),r.targetLinks.sort(n)}),v.forEach(function(n){var t=0,r=0;n.sourceLinks.forEach(function(n){n.sy=t,t+=n.dy}),n.targetLinks.forEach(function(n){n.ty=r,r+=n.dy})})}function s(n){return n.y+n.dy/2}function y(n){return n.value}var d={},h=24,l=8,g=[1,1],v=[],k=[];return d.nodeWidth=function(n){return arguments.length?(h=+n,d):h},d.nodePadding=function(n){return arguments.length?(l=+n,d):l},d.nodes=function(n){return arguments.length?(v=n,d):v},d.links=function(n){return arguments.length?(k=n,d):k},d.size=function(n){return arguments.length?(g=n,d):g},d.layout=function(t){return n(),u(),o(),f(t),a(),d},d.relayout=function(){return a(),d},d.link=function(){function n(n){var r=n.source.x+n.source.dx,u=n.target.x,o=e.interpolateNumber(r,u),c=o(t),i=o(1-t),f=n.source.y+n.sy+n.dy/2,a=n.target.y+n.ty+n.dy/2;return"M"+r+","+f+"C"+c+","+f+" "+i+","+a+" "+u+","+a}var t=.5;return n.curvature=function(r){return arguments.length?(t=+r,n):t},n},d};n.sankey=u,Object.defineProperty(n,"__esModule",{value:!0})});
// https://github.com/d3/d3-sankey Version 0.5.0. Copyright 2017 Mike Bostock.
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("d3-array"),require("d3-collection"),require("d3-shape")):"function"==typeof define&&define.amd?define(["exports","d3-array","d3-collection","d3-shape"],t):t(n.d3=n.d3||{},n.d3,n.d3,n.d3)}(this,function(n,t,e,r){"use strict";function o(n){return function(){return n}}function u(n,t){return n.source.y-t.source.y}function i(n,t){return n.target.y-t.target.y}function c(n,t){return n.y-t.y}function f(n){return n.value}function s(n){return n.y+n.dy/2}function a(n){return s(n.source)*n.value}function d(n){return s(n.target)*n.value}function y(n){return n.nodes}function l(n){return n.links}function h(n){return[n.source.x+n.source.dx,n.source.y+n.sy+n.dy/2]}function g(n){return[n.target.x,n.target.y+n.ty+n.dy/2]}var k=function(){function n(){var n={nodes:j.apply(null,arguments),links:H.apply(null,arguments)};return r(n),h(n),g(n),v(n),E(n),n}function r(n){n.nodes.forEach(function(n){n.sourceLinks=[],n.targetLinks=[]}),n.links.forEach(function(t){var e=t.source,r=t.target;"number"==typeof e&&(e=t.source=n.nodes[t.source]),"number"==typeof r&&(r=t.target=n.nodes[t.target]),e.sourceLinks.push(t),r.targetLinks.push(t)})}function h(n){n.nodes.forEach(function(n){n.value=Math.max(t.sum(n.sourceLinks,f),t.sum(n.targetLinks,f))})}function g(n){for(var t,e=n.nodes,r=0;e.length;)t=[],e.forEach(function(n){n.x=r,n.dx=q,n.sourceLinks.forEach(function(n){t.indexOf(n.target)<0&&t.push(n.target)})}),e=t,++r;k(n,r),p(n,(m-L-q)/(r-1))}function k(n,t){n.nodes.forEach(function(n){n.sourceLinks.length||(n.x=t-1)})}function p(n,t){n.nodes.forEach(function(n){n.x=L+n.x*t})}function v(n){function r(){o.forEach(function(n){var t,e,r,o=x,u=n.length;for(n.sort(c),r=0;r<u;++r)t=n[r],e=o-t.y,e>0&&(t.y+=e),o=t.y+t.dy+z;if((e=o-z-b)>0)for(o=t.y-=e,r=u-2;r>=0;--r)t=n[r],e=t.y+t.dy+z-o,e>0&&(t.y-=e),o=t.y})}var o=e.nest().key(function(n){return n.x}).sortKeys(t.ascending).entries(n.nodes).map(function(n){return n.values});!function(){var e=t.min(o,function(n){return(b-x-(n.length-1)*z)/t.sum(n,f)});o.forEach(function(n){n.forEach(function(n,t){n.y=t,n.dy=n.value*e})}),n.links.forEach(function(n){n.dy=n.value*e})}(),r();for(var u=1,i=M;i>0;--i)!function(n){o.slice().reverse().forEach(function(e){e.forEach(function(e){e.sourceLinks.length&&(e.y+=(t.sum(e.sourceLinks,d)/t.sum(e.sourceLinks,f)-s(e))*n)})})}(u*=.99),r(),function(n){o.forEach(function(e){e.forEach(function(e){e.targetLinks.length&&(e.y+=(t.sum(e.targetLinks,a)/t.sum(e.targetLinks,f)-s(e))*n)})})}(u),r()}function E(n){n.nodes.forEach(function(n){n.sourceLinks.sort(i),n.targetLinks.sort(u)}),n.nodes.forEach(function(n){var t=0,e=0;n.sourceLinks.forEach(function(n){n.sy=t,t+=n.dy}),n.targetLinks.forEach(function(n){n.ty=e,e+=n.dy})})}var L=0,x=0,m=1,b=1,q=24,z=8,j=y,H=l,M=32;return n.update=function(n){return E(n),n},n.nodeWidth=function(t){return arguments.length?(q=+t,n):q},n.nodePadding=function(t){return arguments.length?(z=+t,n):z},n.nodes=function(t){return arguments.length?(j="function"==typeof t?t:o(t),n):j},n.links=function(t){return arguments.length?(H="function"==typeof t?t:o(t),n):H},n.size=function(t){return arguments.length?(L=x=0,m=+t[0],b=+t[1],n):[m-L,b-x]},n.extent=function(t){return arguments.length?(L=+t[0][0],m=+t[1][0],x=+t[0][1],b=+t[1][1],n):[[L,x],[m,b]]},n.iterations=function(t){return arguments.length?(M=+t,n):M},n},p=function(){return r.linkHorizontal().source(h).target(g)};n.sankey=k,n.sankeyLinkHorizontal=p,Object.defineProperty(n,"__esModule",{value:!0})});
export {default as sankey} from "./src/sankey";
export {default as sankeyLinkHorizontal} from "./src/sankeyLinkHorizontal";
{
"name": "d3-sankey",
"version": "0.4.2",
"version": "0.5.0",
"description": "Visualize flow between nodes in a directed acyclic network.",

@@ -12,8 +12,8 @@ "keywords": [

"name": "Mike Bostock",
"url": "http://bost.ocks.org/mike"
"url": "https://bost.ocks.org/mike/"
},
"license": "BSD-3-Clause",
"main": "build/d3-sankey.js",
"module": "index",
"jsnext:main": "index",
"module": "index",
"homepage": "https://github.com/d3/d3-sankey",

@@ -25,3 +25,3 @@ "repository": {

"scripts": {
"pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -g d3-array:d3,d3-collection:d3,d3-interpolate:d3 -f umd -n d3 -o build/d3-sankey.js -- index.js",
"pretest": "rm -rf build && mkdir build && rollup --banner \"$(preamble)\" -g d3-array:d3,d3-collection:d3,d3-shape:d3 -f umd -n d3 -o build/d3-sankey.js -- index.js",
"test": "tape 'test/**/*-test.js' && eslint index.js src",

@@ -34,7 +34,8 @@ "prepublish": "npm run test && uglifyjs --preamble \"$(preamble)\" build/d3-sankey.js -c -m -o build/d3-sankey.min.js",

"d3-collection": "1",
"d3-interpolate": "1"
"d3-interpolate": "1",
"d3-shape": "^1.2.0"
},
"devDependencies": {
"eslint": "3",
"package-preamble": "0.0.2",
"package-preamble": "0.1.0",
"rollup": "0.41",

@@ -41,0 +42,0 @@ "tape": "4",

# d3-sankey
D3 4.0 implementation of the Sankey plugin to visualize the flow between nodes in a directed acyclic network.
Sankey diagrams visualize the directed flow between nodes in an acyclic network. For example, this diagram shows a possible scenario of UK energy production and consumption in 2050:
[<img alt="Sankey diagram" src="https://raw.githubusercontent.com/d3/d3-sankey/master/img/energy.png" width="960" height="500">](https://bl.ocks.org/mbostock/ca9a0bb7ba204d12974bca90acc507c0)
Source: Department of Energy & Climate Change, Tom Counsell.
## Installing

@@ -11,4 +15,7 @@

```javascript
<script src="https://d3js.org/d3.v4.js"></script>
```html
<script src="https://unpkg.com/d3-array@1"></script>
<script src="https://unpkg.com/d3-collection@1"></script>
<script src="https://unpkg.com/d3-path@1"></script>
<script src="https://unpkg.com/d3-shape@1"></script>
<script src="https://unpkg.com/d3-sankey@0"></script>

@@ -22,60 +29,128 @@ <script>

## Demo
Here is Mike Bostock's famous example [recreated with d3-sankey](http://bl.ocks.org/xaranke/9ada4c74a87b57ae7308).
## API Reference
Clone or download the block, then run `npm install` and `npm run build` to create `d3.min.js`.
<a href="#sankey" name="sankey">#</a> d3.<b>sankey</b>() [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L41 "Source")
## API Reference
Constructs a new Sankey generator with the default settings.
<a href="#sankey" name="sankey">#</a> <b>sankey</b>()
<a href="#_sankey" name="_sankey">#</a> <i>sankey</i>(<i>arguments</i>…) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L49 "Source")
Constructs a new sankey generator with the default settings.
Computes the node and link positions for the given *arguments*, returning a *graph* representing the Sankey layout. The returned *graph* has the following properties:
<a name="sankey_nodeWidth" href="#sankey_nodeWidth">#</a> <i>sankey</i>.<b>nodeWidth</b>([<i>width</i>])
* *graph*.nodes - the array of [nodes](#sankey_nodes)
* *graph*.links - the array of [links](#sankey_links)
If <i>width</i> is specified, sets the node width to the specified function or number and returns this sankey generator. If <i>width</i> is not specified, returns the current node width accessor, which defaults to:
<a href="#sankey_update" name="sankey_update">#</a> <i>sankey</i>.<b>update</b>(<i>graph</i>) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L59 "Source")
Recomputes the specified *graph*’s links’ positions, updating the following properties of each *link*:
* *link*.sy - the link’s vertical starting position (at source node)
* *link*.ty - the link’s vertical end position (at target node)
This method is intended to be called after computing the initial [Sankey layout](#_sankey), for example when the diagram is repositioned interactively.
<a name="sankey_nodes" href="#sankey_nodes">#</a> <i>sankey</i>.<b>nodes</b>([<i>nodes</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L72 "Source")
If *nodes* is specified, sets the Sankey generator’s nodes accessor to the specified function or array and returns this Sankey generator. If *nodes* is not specified, returns the current nodes accessor, which defaults to:
```js
function nodeWidth() {
return 24;
function nodes(graph) {
return graph.nodes;
}
```
<a name="sankey_nodePadding" href="#sankey_nodePadding">#</a> <i>sankey</i>.<b>nodePadding</b>([<i>padding</i>])
If *nodes* is specified as a function, the function is invoked when the Sankey layout is [generated](#_sankey), being passed any arguments passed to the Sankey generator. This function must return an array of nodes. If *nodes* is not a function, it must be a constant array of *nodes*.
If <i>padding</i> is specified, sets the node padding to the specified function or number and returns this sankey generator. If <i>padding</i> is not specified, returns the current node padding accessor, which defaults to:
Each *node* must be an object. The following properties are assigned by the [Sankey generator](#_sankey):
* *node*.sourceLinks - the array of outgoing [links](#sankey_links) which have this node as their source
* *node*.targetLinks - the array of incoming [links](#sankey_links) which have this node as their target
* *node*.value - the node’s value; the sum of *link*.value for the node’s incoming [links](#sankey_links)
* *node*x - the node’s horizontal position (derived from the graph topology)
* *node*.dx - the node’s node width
* *node*.y - the node’s vertical position
* *node*.dy - the node’s height (proportional to its value)
See also [*sankey*.links](#sankey_links).
<a name="sankey_links" href="#sankey_links">#</a> <i>sankey</i>.<b>links</b>([<i>links</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L76 "Source")
If *links* is specified, sets the Sankey generator’s links accessor to the specified function or array and returns this Sankey generator. If *links* is not specified, returns the current links accessor, which defaults to:
```js
function nodePadding() {
return 8;
function links(graph) {
return graph.links;
}
```
Here padding refers to the vertical space between nodes that occupy the same horizontal space.
<a name="sankey_nodes" href="#sankey_nodes">#</a> <i>sankey</i>.<b>nodes</b>([<i>nodes</i>])
If *links* is specified as a function, the function is invoked when the Sankey layout is [generated](#_sankey), being passed any arguments passed to the Sankey generator. This function must return an array of links. If *links* is not a function, it must be a constant array of *links*.
If <i>nodes</i> is specified, sets the list of nodes to the specified function or array and returns this sankey generator. If <i>nodes</i> is not specified, returns the current accessor to the list of nodes, which defaults to:
Each *link* must be an object with the following properties:
* *link*.source - the link’s source [node](#sankey_nodes)
* *link*.target - the link’s target [node](#sankey_nodes)
* *link*.value - the link’s numeric value
For convenience, a link’s source and target may be initialized using the zero-based index of corresponding node in the array of nodes passed to the [Sankey generator](#_sankey) rather than object references. The following properties are assigned to each link by the [Sankey generator](#_sankey):
* *link*.dy - the link’s breadth (proportional to its value)
* *link*.sy - the link’s vertical starting position (at source node)
* *link*.ty - the link’s vertical end position (at target node)
<a name="sankey_nodeWidth" href="#sankey_nodeWidth">#</a> <i>sankey</i>.<b>nodeWidth</b>([<i>width</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L64 "Source")
If *width* is specified, sets the node width to the specified number and returns this Sankey generator. If *width* is not specified, returns the current node width, which defaults to 24.
<a name="sankey_nodePadding" href="#sankey_nodePadding">#</a> <i>sankey</i>.<b>nodePadding</b>([<i>padding</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L68 "Source")
If *padding* is specified, sets the vertical separation between nodes at each column to the specified number and returns this Sankey generator. If *padding* is not specified, returns the current node padding, which defaults to 8.
<a name="sankey_extent" href="#sankey_extent">#</a> <i>sankey</i>.<b>extent</b>([<i>extent</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L84 "Source")
If *extent* is specified, sets the extent of the Sankey layout to the specified bounds and returns the layout. The *extent* bounds are specified as an array \[\[<i>x0</i>, <i>y0</i>\], \[<i>x1</i>, <i>y1</i>\]\], where *x0* is the left side of the extent, *y0* is the top, *x1* is the right and *y1* is the bottom. If *extent* is not specified, returns the current extent which defaults to null.
<a name="sankey_size" href="#sankey_size">#</a> <i>sankey</i>.<b>size</b>([<i>size</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L80 "Source")
An alias for [*sankey*.extent](#sankey_extent) where the minimum *x* and *y* of the extent are ⟨0,0⟩. Equivalent to:
```js
function nodes() {
return [];
}
sankey.extent([[0, 0], size]);
```
<a name="sankey_links" href="#sankey_links">#</a> <i>sankey</i>.<b>links</b>([<i>links</i>])
<a name="sankey_iterations" href="#sankey_iterations">#</a> <i>sankey</i>.<b>iterations</b>([<i>iterations</i>]) [<>](https://github.com/d3/d3-sankey/blob/master/src/sankey.js#L88 "Source")
If <i>links</i> is specified, sets the list of links to the specified function or array and returns this sankey generator. If <i>links</i> is not specified, returns the current accessor to the list of links, which defaults to:
If *iterations* is specified, sets the number of relaxation iterations when [generating the layout](#_sankey) and returns this Sankey generator. If *iterations* is not specified, returns the current number of relaxation iterations, which defaults to 32.
### Links
<a name="sankeyLinkHorizontal" href="#sankeyLinkHorizontal">#</a> d3.<i>sankeyLinkHorizontal</i>() [<>](https://github.com/d3/d3-sankey/blob/master/src/sankeyLinkHorizontal.js "Source")
Returns a [horizontal link shape](https://github.com/d3/d3-shape/blob/master/README.md#linkHorizontal) suitable for a Sankey diagram. The [source accessor](https://github.com/d3/d3-shape/blob/master/README.md#link_source) is defined as:
```js
function links() {
return [];
function source(d) {
return [d.source.x + d.source.dx, d.source.y + d.sy + d.dy / 2];
}
```
<a name="sankey_layout" href="#sankey_layout">#</a> <i>sankey</i>.<b>layout</b>([<i>iterations</i>])
The [target accessor](https://github.com/d3/d3-shape/blob/master/README.md#link_target) is defined as:
Returns the current accessor to the SVG layout object. Here <i>iterations</i> is the number of times the converging function <b>computeNodeDepths</b> is run.
```js
function target(d) {
return [d.target.x, d.target.y + d.ty + d.dy / 2];
}
```
<a name="sankey_relayout" href="#sankey_relayout">#</a> <i>sankey</i>.<b>relayout</b>()
For example, to render the links of a Sankey diagram in SVG, you might say:
Similar to <b>layout</b> but only recalculates the depth of links. Primarily used when a node is moved vertically.
```js
svg.append("g")
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-opacity", 0.2)
.selectAll("path")
.data(graph.links)
.enter().append("path")
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke-width", function(d) { return d.dy; });
```
import {ascending, min, sum} from "d3-array";
import {nest} from "d3-collection";
import {interpolateNumber} from "d3-interpolate";
import constant from "./constant";
function ascendingSourceDepth(a, b) {
return a.source.y - b.source.y;
}
function ascendingTargetDepth(a, b) {
return a.target.y - b.target.y;
}
function ascendingDepth(a, b) {
return a.y - b.y;
}
function value(d) {
return d.value;
}
function nodeCenter(node) {
return node.y + node.dy / 2;
}
function weightedSource(link) {
return nodeCenter(link.source) * link.value;
}
function weightedTarget(link) {
return nodeCenter(link.target) * link.value;
}
function defaultNodes(graph) {
return graph.nodes;
}
function defaultLinks(graph) {
return graph.links;
}
export default function() {
var sankey = {},
nodeWidth = 24,
nodePadding = 8,
size = [1, 1],
nodes = [],
links = [];
var x0 = 0, y0 = 0, x1 = 1, y1 = 1, // extent
dx = 24, // nodeWidth
py = 8, // nodePadding
nodes = defaultNodes,
links = defaultLinks,
iterations = 32;
function sankey() {
var graph = {nodes: nodes.apply(null, arguments), links: links.apply(null, arguments)};
computeNodeLinks(graph);
computeNodeValues(graph);
computeNodeBreadths(graph);
computeNodeDepths(graph, iterations);
computeLinkDepths(graph);
return graph;
}
sankey.update = function(graph) {
computeLinkDepths(graph);
return graph;
};
sankey.nodeWidth = function(_) {
if (!arguments.length) return nodeWidth;
nodeWidth = +_;
return sankey;
return arguments.length ? (dx = +_, sankey) : dx;
};
sankey.nodePadding = function(_) {
if (!arguments.length) return nodePadding;
nodePadding = +_;
return sankey;
return arguments.length ? (py = +_, sankey) : py;
};
sankey.nodes = function(_) {
if (!arguments.length) return nodes;
nodes = _;
return sankey;
return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes;
};
sankey.links = function(_) {
if (!arguments.length) return links;
links = _;
return sankey;
return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links;
};
sankey.size = function(_) {
if (!arguments.length) return size;
size = _;
return sankey;
return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0];
};
sankey.layout = function(iterations) {
computeNodeLinks();
computeNodeValues();
computeNodeBreadths();
computeNodeDepths(iterations);
computeLinkDepths();
return sankey;
sankey.extent = function(_) {
return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]];
};
sankey.relayout = function() {
computeLinkDepths();
return sankey;
sankey.iterations = function(_) {
return arguments.length ? (iterations = +_, sankey) : iterations;
};
sankey.link = function() {
var curvature = .5;
function link(d) {
var x0 = d.source.x + d.source.dx,
x1 = d.target.x,
xi = interpolateNumber(x0, x1),
x2 = xi(curvature),
x3 = xi(1 - curvature),
y0 = d.source.y + d.sy + d.dy / 2,
y1 = d.target.y + d.ty + d.dy / 2;
return "M" + x0 + "," + y0
+ "C" + x2 + "," + y0
+ " " + x3 + "," + y1
+ " " + x1 + "," + y1;
}
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) {
function computeNodeLinks(graph) {
graph.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];
graph.links.forEach(function(link) {
var source = link.source, target = link.target;
if (typeof source === "number") source = link.source = graph.nodes[link.source];
if (typeof target === "number") target = link.target = graph.nodes[link.target];
source.sourceLinks.push(link);

@@ -101,4 +109,4 @@ target.targetLinks.push(link);

// Compute the value (size) of each node by summing the associated links.
function computeNodeValues() {
nodes.forEach(function(node) {
function computeNodeValues(graph) {
graph.nodes.forEach(function(node) {
node.value = Math.max(

@@ -115,4 +123,4 @@ sum(node.sourceLinks, value),

// nodes with no outgoing links are assigned the maximum breadth.
function computeNodeBreadths() {
var remainingNodes = nodes,
function computeNodeBreadths(graph) {
var remainingNodes = graph.nodes,
nextNodes,

@@ -125,3 +133,3 @@ x = 0;

node.x = x;
node.dx = nodeWidth;
node.dx = dx;
node.sourceLinks.forEach(function(link) {

@@ -138,8 +146,8 @@ if (nextNodes.indexOf(link.target) < 0) {

//
moveSinksRight(x);
scaleNodeBreadths((size[0] - nodeWidth) / (x - 1));
moveSinksRight(graph, x);
scaleNodeBreadths(graph, (x1 - x0 - dx) / (x - 1));
}
// function moveSourcesRight() {
// nodes.forEach(function(node) {
// function moveSourcesRight(graph) {
// graph.nodes.forEach(function(node) {
// if (!node.targetLinks.length) {

@@ -151,4 +159,4 @@ // node.x = min(node.sourceLinks, function(d) { return d.target.x; }) - 1;

function moveSinksRight(x) {
nodes.forEach(function(node) {
function moveSinksRight(graph, x) {
graph.nodes.forEach(function(node) {
if (!node.sourceLinks.length) {

@@ -160,13 +168,13 @@ node.x = x - 1;

function scaleNodeBreadths(kx) {
nodes.forEach(function(node) {
node.x *= kx;
function scaleNodeBreadths(graph, kx) {
graph.nodes.forEach(function(node) {
node.x = x0 + node.x * kx;
});
}
function computeNodeDepths(iterations) {
function computeNodeDepths(graph) {
var nodesByBreadth = nest()
.key(function(d) { return d.x; })
.sortKeys(ascending)
.entries(nodes)
.entries(graph.nodes)
.map(function(d) { return d.values; });

@@ -177,4 +185,4 @@

resolveCollisions();
for (var alpha = 1; iterations > 0; --iterations) {
relaxRightToLeft(alpha *= .99);
for (var alpha = 1, n = iterations; n > 0; --n) {
relaxRightToLeft(alpha *= 0.99);
resolveCollisions();

@@ -187,3 +195,3 @@ relaxLeftToRight(alpha);

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

@@ -198,3 +206,3 @@

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

@@ -208,11 +216,6 @@ });

if (node.targetLinks.length) {
var y = sum(node.targetLinks, weightedSource) / sum(node.targetLinks, value);
node.y += (y - center(node)) * alpha;
node.y += (sum(node.targetLinks, weightedSource) / sum(node.targetLinks, value) - nodeCenter(node)) * alpha;
}
});
});
function weightedSource(link) {
return center(link.source) * link.value;
}
}

@@ -224,11 +227,6 @@

if (node.sourceLinks.length) {
var y = sum(node.sourceLinks, weightedTarget) / sum(node.sourceLinks, value);
node.y += (y - center(node)) * alpha;
node.y += (sum(node.sourceLinks, weightedTarget) / sum(node.sourceLinks, value) - nodeCenter(node)) * alpha;
}
});
});
function weightedTarget(link) {
return center(link.target) * link.value;
}
}

@@ -240,3 +238,3 @@

dy,
y0 = 0,
y = y0,
n = nodes.length,

@@ -249,11 +247,11 @@ i;

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

@@ -263,5 +261,5 @@ // Push any overlapping nodes back up.

node = nodes[i];
dy = node.y + node.dy + nodePadding - y0;
dy = node.y + node.dy + py - y;
if (dy > 0) node.y -= dy;
y0 = node.y;
y = node.y;
}

@@ -271,14 +269,10 @@ }

}
function ascendingDepth(a, b) {
return a.y - b.y;
}
}
function computeLinkDepths() {
nodes.forEach(function(node) {
function computeLinkDepths(graph) {
graph.nodes.forEach(function(node) {
node.sourceLinks.sort(ascendingTargetDepth);
node.targetLinks.sort(ascendingSourceDepth);
});
nodes.forEach(function(node) {
graph.nodes.forEach(function(node) {
var sy = 0, ty = 0;

@@ -294,21 +288,5 @@ node.sourceLinks.forEach(function(link) {

});
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;
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc