Daniel Quathamer
2 years ago
13 changed files with 711 additions and 572 deletions
@ -1,11 +1,10 @@ |
|||||||
5^CATEGORY^Kategorie-Achse^60^ |
5^CATEGORY^Kategorie-Achse^70^ |
||||||
6^MEASURE^Maß-Achse^70^ |
6^MEASURE^Maß-Achse^80^ |
||||||
10^LAYOUT^Größe und Ränder^10^ |
10^LAYOUT^Größe und Ränder^20^ |
||||||
11^STYLE^Formatierung^20^ |
11^STYLE^Formatierung^30^ |
||||||
12^MARKS^Grafik-Elemente^30^ |
12^MARKS^Grafik-Elemente^10^ |
||||||
15^SCALES^Skalen-Typen^40^ |
15^SCALES^Skalen-Typen^100^ |
||||||
17^AXIS^Achsen-Customizing^50^ |
18^TICKS_LABELS_X^X-Achsen-Beschriftung^60^ |
||||||
18^TICKS_LABELS_X^X Achsen-Beschriftung^^ |
19^TICKFORMATTING^Zahlen-, Datumsformat^90^ |
||||||
19^TICKFORMATTING^Zahlen-, Datumsformat^80^ |
20^COLOR^Farben^40^ |
||||||
20^COLOR^Farben^90^ |
21^TICKS_LABELS_Y^Y-Achsen-Beschriftung^50^ |
||||||
21^TICKS_LABELS_Y^Y Achsen-Beschriftung^^ |
|
||||||
|
@ -0,0 +1,425 @@ |
|||||||
|
// https://github.com/d3/d3-sankey v0.12.3 Copyright 2019 Mike Bostock
|
||||||
|
(function (global, factory) { |
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-array'), require('d3-shape')) : |
||||||
|
typeof define === 'function' && define.amd ? define(['exports', 'd3-array', 'd3-shape'], factory) : |
||||||
|
(global = global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3)); |
||||||
|
}(this, function (exports, d3Array, d3Shape) { 'use strict'; |
||||||
|
|
||||||
|
function targetDepth(d) { |
||||||
|
return d.target.depth; |
||||||
|
} |
||||||
|
|
||||||
|
function left(node) { |
||||||
|
return node.depth; |
||||||
|
} |
||||||
|
|
||||||
|
function right(node, n) { |
||||||
|
return n - 1 - node.height; |
||||||
|
} |
||||||
|
|
||||||
|
function justify(node, n) { |
||||||
|
return node.sourceLinks.length ? node.depth : n - 1; |
||||||
|
} |
||||||
|
|
||||||
|
function center(node) { |
||||||
|
return node.targetLinks.length ? node.depth |
||||||
|
: node.sourceLinks.length ? d3Array.min(node.sourceLinks, targetDepth) - 1 |
||||||
|
: 0; |
||||||
|
} |
||||||
|
|
||||||
|
function constant(x) { |
||||||
|
return function() { |
||||||
|
return x; |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
function ascendingSourceBreadth(a, b) { |
||||||
|
return ascendingBreadth(a.source, b.source) || a.index - b.index; |
||||||
|
} |
||||||
|
|
||||||
|
function ascendingTargetBreadth(a, b) { |
||||||
|
return ascendingBreadth(a.target, b.target) || a.index - b.index; |
||||||
|
} |
||||||
|
|
||||||
|
function ascendingBreadth(a, b) { |
||||||
|
return a.y0 - b.y0; |
||||||
|
} |
||||||
|
|
||||||
|
function value(d) { |
||||||
|
return d.value; |
||||||
|
} |
||||||
|
|
||||||
|
function defaultId(d) { |
||||||
|
return d.index; |
||||||
|
} |
||||||
|
|
||||||
|
function defaultNodes(graph) { |
||||||
|
return graph.nodes; |
||||||
|
} |
||||||
|
|
||||||
|
function defaultLinks(graph) { |
||||||
|
return graph.links; |
||||||
|
} |
||||||
|
|
||||||
|
function find(nodeById, id) { |
||||||
|
const node = nodeById.get(id); |
||||||
|
if (!node) throw new Error("missing: " + id); |
||||||
|
return node; |
||||||
|
} |
||||||
|
|
||||||
|
function computeLinkBreadths({nodes}) { |
||||||
|
for (const node of nodes) { |
||||||
|
let y0 = node.y0; |
||||||
|
let y1 = y0; |
||||||
|
for (const link of node.sourceLinks) { |
||||||
|
link.y0 = y0 + link.width / 2; |
||||||
|
y0 += link.width; |
||||||
|
} |
||||||
|
for (const link of node.targetLinks) { |
||||||
|
link.y1 = y1 + link.width / 2; |
||||||
|
y1 += link.width; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function Sankey() { |
||||||
|
let x0 = 0, y0 = 0, x1 = 1, y1 = 1; // extent
|
||||||
|
let dx = 24; // nodeWidth
|
||||||
|
let dy = 8, py; // nodePadding
|
||||||
|
let id = defaultId; |
||||||
|
let align = justify; |
||||||
|
let sort; |
||||||
|
let linkSort; |
||||||
|
let nodes = defaultNodes; |
||||||
|
let links = defaultLinks; |
||||||
|
let iterations = 6; |
||||||
|
|
||||||
|
function sankey() { |
||||||
|
const graph = {nodes: nodes.apply(null, arguments), links: links.apply(null, arguments)}; |
||||||
|
computeNodeLinks(graph); |
||||||
|
computeNodeValues(graph); |
||||||
|
computeNodeDepths(graph); |
||||||
|
computeNodeHeights(graph); |
||||||
|
computeNodeBreadths(graph); |
||||||
|
computeLinkBreadths(graph); |
||||||
|
return graph; |
||||||
|
} |
||||||
|
|
||||||
|
sankey.update = function(graph) { |
||||||
|
computeLinkBreadths(graph); |
||||||
|
return graph; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.nodeId = function(_) { |
||||||
|
return arguments.length ? (id = typeof _ === "function" ? _ : constant(_), sankey) : id; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.nodeAlign = function(_) { |
||||||
|
return arguments.length ? (align = typeof _ === "function" ? _ : constant(_), sankey) : align; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.nodeSort = function(_) { |
||||||
|
return arguments.length ? (sort = _, sankey) : sort; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.nodeWidth = function(_) { |
||||||
|
return arguments.length ? (dx = +_, sankey) : dx; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.nodePadding = function(_) { |
||||||
|
return arguments.length ? (dy = py = +_, sankey) : dy; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.nodes = function(_) { |
||||||
|
return arguments.length ? (nodes = typeof _ === "function" ? _ : constant(_), sankey) : nodes; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.links = function(_) { |
||||||
|
return arguments.length ? (links = typeof _ === "function" ? _ : constant(_), sankey) : links; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.linkSort = function(_) { |
||||||
|
return arguments.length ? (linkSort = _, sankey) : linkSort; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.size = function(_) { |
||||||
|
return arguments.length ? (x0 = y0 = 0, x1 = +_[0], y1 = +_[1], sankey) : [x1 - x0, y1 - y0]; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.extent = function(_) { |
||||||
|
return arguments.length ? (x0 = +_[0][0], x1 = +_[1][0], y0 = +_[0][1], y1 = +_[1][1], sankey) : [[x0, y0], [x1, y1]]; |
||||||
|
}; |
||||||
|
|
||||||
|
sankey.iterations = function(_) { |
||||||
|
return arguments.length ? (iterations = +_, sankey) : iterations; |
||||||
|
}; |
||||||
|
|
||||||
|
function computeNodeLinks({nodes, links}) { |
||||||
|
for (const [i, node] of nodes.entries()) { |
||||||
|
node.index = i; |
||||||
|
node.sourceLinks = []; |
||||||
|
node.targetLinks = []; |
||||||
|
} |
||||||
|
const nodeById = new Map(nodes.map((d, i) => [id(d, i, nodes), d])); |
||||||
|
for (const [i, link] of links.entries()) { |
||||||
|
link.index = i; |
||||||
|
let {source, target} = link; |
||||||
|
if (typeof source !== "object") source = link.source = find(nodeById, source); |
||||||
|
if (typeof target !== "object") target = link.target = find(nodeById, target); |
||||||
|
source.sourceLinks.push(link); |
||||||
|
target.targetLinks.push(link); |
||||||
|
} |
||||||
|
if (linkSort != null) { |
||||||
|
for (const {sourceLinks, targetLinks} of nodes) { |
||||||
|
sourceLinks.sort(linkSort); |
||||||
|
targetLinks.sort(linkSort); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function computeNodeValues({nodes}) { |
||||||
|
for (const node of nodes) { |
||||||
|
node.value = node.fixedValue === undefined |
||||||
|
? Math.max(d3Array.sum(node.sourceLinks, value), d3Array.sum(node.targetLinks, value)) |
||||||
|
: node.fixedValue; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function computeNodeDepths({nodes}) { |
||||||
|
const n = nodes.length; |
||||||
|
let current = new Set(nodes); |
||||||
|
let next = new Set; |
||||||
|
let x = 0; |
||||||
|
while (current.size) { |
||||||
|
for (const node of current) { |
||||||
|
node.depth = x; |
||||||
|
for (const {target} of node.sourceLinks) { |
||||||
|
next.add(target); |
||||||
|
} |
||||||
|
} |
||||||
|
if (++x > n) throw new Error("circular link"); |
||||||
|
current = next; |
||||||
|
next = new Set; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function computeNodeHeights({nodes}) { |
||||||
|
const n = nodes.length; |
||||||
|
let current = new Set(nodes); |
||||||
|
let next = new Set; |
||||||
|
let x = 0; |
||||||
|
while (current.size) { |
||||||
|
for (const node of current) { |
||||||
|
node.height = x; |
||||||
|
for (const {source} of node.targetLinks) { |
||||||
|
next.add(source); |
||||||
|
} |
||||||
|
} |
||||||
|
if (++x > n) throw new Error("circular link"); |
||||||
|
current = next; |
||||||
|
next = new Set; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function computeNodeLayers({nodes}) { |
||||||
|
const x = d3Array.max(nodes, d => d.depth) + 1; |
||||||
|
const kx = (x1 - x0 - dx) / (x - 1); |
||||||
|
const columns = new Array(x); |
||||||
|
for (const node of nodes) { |
||||||
|
const i = Math.max(0, Math.min(x - 1, Math.floor(align.call(null, node, x)))); |
||||||
|
node.layer = i; |
||||||
|
node.x0 = x0 + i * kx; |
||||||
|
node.x1 = node.x0 + dx; |
||||||
|
if (columns[i]) columns[i].push(node); |
||||||
|
else columns[i] = [node]; |
||||||
|
} |
||||||
|
if (sort) for (const column of columns) { |
||||||
|
column.sort(sort); |
||||||
|
} |
||||||
|
return columns; |
||||||
|
} |
||||||
|
|
||||||
|
function initializeNodeBreadths(columns) { |
||||||
|
const ky = d3Array.min(columns, c => (y1 - y0 - (c.length - 1) * py) / d3Array.sum(c, value)); |
||||||
|
for (const nodes of columns) { |
||||||
|
let y = y0; |
||||||
|
for (const node of nodes) { |
||||||
|
node.y0 = y; |
||||||
|
node.y1 = y + node.value * ky; |
||||||
|
y = node.y1 + py; |
||||||
|
for (const link of node.sourceLinks) { |
||||||
|
link.width = link.value * ky; |
||||||
|
} |
||||||
|
} |
||||||
|
y = (y1 - y + py) / (nodes.length + 1); |
||||||
|
for (let i = 0; i < nodes.length; ++i) { |
||||||
|
const node = nodes[i]; |
||||||
|
node.y0 += y * (i + 1); |
||||||
|
node.y1 += y * (i + 1); |
||||||
|
} |
||||||
|
reorderLinks(nodes); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function computeNodeBreadths(graph) { |
||||||
|
const columns = computeNodeLayers(graph); |
||||||
|
py = Math.min(dy, (y1 - y0) / (d3Array.max(columns, c => c.length) - 1)); |
||||||
|
initializeNodeBreadths(columns); |
||||||
|
for (let i = 0; i < iterations; ++i) { |
||||||
|
const alpha = Math.pow(0.99, i); |
||||||
|
const beta = Math.max(1 - alpha, (i + 1) / iterations); |
||||||
|
relaxRightToLeft(columns, alpha, beta); |
||||||
|
relaxLeftToRight(columns, alpha, beta); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Reposition each node based on its incoming (target) links.
|
||||||
|
function relaxLeftToRight(columns, alpha, beta) { |
||||||
|
for (let i = 1, n = columns.length; i < n; ++i) { |
||||||
|
const column = columns[i]; |
||||||
|
for (const target of column) { |
||||||
|
let y = 0; |
||||||
|
let w = 0; |
||||||
|
for (const {source, value} of target.targetLinks) { |
||||||
|
let v = value * (target.layer - source.layer); |
||||||
|
y += targetTop(source, target) * v; |
||||||
|
w += v; |
||||||
|
} |
||||||
|
if (!(w > 0)) continue; |
||||||
|
let dy = (y / w - target.y0) * alpha; |
||||||
|
target.y0 += dy; |
||||||
|
target.y1 += dy; |
||||||
|
reorderNodeLinks(target); |
||||||
|
} |
||||||
|
if (sort === undefined) column.sort(ascendingBreadth); |
||||||
|
resolveCollisions(column, beta); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Reposition each node based on its outgoing (source) links.
|
||||||
|
function relaxRightToLeft(columns, alpha, beta) { |
||||||
|
for (let n = columns.length, i = n - 2; i >= 0; --i) { |
||||||
|
const column = columns[i]; |
||||||
|
for (const source of column) { |
||||||
|
let y = 0; |
||||||
|
let w = 0; |
||||||
|
for (const {target, value} of source.sourceLinks) { |
||||||
|
let v = value * (target.layer - source.layer); |
||||||
|
y += sourceTop(source, target) * v; |
||||||
|
w += v; |
||||||
|
} |
||||||
|
if (!(w > 0)) continue; |
||||||
|
let dy = (y / w - source.y0) * alpha; |
||||||
|
source.y0 += dy; |
||||||
|
source.y1 += dy; |
||||||
|
reorderNodeLinks(source); |
||||||
|
} |
||||||
|
if (sort === undefined) column.sort(ascendingBreadth); |
||||||
|
resolveCollisions(column, beta); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function resolveCollisions(nodes, alpha) { |
||||||
|
const i = nodes.length >> 1; |
||||||
|
const subject = nodes[i]; |
||||||
|
resolveCollisionsBottomToTop(nodes, subject.y0 - py, i - 1, alpha); |
||||||
|
resolveCollisionsTopToBottom(nodes, subject.y1 + py, i + 1, alpha); |
||||||
|
resolveCollisionsBottomToTop(nodes, y1, nodes.length - 1, alpha); |
||||||
|
resolveCollisionsTopToBottom(nodes, y0, 0, alpha); |
||||||
|
} |
||||||
|
|
||||||
|
// Push any overlapping nodes down.
|
||||||
|
function resolveCollisionsTopToBottom(nodes, y, i, alpha) { |
||||||
|
for (; i < nodes.length; ++i) { |
||||||
|
const node = nodes[i]; |
||||||
|
const dy = (y - node.y0) * alpha; |
||||||
|
if (dy > 1e-6) node.y0 += dy, node.y1 += dy; |
||||||
|
y = node.y1 + py; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Push any overlapping nodes up.
|
||||||
|
function resolveCollisionsBottomToTop(nodes, y, i, alpha) { |
||||||
|
for (; i >= 0; --i) { |
||||||
|
const node = nodes[i]; |
||||||
|
const dy = (node.y1 - y) * alpha; |
||||||
|
if (dy > 1e-6) node.y0 -= dy, node.y1 -= dy; |
||||||
|
y = node.y0 - py; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function reorderNodeLinks({sourceLinks, targetLinks}) { |
||||||
|
if (linkSort === undefined) { |
||||||
|
for (const {source: {sourceLinks}} of targetLinks) { |
||||||
|
sourceLinks.sort(ascendingTargetBreadth); |
||||||
|
} |
||||||
|
for (const {target: {targetLinks}} of sourceLinks) { |
||||||
|
targetLinks.sort(ascendingSourceBreadth); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function reorderLinks(nodes) { |
||||||
|
if (linkSort === undefined) { |
||||||
|
for (const {sourceLinks, targetLinks} of nodes) { |
||||||
|
sourceLinks.sort(ascendingTargetBreadth); |
||||||
|
targetLinks.sort(ascendingSourceBreadth); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the target.y0 that would produce an ideal link from source to target.
|
||||||
|
function targetTop(source, target) { |
||||||
|
let y = source.y0 - (source.sourceLinks.length - 1) * py / 2; |
||||||
|
for (const {target: node, width} of source.sourceLinks) { |
||||||
|
if (node === target) break; |
||||||
|
y += width + py; |
||||||
|
} |
||||||
|
for (const {source: node, width} of target.targetLinks) { |
||||||
|
if (node === source) break; |
||||||
|
y -= width; |
||||||
|
} |
||||||
|
return y; |
||||||
|
} |
||||||
|
|
||||||
|
// Returns the source.y0 that would produce an ideal link from source to target.
|
||||||
|
function sourceTop(source, target) { |
||||||
|
let y = target.y0 - (target.targetLinks.length - 1) * py / 2; |
||||||
|
for (const {source: node, width} of target.targetLinks) { |
||||||
|
if (node === source) break; |
||||||
|
y += width + py; |
||||||
|
} |
||||||
|
for (const {target: node, width} of source.sourceLinks) { |
||||||
|
if (node === target) break; |
||||||
|
y -= width; |
||||||
|
} |
||||||
|
return y; |
||||||
|
} |
||||||
|
|
||||||
|
return sankey; |
||||||
|
} |
||||||
|
|
||||||
|
function horizontalSource(d) { |
||||||
|
return [d.source.x1, d.y0]; |
||||||
|
} |
||||||
|
|
||||||
|
function horizontalTarget(d) { |
||||||
|
return [d.target.x0, d.y1]; |
||||||
|
} |
||||||
|
|
||||||
|
function sankeyLinkHorizontal() { |
||||||
|
return d3Shape.linkHorizontal() |
||||||
|
.source(horizontalSource) |
||||||
|
.target(horizontalTarget); |
||||||
|
} |
||||||
|
|
||||||
|
exports.sankey = Sankey; |
||||||
|
exports.sankeyCenter = center; |
||||||
|
exports.sankeyJustify = justify; |
||||||
|
exports.sankeyLeft = left; |
||||||
|
exports.sankeyLinkHorizontal = sankeyLinkHorizontal; |
||||||
|
exports.sankeyRight = right; |
||||||
|
|
||||||
|
Object.defineProperty(exports, '__esModule', { value: true }); |
||||||
|
|
||||||
|
})); |
File diff suppressed because one or more lines are too long
@ -1,518 +0,0 @@ |
|||||||
|
|
||||||
|
|
||||||
function makeSankey(targetDiv,data,metaData,chartElem) |
|
||||||
{ |
|
||||||
var measureCaption = getMeasureCaption(chartElem,metaData); //"Studierende";
|
|
||||||
var captionEmptyTarget="Kein Master (eigene HS)"; |
|
||||||
var margin = {top: 10, right: 10, bottom: 10, left: 10}, |
|
||||||
width = 1200 - margin.left - margin.right, |
|
||||||
height = 800 - margin.top - margin.bottom; |
|
||||||
|
|
||||||
var formatNumber = d3.format(",.0f"), // zero decimal places
|
|
||||||
format = function(d) { return measureCaption+": "+formatNumber(d); }, |
|
||||||
color = d3.scale.category20(); |
|
||||||
|
|
||||||
// append the svg canvas to the page
|
|
||||||
var clearCanvas=document.getElementById(targetDiv); |
|
||||||
while (clearCanvas.firstChild) { |
|
||||||
clearCanvas.removeChild(clearCanvas.firstChild); |
|
||||||
} |
|
||||||
//targetDiv
|
|
||||||
var svg = d3.select("#"+targetDiv).append("svg") |
|
||||||
.attr("width", width + margin.left + margin.right) |
|
||||||
.attr("height", height + margin.top + margin.bottom) |
|
||||||
.append("g") |
|
||||||
.attr("transform",
|
|
||||||
"translate(" + margin.left + "," + margin.top + ")"); |
|
||||||
|
|
||||||
// Set the sankey diagram properties
|
|
||||||
var sankey = d3.sankey() |
|
||||||
.nodeWidth(36) |
|
||||||
.nodePadding(40) |
|
||||||
.size([width, height]); |
|
||||||
|
|
||||||
var path = sankey.link(); |
|
||||||
|
|
||||||
// load the data
|
|
||||||
var sNodes=getSankeyNodes(data,captionEmptyTarget); //graph.nodes;
|
|
||||||
var sLinks=getSankeyLinks(sNodes,data); //graph.links;
|
|
||||||
sankey |
|
||||||
.nodes(sNodes) |
|
||||||
.links(sLinks) |
|
||||||
.layout(32); |
|
||||||
|
|
||||||
// add in the links
|
|
||||||
var link = svg.append("g").selectAll(".link") |
|
||||||
.data(sLinks) |
|
||||||
.enter().append("path") |
|
||||||
.attr("class", "link") |
|
||||||
.attr("d", path) |
|
||||||
.style("stroke-width", function(d) { return Math.max(1, d.dy); }) |
|
||||||
.sort(function(a, b) { return b.dy - a.dy; }); |
|
||||||
|
|
||||||
// add the link titles
|
|
||||||
link.append("title") |
|
||||||
.text(function(d) { |
|
||||||
return d.source.name + " → " +
|
|
||||||
d.target.name + "\n" + format(d.value); }); |
|
||||||
|
|
||||||
// add in the nodes
|
|
||||||
var node = svg.append("g").selectAll(".node") |
|
||||||
.data(sNodes) |
|
||||||
.enter().append("g") |
|
||||||
.attr("class", "node") |
|
||||||
.attr("transform", function(d) {
|
|
||||||
return "translate(" + d.x + "," + d.y + ")"; }) |
|
||||||
/*.call(d3.behavior.drag() |
|
||||||
.origin(function(d) { return d; }) |
|
||||||
.on("dragstart", function() {
|
|
||||||
this.parentNode.appendChild(this); }) |
|
||||||
.on("drag", dragmove) |
|
||||||
)*/ |
|
||||||
; |
|
||||||
|
|
||||||
// add the rectangles for the nodes
|
|
||||||
node.append("rect") |
|
||||||
.attr("height", function(d) { return d.dy; }) |
|
||||||
.attr("width", sankey.nodeWidth()) |
|
||||||
.style("fill", function(d) {
|
|
||||||
//return d.color = color(d.name.replace(/ .*/, ""));
|
|
||||||
//return d3.scaleLinear().domain([0,3,10]).range(["blue","white","red"]);
|
|
||||||
var r = Math.floor(Math.random() * 255); |
|
||||||
var g = Math.floor(Math.random() * 255); |
|
||||||
var b = Math.floor(Math.random() * 255); |
|
||||||
var col = "rgb(" + r + "," + g + "," + b + ")"; |
|
||||||
return d.color = col; //"red";
|
|
||||||
}) |
|
||||||
.style("stroke", function(d) {
|
|
||||||
return d3.rgb(d.color).darker(2); }) |
|
||||||
.append("title") |
|
||||||
.text(function(d) {
|
|
||||||
return d.name + "\n" + format(d.value); }); |
|
||||||
|
|
||||||
// add in the title for the nodes
|
|
||||||
node.append("text") |
|
||||||
.attr("x", -6) |
|
||||||
.attr("y", function(d) { return d.dy / 2; }) |
|
||||||
.attr("dy", ".35em") |
|
||||||
.attr("text-anchor", "end") |
|
||||||
.attr("transform", null) |
|
||||||
.text(function(d) { return d.name; }) |
|
||||||
.filter(function(d) { return d.x < width / 2; }) |
|
||||||
.attr("x", 6 + sankey.nodeWidth()) |
|
||||||
.attr("text-anchor", "start"); |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function getSankeyNodes(data,captionEmptyTarget) |
|
||||||
{ |
|
||||||
var myNodes=[]; |
|
||||||
|
|
||||||
var distinctSource = []; |
|
||||||
var zs=""; |
|
||||||
for (var i = 0; i < data.length; i++) |
|
||||||
{ |
|
||||||
if(zs.indexOf("_" + data[i].dimension1 +"_")<0) |
|
||||||
{ |
|
||||||
distinctSource.push(data[i].dimension1); |
|
||||||
zs+="_" + data[i].dimension1 +"_"; |
|
||||||
} |
|
||||||
} |
|
||||||
for (var i = 0; i < data.length; i++) |
|
||||||
{ |
|
||||||
if(zs.indexOf("_" + data[i].dimension2 +"_")<0) |
|
||||||
{ |
|
||||||
distinctSource.push(data[i].dimension2); |
|
||||||
zs+="_" + data[i].dimension2 +"_"; |
|
||||||
} |
|
||||||
} |
|
||||||
for (var i = 0; i < distinctSource.length; i++) |
|
||||||
{ |
|
||||||
myNodes[i] = { "node": i, "name": distinctSource[i] }; |
|
||||||
console.log("Abschluss: "+distinctSource[i]); |
|
||||||
} |
|
||||||
//Kein Abschluss:
|
|
||||||
myNodes[i] = { "node": i, "name": captionEmptyTarget }; |
|
||||||
return myNodes; |
|
||||||
} |
|
||||||
|
|
||||||
function getSankeyLinks(myNodes,data) |
|
||||||
{ |
|
||||||
var myLinks=[]; |
|
||||||
|
|
||||||
var linkIndex=0; |
|
||||||
for (var i = 0; i < data.length; i++) |
|
||||||
{ |
|
||||||
if(data[i].dimension2 !="") |
|
||||||
{ |
|
||||||
myLinks[linkIndex]={ "source": getSankeyNodeIndex(myNodes,data[i].dimension1), |
|
||||||
"target": getSankeyNodeIndex(myNodes,data[i].dimension2), "value":data[i].measure1 }; |
|
||||||
linkIndex++; |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
myLinks[linkIndex]={ "source": getSankeyNodeIndex(myNodes,data[i].dimension1), |
|
||||||
"target": myNodes.length-1, "value":data[i].measure1 }; |
|
||||||
linkIndex++; |
|
||||||
}
|
|
||||||
} |
|
||||||
return myLinks; |
|
||||||
} |
|
||||||
|
|
||||||
function getSankeyNodeIndex(myNodes,name) |
|
||||||
{ |
|
||||||
var myIndex=0; |
|
||||||
for (var i = 0; i < myNodes.length; i++) |
|
||||||
{ |
|
||||||
if(myNodes[i].name==name) |
|
||||||
myIndex=i |
|
||||||
} |
|
||||||
return myIndex; |
|
||||||
} |
|
||||||
|
|
||||||
function getMeasureCaption(chartElem,metaData) |
|
||||||
{ |
|
||||||
var measureCaption=""; |
|
||||||
for(var k=0;k<chartElem.elementTypeProperties.length;k++) |
|
||||||
{ |
|
||||||
if(chartElem.elementTypeProperties[k].propertyValue!="") |
|
||||||
{ |
|
||||||
switch (chartElem.elementTypeProperties[k].vizTypePropertyUniquename) |
|
||||||
{ |
|
||||||
case "viz_measure1": |
|
||||||
measure1=chartElem.elementTypeProperties[k].propertyValue; |
|
||||||
measureCaption=getMetadataOfVizTypeProperty(metaData,measure1); |
|
||||||
if(measureCaption=="") |
|
||||||
measureCaption=measure1; |
|
||||||
break; |
|
||||||
default: |
|
||||||
break; |
|
||||||
|
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
return measureCaption; |
|
||||||
} |
|
||||||
function getMetadataOfVizTypeProperty(metaData,vizTypePropertyUniquename) |
|
||||||
{ |
|
||||||
var caption=""; |
|
||||||
for(var k=0;k<metaData.length;k++) |
|
||||||
{ |
|
||||||
if(metaData[k].colname==vizTypePropertyUniquename) |
|
||||||
{ |
|
||||||
caption=metaData[k].colcaption; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
} |
|
||||||
return caption; |
|
||||||
} |
|
||||||
// the function for moving the nodes for sankey
|
|
||||||
function dragmove(d) { |
|
||||||
d3.select(this).attr("transform",
|
|
||||||
"translate(" + d.x + "," + ( |
|
||||||
d.y = Math.max(0, Math.min(d3.select(this).attr("height") - d.dy, d3.event.y)) |
|
||||||
) + ")"); |
|
||||||
sankey.relayout(); |
|
||||||
link.attr("d", path); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
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.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 + 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) { |
|
||||||
node.sourceLinks = []; |
|
||||||
node.targetLinks = []; |
|
||||||
}); |
|
||||||
links.forEach(function(link) { |
|
||||||
var source = link.source, |
|
||||||
target = link.target; |
|
||||||
console.log("source: "+source+" TARGET: "+target); |
|
||||||
if (typeof source === "number" || typeof source === "string") source = link.source = nodes[link.source]; |
|
||||||
if (typeof target === "number" || typeof target === "string") 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) { |
|
||||||
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; |
|
||||||
}; |
|
Loading…
Reference in new issue