Commit 513e0580 authored by Matija Obreza's avatar Matija Obreza

Use origin, sink and code names instead of indices

parent 2b164824
......@@ -28,7 +28,7 @@ const format = (d) => {
return `${f(d)}`;
}
const sankeyx = ({ nodes, transfers }) => {
const sankeyx = ({ origin, sink, transfers }) => {
const sankey = d3.sankey()
.nodeWidth(15)
.nodePadding(10)
......@@ -36,7 +36,7 @@ const sankeyx = ({ nodes, transfers }) => {
[1, 1],
[width - 1, height - 5]
]);
return sankey({ nodes, transfers });
return sankey({ origin, sink, transfers });
}
const chart = (data) => {
......@@ -56,7 +56,7 @@ const chart = (data) => {
} = sankeyx(data);
svg.append("g")
.attr("stroke", "#000")
.attr("stroke", "#909090")
.selectAll("rect")
.data(nodes)
.join("rect")
......@@ -142,25 +142,22 @@ const chart = (data) => {
return svg.node();
}
// TODO Consider using codes in target array
// TODO and then specify origin and end node names as MEX002 and NOR051
const data = {
nodes: [
{ name: "MEX002" },
{ name: "COL003" },
{ name: "USA996" },
{ name: "SOM000" },
{ name: "NOR051" }
],
origin: "MEX002",
sink: "NOR051",
transfers: [
{ value: 50, target: [ 4 ] },
{ value: 30, target: [ 1, 3, 4 ]},
{ value: 40, target: [ 1, 4 ]},
{ value: 100, target: [ 1, 2, 3, 4 ]},
{ value: 30, target: [ 2, 4 ]},
{ value: 25, target: [ 3 ] },
{ value: 50, target: [ 1, 2, 4 ]},
{ value: 40, target: [ 2 ] },
{ value: 10, target: [ 3, 4 ]},
{ value: 50, target: [ ] },
{ value: 50, target: [ "POR001", "NOR051" ] },
{ value: 30, target: [ "COL003", "SOM001", "POR001" ]},
{ value: 40, target: [ "COL003", "NOR052" ]},
{ value: 100, target: [ "COL003", "USA996", "SOM000", "POR001" ]},
{ value: 30, target: [ "USA996", "NOR052" ]},
{ value: 25, target: [ "SOM000" ] },
{ value: 50, target: [ "COL003", "USA996", "NOR051" ]},
{ value: 40, target: [ "USA996" ] },
{ value: 10, target: [ "SOM000", "NOR051" ]},
{ value: 500, target: [ ] },
]
};
......
......@@ -36,7 +36,7 @@ All rights reserved.
import {ascending, min, sum} from "d3-array";
import {map, nest} from "d3-collection";
import {justify} from "d3-sankey/src/align";
import {left, justify} from "d3-sankey/src/align";
import constant from "d3-sankey/src/constant";
import { notDeepEqual } from "assert";
......@@ -72,32 +72,46 @@ function defaultId(d) {
return d.index;
}
function defaultNodes(graph) {
return graph.nodes;
function defaultName(d) {
return d.name;
}
function defaultTransfers(graph) {
return graph.transfers;
}
function defaultOrigin(graph) {
return graph.origin;
}
function defaultSink(graph) {
return graph.sink;
}
function find(nodeById, id) {
var node = nodeById.get(id);
if (!node) throw new Error("missing: " + id);
// if (!node) throw new Error("missing: " + id);
return node;
}
function customJustify(node, n) {
return node.depth;
}
export default function() {
var x0 = 0, y0 = 0, x1 = 1, y1 = 1, // extent
dx = 24, // nodeWidth
py = 8, // nodePadding
id = defaultId,
align = justify,
nodes = defaultNodes,
name = defaultName,
align = left, // customJustify, //justify,
transfers = defaultTransfers,
origin = defaultOrigin,
sink = defaultSink,
iterations = 32;
function sankey() {
const graph = {nodes: nodes.apply(null, arguments), transfers: transfers.apply(null, arguments)};
const graph = { origin: origin.apply(null, arguments), sink: sink.apply(null, arguments), transfers: transfers.apply(null, arguments)};
console.log(`Processing graph`, graph);
computeNodeLinks(graph);
computeNodeValues(graph);
......@@ -148,32 +162,58 @@ export default function() {
return arguments.length ? (iterations = +_, sankey) : iterations;
};
function addNode(name, depth) {
const node = {
name: name,
depth: depth,
sourceLinks: [],
targetLinks: [],
};
console.log(`Adding node ${name} depth=${depth}`, node);
return node;
}
// Populate the sourceLinks and targetLinks for each node.
// Also, if the source and target are not objects, assume they are indices.
function computeNodeLinks(graph) {
graph.nodes.forEach(function(node, i) {
node.index = i;
node.sourceLinks = [];
node.targetLinks = [];
graph.nodes = [];
const sourceNode = addNode(graph.origin, 0)
const intermediateNode = addNode(``, 1);
intermediateNode.intermediate = true;
const endNode = addNode(graph.sink, 2);
graph.nodes.push(sourceNode);
graph.nodes.push(intermediateNode);
graph.nodes.push(endNode);
var nodeByName = map(graph.nodes, name);
graph.transfers.forEach((transfer, i) => {
transfer.target.forEach((target) => {
var targetNode = find(nodeByName, target);
if (targetNode === undefined) {
graph.nodes.push(addNode(target, 1));
nodeByName = map(graph.nodes, name);
} else {
console.log(`Found existing target node ${target}`, targetNode);
}
})
});
const endNodeIndex = graph.nodes.length - 1;
var nodeById = map(graph.nodes, id);
const endNode = find(nodeById, endNodeIndex);
console.log(`End node is`, endNode);
graph.links = [];
graph.transfers.forEach((transfer, i) => {
console.log(`Transfer ${i}`, transfer);
const source = find(nodeById, 0);
const includesEndNode = transfer.target.find((target) => target === endNodeIndex) >= 0;
// console.log(`Has end node?`, transfer.target.find((target) => target === endNode.name));
const includesEndNode = transfer.target.find((target) => target === endNode.name) !== undefined;
console.log(`Includes end node? ${includesEndNode}`);
transfer.target.forEach((target, j) => {
console.log(`T ${i},${j} -> ${target}`);
if (typeof target !== "object") target = find(nodeById, target);
if (typeof target !== "object") target = find(nodeByName, target);
if (target !== endNode) {
const link = { value: transfer.value, source, target, cluster: i };
const link = { value: transfer.value, source: sourceNode, target, cluster: i };
// console.log(`Registering transfer`, link);
source.sourceLinks.push(link);
sourceNode.sourceLinks.push(link);
target.targetLinks.push(link);
graph.links.push(link);
if (includesEndNode && transfer.target.length > 1) {
......@@ -183,14 +223,22 @@ export default function() {
graph.links.push(link);
}
} else if (target === endNode && transfer.target.length === 1) {
const link = { value: transfer.value, source, target, cluster: i };
// console.log(`Registering transfer`, link);
source.sourceLinks.push(link);
target.targetLinks.push(link);
graph.links.push(link);
const link1 = { value: transfer.value, source: sourceNode, target: intermediateNode, cluster: i };
const link2 = { value: transfer.value, source: intermediateNode, target: endNode, cluster: i };
console.log(`Adding direct origin -> sink link`, link1, link2);
sourceNode.sourceLinks.push(link1);
intermediateNode.targetLinks.push(link1);
intermediateNode.value = transfer.value;
intermediateNode.sourceLinks.push(link2);
target.targetLinks.push(link2);
graph.links.push(link1);
graph.links.push(link2);
}
});
});
graph.nodes = graph.nodes.filter((node) => node.sourceLinks.length > 0 || node.targetLinks.length > 0);
console.log(`Processed graph`, graph);
// graph.links.forEach(function(link, i) {
// link.index = i;
......@@ -204,20 +252,21 @@ export default function() {
// Compute the value (size) of each node by summing the associated links.
function computeNodeValues(graph) {
var nodeById = map(graph.nodes, id);
const sourceNode = find(nodeById, 0);
var nodeByName = map(graph.nodes, name);
const sourceNode = find(nodeByName, graph.origin);
sourceNode.value = sum(graph.transfers, value);
console.log(`Source node value: ${sourceNode.value}`);
const endNodeIndex = graph.nodes.length - 1;
const endNode = find(nodeById, endNodeIndex);
endNode.value = sum(graph.transfers.filter((transfer) => transfer.target.find((target) => target === endNodeIndex) >= 0 ), value);
console.log(`End node[${endNodeIndex}] value: ${endNode.value}`);
const endNode = find(nodeByName, graph.sink);
if (endNode) {
endNode.value = sum(graph.transfers.filter((transfer) => transfer.target.find((target) => target === endNode.name)), value);
console.log(`End node["${endNode.name}"] value: ${endNode.value}`);
}
graph.transfers.forEach((transfer) => {
transfer.target.forEach((target, j) => {
if (target !== endNodeIndex) {
if (typeof target !== "object") target = find(nodeById, target);
if (!endNode || target !== endNode.name) {
if (typeof target !== "object") target = find(nodeByName, target);
console.log(`Updating node[${j}].value`);
target.value = sum(target.targetLinks, value);
}
......@@ -232,17 +281,9 @@ export default function() {
function computeNodeDepths(graph) {
var nodes, next, x;
var nodeById = map(graph.nodes, id);
const sourceNode = find(nodeById, 0);
const endNodeIndex = graph.nodes.length - 1;
const endNode = find(nodeById, endNodeIndex);
graph.nodes.forEach((node, i, l) => {
node.depth = i === 0 ? 0 : i === l.length - 1 ? 2 : 1;
});
for (nodes = graph.nodes, next = [], x = 0; nodes.length; ++x, nodes = next, next = []) {
nodes.forEach(function(node) {
console.log(`Node ${node.name} x=${x}`);
node.height = x;
node.targetLinks.forEach((link) => {
if (next.indexOf(link.source) < 0) {
......@@ -252,9 +293,19 @@ export default function() {
});
}
graph.nodes.forEach((node) => {
console.log(`Node ${node.name} h=${node.height} d=${node.depth}`);
// node.height = 2 - node.depth;
});
var kx = (x1 - x0 - dx) / (x - 1);
graph.nodes.forEach(function(node) {
console.log(`Node ${node.name} x=${x} kx=${kx} ${node.depth}`);
node.x1 = (node.x0 = x0 + Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x)))) * kx) + dx;
if (node.intermediate) {
// console.log(`Intermediate node x0=${node.x0} x1=${node.x1} diff=${node.x1 - node.x0}`);
node.x1 = node.x0;
}
});
}
......@@ -265,7 +316,7 @@ export default function() {
.entries(graph.nodes)
.map(function(d) { return d.values; });
// console.log(`Columns:`, columns);
console.log(`Columns:`, columns);
initializeNodeBreadth();
resolveCollisions();
......@@ -364,10 +415,15 @@ export default function() {
});
const sourcePositions = {};
const endPositions = {};
var nodeByName = map(graph.nodes, name);
const sourceNode = find(nodeByName, graph.origin);
const endNode = find(nodeByName, graph.sink);
graph.nodes.forEach((node, i) => {
var y0 = node.y0, y1 = y0;
node.sourceLinks.forEach((link) => {
if (i === 0) {
if (node === sourceNode) {
const clusterPos = sourcePositions[link.cluster];
// console.log(`Doing source link.y0 cluster=${link.cluster} pos=${clusterPos} on`, link);
if (clusterPos === undefined) {
......@@ -381,7 +437,7 @@ export default function() {
}
});
node.targetLinks.forEach(function(link) {
if (i === graph.nodes.length - 1) {
if (node === endNode) {
const clusterPos = endPositions[link.cluster];
// console.log(`Doing end link.y0 cluster=${link.cluster} pos=${clusterPos} on`, link);
if (clusterPos === undefined) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment