xxxxxxxxxx
<meta charset="utf-8">
<title>Uniform(height) Sankey</title>
<style>
.node rect {
cursor: move;
fill-opacity: .9;
shape-rendering: crispEdges;
}
.node text {
pointer-events: none;
text-shadow: 0 1px 0 #fff;
}
/*.link {
fill: none;
stroke: #000;
stroke-opacity: .2;
}*/
/*.link:hover {
stroke-opacity: .7;
}*/
</style>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
<script src="https://code.jquery.com/ui/1.11.4/jquery-ui.js"></script>
<script src="https://cdn.jsdelivr.net/npm/underscore@1.12.1/underscore-min.js"></script>
<script type="text/javascript" src="https://rawgithub.com/square/crossfilter/master/crossfilter.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="sankey.js"></script>
<!-- <script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
-->
<body>
<div id="vis"></div>
<input type="text" id="amount" style="border:0; color:#f6931f; font-weight:bold;">
<div id="slider" style="width:400px;"></div>
<script>
var margin = {
top: 10,
right: 50,
bottom: 10,
left: 50
},
width = 500- margin.left - margin.right,
height = 500- margin.top - margin.bottom;
var majorIDMap={};
var color = d3.scale.category20b();
// append the svg canvas to the page
var svg = d3.select("#vis").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(10)
.nodePadding(25)
.size([width, height]);
var path = d3.svg.diagonal()
.source(function(d) { return {"x":d.source.y, "y":d.source.x}; })
.target(function(d) { return {"x":d.target.y, "y":d.target.x}; })
// .source(function(d) {
// return {"x":d.source.y + d.source.dy / 2,
// "y":d.source.x + sankey.nodeWidth()/2};
// })
// .target(function(d) {
// return {"x":d.target.y + d.target.dy / 2,
// "y":d.target.x + sankey.nodeWidth()/2};
// })
.projection(function(d) { return [d.y, d.x]; });
// load the data (using the timelyportfolio csv method)
d3.csv("https://raw.githubusercontent.com/mydu/infovis_proj/master/data/student_flow_aggregated_file.csv", function(error, data) {
// d3.csv("idg.csv", function(error, data) {
extendAttr(data);
var cf = crossfilter(data);
var T12=cf.dimension(function(d){return "T1"+d["T1_Level2_id"]+"|"+"T2"+d["T2_Level2_id"];})
.group().reduceSum(function(d){return d.count;}).top(Infinity);
var T23=cf.dimension(function(d){return "T2"+d["T2_Level2_id"]+"|"+"T3"+d["T3_Level2_id"];})
.group().reduceSum(function(d){return d.count;}).top(Infinity);
//set up graph in same style as original example but empty
graph = {"nodes" : [], "links" : []};
_.each([T12,T23],function(data){
_.each(data,function(d){
graph.nodes.push({ "name": d.key.split("|")[0] });
graph.nodes.push({ "name": d.key.split("|")[1] });
graph.links.push({ "source": d.key.split("|")[0],
"target": d.key.split("|")[1],
"value": d.value });
})
})
// csv to linknode
// data.forEach(function (d) {
// graph.nodes.push({ "name": d.source });
// graph.nodes.push({ "name": d.target });
// graph.links.push({ "source": d.source,
// "target": d.target,
// "value": +d.value });
// });
// return only the distinct / unique nodes
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 nodes to make nodes an array of objects
// rather than an array of strings
graph.nodes.forEach(function (d, i) {
graph.nodes[i] = { "name": d };
});
sankey.nodes(graph.nodes)
.links(graph.links)
.layout(32);
//node scale
var maxVal_node=d3.max(_.pluck(graph.nodes,"value"));
var minVal_node=d3.min(_.pluck(graph.nodes,"value"));
var nodeScale=d3.scale.sqrt()
.domain([minVal_node,maxVal_node])
.range([10,50]);
//link scale
var maxVal_links=d3.max(_.pluck(graph.links,"value"));
var minVal_links=d3.min(_.pluck(graph.links,"value"));
var linkScale=d3.scale.sqrt()
.domain([minVal_links,maxVal_links])
.range([1,10]);
$("#slider").slider({
value:1,
min: minVal_links,
max: maxVal_links,
slide: function( event, ui ) {
$("#amount").val( ui.value );
},
stop: function( event, ui ) {
updateLinks();
}
});
$("#amount").val($("#slider").slider("value"));
// add in the links
var link = svg.append("g")
.attr("class", "links")
.selectAll(".link")
.data(graph.links)
.enter()
.append("path")
.attr("id", function(d,i){
d.id = i;
return "link-"+i;
})
.filter(function(d){ return d.value >$("#slider").slider("value");})
.attr("class", "link")
.attr("d", path)
.style("fill", "none")
.style("stroke", "tan")
.style("stroke-opacity", ".2")
.on("mouseover", function(d) {
d3.select(this).style("stroke-opacity", ".5") } )
.on("mouseout", function() { d3.select(this).style("stroke-opacity", ".2") } )
.style("stroke-width", function (d) {
return linkScale(d.value);
})
.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" + d.value+"\n"+linkScale(d.value);
});
// add in the nodes
var node = svg.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
.on("mouseover",function(d,i){
var interact_mode="hover";
highlight_node_links(d,i,this,interact_mode);
})
.on("mouseout",function(d,i){
var interact_mode="hover";
highlight_node_links(d,i,this,interact_mode);
})
.on("click",function(d,i){
var interact_mode="click";
highlight_node_links(d,i,this,interact_mode);
})
// .on("mouseover", highlight_node_links)
// .on("mouseout", highlight_node_links)
// .on("click",highlight_node_links)
// .call(d3.behavior.drag()
// .origin(function (d) {
// return d;
// })
// .on("dragstart", function () {
// this.parentNode.appendChild(this);
// })
// .on("drag", dragmove));
// add the circles/rect for the nodes
// node.append("circle")
// .attr("cx", sankey.nodeWidth()/2)
// .attr("cy", function (d) {
// return d.dy/2;
// })
// .attr("r", function (d) {
// return Math.sqrt(d.dy);
// })
// .style("fill", function (d) {
// return d.color = color(d.name.replace(/ .*/, ""));
// })
// .style("fill-opacity", ".9")
// .style("shape-rendering", "crispEdges")
// .style("stroke", function (d) {
// return d3.rgb(d.color).darker(2);
// })
// .append("title")
// .text(function (d) {
// return d.name + "\n" + format(d.value);
// });
node.append("rect")
.attr("x", function (d) {
return -nodeScale(d.value)/2;
})
.attr("y", function (d) {
return -sankey.nodeWidth()/2;
})
.attr("width", function(d) { return nodeScale(d.value); })
.attr("height", sankey.nodeWidth())
.style("fill", function(d) {
return d.color = color(d.name.substring(2,d.name.length)); })
// .style("stroke", function(d) {
// return d3.rgb(d.color).darker(2); })
.append("title")
.text(function(d) {
return d.name + "\n" + d.value; });
// add in the title for the nodes
// node.append("text")
// .attr("x", function (d) {
// return - 6 + sankey.nodeWidth() / 2 - Math.sqrt(d.dy);
// })
// .attr("y", function (d) {
// return d.dy / 2;
// })
// .attr("dy", ".15em")
// .attr("transform", null)
// .text(function (d) {
// var majorid=d.name.substring(2,d.name.length)
// // return (_.invert(majorIDMap))[majorid];
// return majorid;
// })
// .filter(function (d) {
// return d.x < width / 2;
// })
// .attr("x", function (d) {
// return 6 + sankey.nodeWidth() / 2 + Math.sqrt(d.dy);
// })
// 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);
};
function highlight_link_links(){
}
function highlight_node_links(node,i,thisnode,mode){
// console.log(thisnode);
var remainingNodes=[],
nextNodes=[];
var stroke_opacity = 0;
if( d3.select(thisnode).attr(mode) == "1" ){
d3.select(thisnode).attr(mode,"0");
stroke_opacity = 0.2;
if (mode==="click") {
d3.select(thisnode)
.on("mouseover",function(d,i){
var interact_mode="hover";
highlight_node_links(d,i,this,interact_mode);
})
.on("mouseout",function(d,i){
var interact_mode="hover";
highlight_node_links(d,i,this,interact_mode);
})
}
}else{
d3.select(thisnode).attr(mode,"1");
stroke_opacity = 0.7;
if (mode==="click") d3.select(thisnode).on("mouseover",null);
if (mode==="click") d3.select(thisnode).on("mouseout",null);
}
// if( d3.select(thisnode).attr("data-clicked") == "1" ){
// d3.select(thisnode).attr("data-clicked","0");
// stroke_opacity = 0.2;
// }else{
// d3.select(thisnode).attr("data-clicked","1");
// stroke_opacity = 0.5;
// }
var traverse = [{
linkType : "sourceLinks",
nodeType : "target"
},{
linkType : "targetLinks",
nodeType : "source"
}];
traverse.forEach(function(step){
node[step.linkType].forEach(function(link) {
remainingNodes.push(link[step.nodeType]);
highlight_link(link.id, stroke_opacity);
});
// while (remainingNodes.length) {
// nextNodes = [];
// remainingNodes.forEach(function(node) {
// node[step.linkType].forEach(function(link) {
// nextNodes.push(link[step.nodeType]);
// highlight_link(link.id, stroke_opacity);
// });
// });
// remainingNodes = nextNodes;
// }
});
}
function highlight_link(id,opacity){
d3.select("#link-"+id).style("stroke-opacity", opacity);
}
function updateLinks(){
d3.select(".links").selectAll("path").remove();
d3.select(".links").selectAll(".link")
.data(graph.links)
.enter()
.append("path")
.attr("id", function(d,i){
d.id = i;
return "link-"+i;
})
.filter(function(d){ return d.value >$("#slider").slider("value");})
.attr("class", "link")
.attr("d", path)
.style("fill", "none")
.style("stroke", "tan")
.style("stroke-opacity", ".33")
.on("mouseover", function() { d3.select(this).style("stroke-opacity", ".7") } )
.on("mouseout", function() { d3.select(this).style("stroke-opacity", ".2") } )
.style("stroke-width", function (d) {
return linkScale(d.value);
})
.sort(function (a, b) {
return b.dy - a.dy;
});
}
});
function extendAttr(data){
var majors=_.uniq(_.pluck(data,"T1_Level2"))
majorIDMap=_.object(majors,_.range(majors.length))
var terms=["T1_Level2", "T2_Level2", "T3_Level2"];
_.each(data,function(d){
_.each(terms,function(i){
d[i+"_id"]=majorIDMap[d[i]]
})
d.count=parseInt(d.count);
});
}
</script>
</body>
</html>
Modified http://code.jquery.com/jquery-1.10.2.js to a secure url
Modified http://code.jquery.com/ui/1.11.4/jquery-ui.js to a secure url
Updated missing url https://rawgithub.com/jashkenas/underscore/master/underscore-min.js to https://cdn.jsdelivr.net/npm/underscore@1.12.1/underscore-min.js
Modified http://rawgithub.com/square/crossfilter/master/crossfilter.min.js to a secure url
Modified http://d3js.org/d3.v3.min.js to a secure url
Modified http://code.jquery.com/jquery-1.12.0.min.js to a secure url
https://code.jquery.com/jquery-1.10.2.js
https://code.jquery.com/ui/1.11.4/jquery-ui.js
https://rawgithub.com/jashkenas/underscore/master/underscore-min.js
https://rawgithub.com/square/crossfilter/master/crossfilter.min.js
https://d3js.org/d3.v3.min.js
https://code.jquery.com/jquery-1.12.0.min.js