Solution is not finished and needs more understanding. Trying to position the nodes horizontally in a sankey diagram according to data. Hard coding these values for now at the bottom:
graph.links[3].source.x = 200; graph.links[7].source.x = 100; graph.links[7].target.x = 500; graph.links[5].target.x = 800;
then updating the links and rects. Oddly, the rects know to position themselves automatically. I would have thought I'd need to have changed the data first:
graph.nodes[5].dx = 800 etc //but this isn't necessary it seems, not sure yet why
I've disabled drag functionality too as the labels get all jumbled up otherwise
added back drag functionality, had to comment out this line which was affecting node order //.on("dragstart", function() { this.parentNode.appendChild(this); })xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<title>Applications drafts</title>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="sankey.js"></script>
<style>
.node rect {
cursor: move;
fill-opacity: .9;
shape-rendering: crispEdges;
}
.node text {
pointer-events: none;
text-shadow: 0 1px 0 #fff;
font-family: ChevinLight;
}
.link {
fill: none;
/* stroke: #4A0000;*/
stroke-opacity: .2;
}
.link:hover {
stroke-opacity: .8;
}
.toggle {
font-size: 20px;
font-family: arial;
}
</style>
</head>
<body>
<script type="text/javascript">
var margin = {top: 10, right: 10, bottom: 10, left: 10},
width = 1200 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
// Set the sankey diagram properties
var sankey = d3.sankey()
.nodeWidth(36)
.nodePadding(40)
.size([width, height]);
var path = sankey.link();
var timelineScale = d3.time.scale()
.domain([new Date(2014,01,01), new Date(2014,12,31)])
.range(["red", "blue"]);
d3.csv("data.csv",function(error,csv) {
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
csv.forEach(function(d) {
d.StartDate = parseDate(d.StartDate);
d.EndDate = parseDate(d.EndDate);
})
//set up graph data but empty
graph = {"nodes" : [], "links" : []};
csv.forEach(function (d) {
graph.nodes.push({ "name": d.SourceApp});
graph.nodes.push({ "name": d.TargetApp});
graph.links.push({ "source": d.SourceApp,
"target": d.TargetApp ,
"project": d.Name,
"value":4,
"linkCol":d.Status});
});
// return only the distinct / unique nodes - equivalent of a dedupe
graph.nodes = d3.keys(d3.nest()
.key(function (d) { return d.name ; })
.map(graph.nodes));
// loop through each link replacing the text with its index from node
graph.links.forEach(function (d, i) {
graph.links[i].source = graph.nodes.indexOf(graph.links[i].source);
graph.links[i].target = graph.nodes.indexOf(graph.links[i].target);
});
//now loop through each node to make nodes an array of objects
// rather than an array of strings
graph.nodes.forEach(function (d, i) {
graph.nodes[i] = { "name": d };
});
graph.nodes.forEach(function (d) {
if (d.name.charAt(0) == '_') {d.col = "#E9E9E9"} else {d.col = "blue"};
if (d.name.charAt(0) == '_') {d.name = ""};
})
console.log(graph.nodes);
viz(graph);
})
function viz(incoming) {
sankey.nodes(incoming.nodes).links(incoming.links).layout(32);
// append the svg canvas to the page
var svg = d3.select('body').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 + ")");
// add in the links
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke",function(d) {return d.linkCol})
.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.project + ":" + d.source.name + " → " +
d.target.name ; });
var drag = d3.behavior.drag()
.origin(function(d) { return d; })
//.on("dragstart", function() { this.parentNode.appendChild(this); })
.on("drag", dragmove);
// add in the nodes
var node = svg.append("g").selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", function(d){return d.name + " node"})
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; })
.call(drag);
// 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.col });
// 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");
// the function for moving the nodes
function dragmove(d) {
d3.select(this).attr("transform",
"translate(" + (
d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
) + "," + (
d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
) + ")");
sankey.relayout();
link.attr("d", path);
}
var button = d3.select('body').append("p").html("Click me to see the nodes move to a new position").attr("class","toggle");
button.on("click",test);
function test() {
// var candidate = d3.select(".p2");
// candidate.attr("transform",
// "translate(" + (
// this.x = 200
// ) + "," + (
// this.y = 200
// ) + ")");
// sankey.relayout();
// link.attr("d", path);
//console.log(link.attr("d"));
//link data needs to change before we'll see a change in path
//manually set x position for links, node position data seem to adjust automatically...
graph.links[3].source.x = 200;
graph.links[7].source.x = 100;
graph.links[7].target.x = 500;
graph.links[5].target.x = 800;
// add in the nodes
d3.selectAll(".node")
.data(graph.nodes)
.transition()
.duration(2000)
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
d3.selectAll(".link")
.data(graph.links)
.transition()
.duration(2000)
.attr("d", path)
.sort(function(a, b) { return b.dy - a.dy; });
//console.log(link.style("stroke"));
//console.log(path);
}
};
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js