Last active
April 9, 2021 23:38
-
-
Save csaladenes/38d92466c2ae4d68793e to your computer and use it in GitHub Desktop.
Sankey Diagram Generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(e,t){if(typeof define==="function"&&define.amd){define(t)}else{e.Dragdealer=t()}})(this,function(){function f(e){var t="Webkit Moz ms O".split(" "),n=document.documentElement.style;if(n[e]!==undefined)return e;e=e.charAt(0).toUpperCase()+e.substr(1);for(var r=0;r<t.length;r++){if(n[t[r]+e]!==undefined){return t[r]+e}}}function l(e){if(a.backfaceVisibility&&a.perspective){e.style[a.perspective]="1000px";e.style[a.backfaceVisibility]="hidden"}}var e=function(e,t){this.bindMethods();this.options=this.applyDefaults(t||{});this.wrapper=this.getWrapperElement(e);if(!this.wrapper){return}this.handle=this.getHandleElement(this.wrapper,this.options.handleClass);if(!this.handle){return}this.init();this.bindEventListeners()};e.prototype={defaults:{disabled:false,horizontal:true,vertical:false,slide:true,steps:0,snap:false,loose:false,speed:.1,xPrecision:0,yPrecision:0,handleClass:"handle",css3:true,activeClass:"active"},init:function(){if(this.options.css3){l(this.handle)}this.value={prev:[-1,-1],current:[this.options.x||0,this.options.y||0],target:[this.options.x||0,this.options.y||0]};this.offset={wrapper:[0,0],mouse:[0,0],prev:[-999999,-999999],current:[0,0],target:[0,0]};this.change=[0,0];this.stepRatios=this.calculateStepRatios();this.activity=false;this.dragging=false;this.tapping=false;this.reflow();if(this.options.disabled){this.disable()}},applyDefaults:function(e){for(var t in this.defaults){if(!e.hasOwnProperty(t)){e[t]=this.defaults[t]}}return e},getWrapperElement:function(e){if(typeof e=="string"){return document.getElementById(e)}else{return e}},getHandleElement:function(e,t){var n,r,i;if(e.getElementsByClassName){n=e.getElementsByClassName(t);if(n.length>0){return n[0]}}else{r=new RegExp("(^|\\s)"+t+"(\\s|$)");n=e.getElementsByTagName("*");for(i=0;i<n.length;i++){if(r.test(n[i].className)){return n[i]}}}},calculateStepRatios:function(){var e=[];if(this.options.steps>1){for(var t=0;t<=this.options.steps-1;t++){e[t]=t/(this.options.steps-1)}}return e},setWrapperOffset:function(){this.offset.wrapper=u.get(this.wrapper)},calculateBounds:function(){var e={top:this.options.top||0,bottom:-(this.options.bottom||0)+this.wrapper.offsetHeight,left:this.options.left||0,right:-(this.options.right||0)+this.wrapper.offsetWidth};e.availWidth=e.right-e.left-this.handle.offsetWidth;e.availHeight=e.bottom-e.top-this.handle.offsetHeight;return e},calculateValuePrecision:function(){var e=this.options.xPrecision||Math.abs(this.bounds.availWidth),t=this.options.yPrecision||Math.abs(this.bounds.availHeight);return[e?1/e:0,t?1/t:0]},bindMethods:function(){this.onHandleMouseDown=t(this.onHandleMouseDown,this);this.onHandleTouchStart=t(this.onHandleTouchStart,this);this.onDocumentMouseMove=t(this.onDocumentMouseMove,this);this.onWrapperTouchMove=t(this.onWrapperTouchMove,this);this.onWrapperMouseDown=t(this.onWrapperMouseDown,this);this.onWrapperTouchStart=t(this.onWrapperTouchStart,this);this.onDocumentMouseUp=t(this.onDocumentMouseUp,this);this.onDocumentTouchEnd=t(this.onDocumentTouchEnd,this);this.onHandleClick=t(this.onHandleClick,this);this.onWindowResize=t(this.onWindowResize,this)},bindEventListeners:function(){n(this.handle,"mousedown",this.onHandleMouseDown);n(this.handle,"touchstart",this.onHandleTouchStart);n(document,"mousemove",this.onDocumentMouseMove);n(this.wrapper,"touchmove",this.onWrapperTouchMove);n(this.wrapper,"mousedown",this.onWrapperMouseDown);n(this.wrapper,"touchstart",this.onWrapperTouchStart);n(document,"mouseup",this.onDocumentMouseUp);n(document,"touchend",this.onDocumentTouchEnd);n(this.handle,"click",this.onHandleClick);n(window,"resize",this.onWindowResize);var e=this;this.interval=setInterval(function(){e.animate()},25);this.animate(false,true)},unbindEventListeners:function(){r(this.handle,"mousedown",this.onHandleMouseDown);r(this.handle,"touchstart",this.onHandleTouchStart);r(document,"mousemove",this.onDocumentMouseMove);r(this.wrapper,"touchmove",this.onWrapperTouchMove);r(this.wrapper,"mousedown",this.onWrapperMouseDown);r(this.wrapper,"touchstart",this.onWrapperTouchStart);r(document,"mouseup",this.onDocumentMouseUp);r(document,"touchend",this.onDocumentTouchEnd);r(this.handle,"click",this.onHandleClick);r(window,"resize",this.onWindowResize);clearInterval(this.interval)},onHandleMouseDown:function(e){o.refresh(e);i(e);s(e);this.activity=false;this.startDrag()},onHandleTouchStart:function(e){o.refresh(e);s(e);this.activity=false;this.startDrag()},onDocumentMouseMove:function(e){o.refresh(e);if(this.dragging){this.activity=true}},onWrapperTouchMove:function(e){o.refresh(e);if(!this.activity&&this.draggingOnDisabledAxis()){if(this.dragging){this.stopDrag()}return}i(e);this.activity=true},onWrapperMouseDown:function(e){o.refresh(e);i(e);this.startTap()},onWrapperTouchStart:function(e){o.refresh(e);i(e);this.startTap()},onDocumentMouseUp:function(e){this.stopDrag();this.stopTap()},onDocumentTouchEnd:function(e){this.stopDrag();this.stopTap()},onHandleClick:function(e){if(this.activity){i(e);s(e)}},onWindowResize:function(e){this.reflow()},enable:function(){this.disabled=false;this.handle.className=this.handle.className.replace(/\s?disabled/g,"")},disable:function(){this.disabled=true;this.handle.className+=" disabled"},reflow:function(){this.setWrapperOffset();this.bounds=this.calculateBounds();this.valuePrecision=this.calculateValuePrecision();this.updateOffsetFromValue()},getStep:function(){return[this.getStepNumber(this.value.target[0]),this.getStepNumber(this.value.target[1])]},getValue:function(){return this.value.target},setStep:function(e,t,n){this.setValue(this.options.steps&&e>1?(e-1)/(this.options.steps-1):0,this.options.steps&&t>1?(t-1)/(this.options.steps-1):0,n)},setValue:function(e,t,n){this.setTargetValue([e,t||0]);if(n){this.groupCopy(this.value.current,this.value.target);this.updateOffsetFromValue();this.callAnimationCallback()}},startTap:function(){if(this.disabled){return}this.tapping=true;this.setWrapperOffset();this.setTargetValueByOffset([o.x-this.offset.wrapper[0]-this.handle.offsetWidth/2,o.y-this.offset.wrapper[1]-this.handle.offsetHeight/2])},stopTap:function(){if(this.disabled||!this.tapping){return}this.tapping=false;this.setTargetValue(this.value.current)},startDrag:function(){if(this.disabled){return}this.dragging=true;this.setWrapperOffset();this.offset.mouse=[o.x-u.get(this.handle)[0],o.y-u.get(this.handle)[1]];if(!this.wrapper.className.match(this.options.activeClass)){this.wrapper.className+=" "+this.options.activeClass}},stopDrag:function(){if(this.disabled||!this.dragging){return}this.dragging=false;var e=this.groupClone(this.value.current);if(this.options.slide){var t=this.change;e[0]+=t[0]*4;e[1]+=t[1]*4}this.setTargetValue(e);this.wrapper.className=this.wrapper.className.replace(" "+this.options.activeClass,"")},callAnimationCallback:function(){var e=this.value.current;if(this.options.snap&&this.options.steps>1){e=this.getClosestSteps(e)}if(!this.groupCompare(e,this.value.prev)){if(typeof this.options.animationCallback=="function"){this.options.animationCallback.call(this,e[0],e[1])}this.groupCopy(this.value.prev,e)}},callTargetCallback:function(){if(typeof this.options.callback=="function"){this.options.callback.call(this,this.value.target[0],this.value.target[1])}},animate:function(e,t){if(e&&!this.dragging){return}if(this.dragging){var n=this.groupClone(this.value.target);var r=[o.x-this.offset.wrapper[0]-this.offset.mouse[0],o.y-this.offset.wrapper[1]-this.offset.mouse[1]];this.setTargetValueByOffset(r,this.options.loose);this.change=[this.value.target[0]-n[0],this.value.target[1]-n[1]]}if(this.dragging||t){this.groupCopy(this.value.current,this.value.target)}if(this.dragging||this.glide()||t){this.updateOffsetFromValue();this.callAnimationCallback()}},glide:function(){var e=[this.value.target[0]-this.value.current[0],this.value.target[1]-this.value.current[1]];if(!e[0]&&!e[1]){return false}if(Math.abs(e[0])>this.valuePrecision[0]||Math.abs(e[1])>this.valuePrecision[1]){this.value.current[0]+=e[0]*this.options.speed;this.value.current[1]+=e[1]*this.options.speed}else{this.groupCopy(this.value.current,this.value.target)}return true},updateOffsetFromValue:function(){if(!this.options.snap){this.offset.current=this.getOffsetsByRatios(this.value.current)}else{this.offset.current=this.getOffsetsByRatios(this.getClosestSteps(this.value.current))}if(!this.groupCompare(this.offset.current,this.offset.prev)){this.renderHandlePosition();this.groupCopy(this.offset.prev,this.offset.current)}},renderHandlePosition:function(){var e="";if(this.options.css3&&a.transform){if(this.options.horizontal){e+="translateX("+this.offset.current[0]+"px)"}if(this.options.vertical){e+=" translateY("+this.offset.current[1]+"px)"}this.handle.style[a.transform]=e;return}if(this.options.horizontal){this.handle.style.left=this.offset.current[0]+"px"}if(this.options.vertical){this.handle.style.top=this.offset.current[1]+"px"}},setTargetValue:function(e,t){var n=t?this.getLooseValue(e):this.getProperValue(e);this.groupCopy(this.value.target,n);this.offset.target=this.getOffsetsByRatios(n);this.callTargetCallback()},setTargetValueByOffset:function(e,t){var n=this.getRatiosByOffsets(e);var r=t?this.getLooseValue(n):this.getProperValue(n);this.groupCopy(this.value.target,r);this.offset.target=this.getOffsetsByRatios(r)},getLooseValue:function(e){var t=this.getProperValue(e);return[t[0]+(e[0]-t[0])/4,t[1]+(e[1]-t[1])/4]},getProperValue:function(e){var t=this.groupClone(e);t[0]=Math.max(t[0],0);t[1]=Math.max(t[1],0);t[0]=Math.min(t[0],1);t[1]=Math.min(t[1],1);if(!this.dragging&&!this.tapping||this.options.snap){if(this.options.steps>1){t=this.getClosestSteps(t)}}return t},getRatiosByOffsets:function(e){return[this.getRatioByOffset(e[0],this.bounds.availWidth,this.bounds.left),this.getRatioByOffset(e[1],this.bounds.availHeight,this.bounds.top)]},getRatioByOffset:function(e,t,n){return t?(e-n)/t:0},getOffsetsByRatios:function(e){return[this.getOffsetByRatio(e[0],this.bounds.availWidth,this.bounds.left),this.getOffsetByRatio(e[1],this.bounds.availHeight,this.bounds.top)]},getOffsetByRatio:function(e,t,n){return Math.round(e*t)+n},getStepNumber:function(e){return this.getClosestStep(e)*(this.options.steps-1)+1},getClosestSteps:function(e){return[this.getClosestStep(e[0]),this.getClosestStep(e[1])]},getClosestStep:function(e){var t=0;var n=1;for(var r=0;r<=this.options.steps-1;r++){if(Math.abs(this.stepRatios[r]-e)<n){n=Math.abs(this.stepRatios[r]-e);t=r}}return this.stepRatios[t]},groupCompare:function(e,t){return e[0]==t[0]&&e[1]==t[1]},groupCopy:function(e,t){e[0]=t[0];e[1]=t[1]},groupClone:function(e){return[e[0],e[1]]},draggingOnDisabledAxis:function(){return!this.options.horizontal&&o.xDiff>o.yDiff||!this.options.vertical&&o.yDiff>o.xDiff}};var t=function(e,t){return function(){return e.apply(t,arguments)}};var n=function(e,t,n){if(e.addEventListener){e.addEventListener(t,n,false)}else if(e.attachEvent){e.attachEvent("on"+t,n)}};var r=function(e,t,n){if(e.removeEventListener){e.removeEventListener(t,n,false)}else if(e.detachEvent){e.detachEvent("on"+t,n)}};var i=function(e){if(!e){e=window.event}if(e.preventDefault){e.preventDefault()}e.returnValue=false};var s=function(e){if(!e){e=window.event}if(e.stopPropagation){e.stopPropagation()}e.cancelBubble=true};var o={x:0,y:0,xDiff:0,yDiff:0,refresh:function(e){if(!e){e=window.event}if(e.type=="mousemove"){this.set(e)}else if(e.touches){this.set(e.touches[0])}},set:function(e){var t=this.x,n=this.y;if(e.clientX||e.clientY){this.x=e.clientX;this.y=e.clientY}else if(e.pageX||e.pageY){this.x=e.pageX-document.body.scrollLeft-document.documentElement.scrollLeft;this.y=e.pageY-document.body.scrollTop-document.documentElement.scrollTop}this.xDiff=Math.abs(this.x-t);this.yDiff=Math.abs(this.y-n)}};var u={get:function(e){var t={left:0,top:0};if(e.getBoundingClientRect!==undefined){t=e.getBoundingClientRect()}return[t.left,t.top]}};var a={transform:f("transform"),perspective:f("perspective"),backfaceVisibility:f("backfaceVisibility")};return e}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*This software is released under the MIT License | |
Copyright (C) 2014 Denes Csala http://www.csaladen.es | |
The following software uses the javascript frameworks below, | |
all of which are distributed under the MIT or GNU/GPL license: | |
D3.js http://d3js.org/ data-oriented javascript framework. | |
- Sankey plugin http://bost.ocks.org/mike/sankey/ for D3.js (heavily modified) by Mike Bostock's, | |
which is based on the initial version http://tamc.github.io/Sankey/ by Thomas Counsell. | |
I have incorporated the ability to render Sankey cycles, as pioneered by https://github.com/cfergus | |
- Dragdealer.js href="http://skidding.github.io/dragdealer/ by Ovidiu Chereches | |
*/ | |
//<!--DATA INIT--> | |
var data={"nodes": [], "links": []} | |
//<!--DATA ENTRY--> | |
nodesform=d3.select("#nodes-form"); | |
function addnode() { | |
nodesform.append("div").append("input").attr("value","New Node"); | |
} | |
function removenode() { | |
nodesform[0][0].children[nodesform[0][0].children.length-1].remove("div") | |
} | |
linksform=d3.select("#links-form"); | |
function addlink() { | |
linksform.append("div").append("input").attr("value","0,1,0.52"); | |
} | |
function removelink() { | |
linksform[0][0].children[linksform[0][0].children.length-1].remove("div") | |
} | |
function draw() { | |
data={"nodes": [], "links": []} | |
for (i = 0; i < nodesform[0][0].children.length; i++) { | |
data.nodes.push({"name": nodesform[0][0].children[i].children[0].value}); | |
} | |
for (i = 0; i < linksform[0][0].children.length; i++) { | |
var array = linksform[0][0].children[i].children[0].value.split(','); | |
data.links.push({"source":parseInt(array[0]),"target":parseInt(array[1]),"value":parseFloat(array[2])}); | |
} | |
change(data); | |
} | |
function save(){ | |
d3.select('#save').style('z-index',100).transition().style('opacity',0.9); | |
st='{"nodes":[' | |
for (i = 0; i < nodesform[0][0].children.length; i++) { | |
st=st+'{"name":"'+nodesform[0][0].children[i].children[0].value+'"},'; | |
} | |
st=st.substring(0, st.length - 1)+'],"links":['; | |
for (i = 0; i < linksform[0][0].children.length; i++) { | |
var array = linksform[0][0].children[i].children[0].value.split(','); | |
st=st+'{"source":'+parseInt(array[0])+',"target":'+parseInt(array[1])+',"value":'+parseFloat(array[2])+'},'; | |
} | |
st = st.substring(0, st.length - 1)+']}'; | |
d3.select('#savetext').text(st); | |
} | |
function load(){ | |
d3.select('#load').style('z-index',100).transition().style('opacity',0.9); | |
} | |
function loadsubmit(){ | |
d3.select('#load').transition().style('opacity',0).style('z-index',-1); | |
var loadtext=d3.select('#load')[0][0].children[1].value; | |
if (loadtext!="") { | |
//redraw | |
var newdata=JSON.parse(loadtext); | |
change(newdata); | |
//remove existing node entry boxes | |
var n=nodesform[0][0].children.length; | |
for (i = 0; i < n; i++) { | |
nodesform[0][0].children[0].remove("div"); | |
} | |
//remove existing link entry boxes | |
var n=linksform[0][0].children.length; | |
for (i = 0; i < n; i++) { | |
linksform[0][0].children[0].remove("div"); | |
} | |
//add new node entry boxes | |
for (i = 0; i < newdata.nodes.length; i++) { | |
nodesform.append("div").append("input").attr("value",newdata.nodes[i].name); | |
} | |
//add new link entry boxes | |
var newdata=JSON.parse(loadtext.substring(loadtext.indexOf('"links":[')+8, loadtext.length - 1)) | |
for (i = 0; i < newdata.length; i++) { | |
linksform.append("div").append("input").attr("value",newdata[i].source+","+newdata[i].target+","+newdata[i].value); | |
} | |
} | |
} | |
//<!--SANKEY DIAGRAM--> | |
var padding = 28; | |
var paddingmultiplier = 50; | |
var lowopacity = 0.3; | |
var highopacity = 0.7; | |
var format2Number = d3.format(",.2f"), | |
formatNumber = d3.format(",.0f"), | |
format = function(a) { | |
return formatNumber(a) | |
}, | |
format2 = function(a) { | |
return format2Number(a) | |
}, | |
color = d3.scale.category20(); | |
d3.select("#chart").style("width", document.getElementById("chart").offsetWidth - sizecorrection) | |
d3.select("#titlebar").style("width", document.getElementById("titlebar").offsetWidth - sizecorrection) | |
var margin = { | |
top: 70, | |
right: 10, | |
bottom: 30, | |
left: 40 | |
}, | |
width = document.getElementById("chart").offsetWidth - margin.left - margin.right, | |
height = document.getElementById("chart").offsetHeight - margin.bottom - 90; | |
var svg = d3.select("#chart").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 + ")"); | |
var sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]); | |
var path = sankey.reversibleLink(); | |
var change = function() {}; | |
change = function(d) { | |
padding = paddingmultiplier * (1 - densityslider.getValue()[0]) + 3 | |
svg.selectAll("g").remove(); | |
sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]); | |
sankey.nodes(d.nodes).links(d.links).layout(500); | |
var g = svg.append("g") //link | |
.selectAll(".link").data(d.links).enter().append("g").attr("class", "link").sort(function(j, i) { | |
return i.dy - j.dy | |
}); | |
var h = g.append("path") //path0 | |
.attr("d", path(0)); | |
var f = g.append("path") //path1 | |
.attr("d", path(1)); | |
var e = g.append("path") //path2 | |
.attr("d", path(2)); | |
g.attr("fill", function(i) { | |
return i.source.color = color(i.source.name.replace(/ .*/, "")) | |
}).attr("opacity", lowopacity).on("mouseover", function(d) { | |
d3.select(this).style('opacity', highopacity); | |
}).on("mouseout", function(d) { | |
d3.select(this).style('opacity', lowopacity); | |
}).append("title") //link | |
.text(function(i) { | |
return i.source.name + " → " + i.target.name + "\n" + format2(i.value) | |
}); | |
var c = svg.append("g") //node | |
.selectAll(".node").data(d.nodes).enter().append("g").attr("class", "node").attr("transform", function(i) { | |
return "translate(" + i.x + "," + i.y + ")" | |
}).call(d3.behavior.drag().origin(function(i) { | |
return i | |
}).on("dragstart", function() { | |
this.parentNode.appendChild(this) | |
}).on("drag", b)); | |
c.append("rect") //node | |
.attr("height", function(i) { | |
return i.dy | |
}).attr("width", sankey.nodeWidth()).style("fill", function(i) { | |
return i.color = color(i.name.replace(/ .*/, "")) | |
}).style("stroke", function(i) { | |
return d3.rgb(i.color).darker(2) | |
}).on("mouseover", function(d) { | |
svg.selectAll(".link").filter(function(l) { | |
return l.source == d || l.target == d; | |
}).transition().style('opacity', highopacity); | |
}).on("mouseout", function(d) { | |
svg.selectAll(".link").filter(function(l) { | |
return l.source == d || l.target == d; | |
}).transition().style('opacity', lowopacity); | |
}).on("dblclick", function(d) { | |
svg.selectAll(".link").filter(function(l) { | |
return l.target == d; | |
}).attr("display", function() { | |
if (d3.select(this).attr("display") == "none") return "inline" | |
else return "none" | |
}); | |
}).append("title").text(function(i) { | |
return i.name + "\n" + format2(i.value) | |
}); | |
c.append("text") //node | |
.attr("x", -6).attr("y", function(i) { | |
return i.dy / 2 | |
}).attr("dy", ".35em").attr("text-anchor", "end").attr("transform", null).text(function(i) { | |
return i.name | |
}).filter(function(i) { | |
return i.x < width / 2 | |
}).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start") | |
c.append("text") //node | |
.attr("x", function(i) {return -i.dy / 2}) | |
.attr("y", function(i) {return i.dx / 2 + 6}) | |
.attr("transform", "rotate(270)").attr("text-anchor", "middle").text(function(i) { | |
if (i.dy>50){ | |
return format(i.value); | |
} | |
}).attr("fill","white").attr("stroke","black"); | |
function b(i) { //dragmove | |
if (document.getElementById("ymove").checked) { | |
if (document.getElementById("xmove").checked) { | |
d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")") | |
} else { | |
d3.select(this).attr("transform", "translate(" + i.x + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")") | |
} | |
} else { | |
if (document.getElementById("xmove").checked) { | |
d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + i.y + ")") | |
} | |
} | |
sankey.relayout(); | |
f.attr("d", path(1)); | |
h.attr("d", path(0)); | |
e.attr("d", path(2)) | |
}; | |
}; | |
draw(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE xhtml> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<meta http-equiv="content-Type" content="text/html; charset=utf-8" /> | |
<meta name="description" content="Sankey Diagram Generator #sankey #d3js @csaladenes" /> | |
<meta name="keywords" content="csaladenes, sankey diagram, d3.js, d3js" /> | |
<!--<meta property="og:image" content="http://food.csaladen.es/favicon.png" />--> | |
<meta property="og:image" content="http://food.csaladen.es/food_energy_flows_sankey.jpg" /> | |
<meta property="og:description" content="Sankey Diagram Generator #sankey #d3js @csaladenes" /> | |
<meta property="og:title" content="Sankey Diagram" /> | |
<meta property="og:type" content="article" /> | |
<meta property="og:site_name" content="food.csaladen.es" /> | |
<!--<meta property="og:url" content="http://food.csaladen.es" />--> | |
<meta property="fb:admins" content="100943737036023614165" /> | |
<title>Sankey Diagram</title> | |
<link rel="shortcut icon" href="http://food.csaladen.es/favicon.png" /> | |
<style> | |
html, | |
body { | |
height: 100%; | |
font-family: "Trebuchet MS", "Open Sans", Segoe UI light, Verdana, Tahoma, Helvetica, sans-serif; | |
margin: 0; | |
padding: 0; | |
background: #fff; | |
} | |
#chart { | |
height: 99%; | |
float: left; | |
position: absolute; | |
width: -moz-calc(80% - 45px); | |
width: -webkit-calc(80% - 45px); | |
width: calc(80% - 45px); | |
} | |
a { | |
text-decoration: none; | |
} | |
.pagetitle { | |
height: 30px; | |
color: #F60; | |
font-size: 20px; | |
line-height: 30px; | |
text-align: center; | |
font-weight: bold; | |
font-style: oblique | |
} | |
.node rect { | |
cursor: move; | |
fill-opacity: .9; | |
shape-rendering: crispEdges | |
} | |
.node text { | |
pointer-events: none; | |
text-shadow: 0 1px 0 #fff | |
} | |
.link { | |
stroke: #000; | |
stroke-opacity: .3; | |
} | |
.link:hover { | |
stroke-opacity: .7; | |
} | |
#titlebar { | |
width: -moz-calc(80% - 90px); | |
width: -webkit-calc(80% - 90px); | |
width: calc(80% - 90px); | |
height: 30px; | |
border-bottom: solid; | |
border-color: #EEE; | |
padding-bottom: 5px; | |
margin-left: 40px; | |
margin-top: 5px; | |
position: fixed; | |
z-index: 50; | |
} | |
#titlebar-right { | |
right: -moz-calc(20% + 50px); | |
right: -webkit-calc(20% + 50px); | |
right: calc(20% + 50px); | |
height: 30px; | |
padding-bottom: 5px; | |
margin-left: 40px; | |
margin-top: 5px; | |
position: fixed; | |
z-index: 50; | |
} | |
.dragdealer { | |
height: 30px; | |
background: #EEE; | |
z-index: 50; | |
} | |
.dragdealer .handle { | |
top: 0; | |
left: 0; | |
cursor: pointer; | |
} | |
.dragdealer .red-bar { | |
width: 100px; | |
height: 30px; | |
background: #F80; | |
color: #FFF; | |
font-size: 20px; | |
line-height: 30px; | |
text-align: center; | |
font-weight: bold; | |
font-style: oblique | |
} | |
.dragdealer .red-bar:hover { | |
background: #F60 | |
} | |
.dragdealer .orange-bar { | |
width: 25px; | |
height: 18px; | |
background: #F80; | |
color: #FFF; | |
font-size: 18px; | |
line-height: 16px; | |
text-align: center; | |
font-weight: bold; | |
} | |
.dragdealer .orange-bar:hover { | |
background: #F60 | |
} | |
.dragdealer .disabled { | |
background: #898989 | |
} | |
#social { | |
position: fixed; | |
bottom: 5; | |
right: 0; | |
text-decoration: none; | |
padding: 3px; | |
padding-top:1px; | |
z-index: 800; | |
white-space:nowrap; | |
overflow:hidden; | |
} | |
#social a{ | |
margin-left:10px; | |
margin-right:5px; | |
color: #000; | |
font-size: 10px; | |
font-family: "Trebuchet MS"; | |
font-weight: lighter; | |
text-decoration: none; | |
} | |
.likeform input { | |
width:100%; | |
min-width:220px; | |
} | |
</style> | |
<script src="dragdealer.js"></script> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script> | |
var sizecorrection = Math.max(0, 220 - parseInt(window.innerWidth * 0.2)); | |
function checksize() { | |
if ((window.innerWidth < 600) || (window.innerHeight < 300)) { | |
alert("The recommended minimum resolution is 600 x 300.\n Yours is " + window.innerWidth + " x " + window.innerHeight + "."); | |
} | |
setTimeout(function(){ | |
d3.select("#social").transition().style("opacity", 1); | |
},3000); | |
} | |
window.onresize = function() { | |
window.location.reload(); | |
} | |
</script> | |
<script> | |
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); | |
ga('create', 'UA-40713687-2', 'auto'); | |
ga('send', 'pageview'); | |
</script> | |
<script type="text/javascript">var switchTo5x=true;</script> | |
<script type="text/javascript" src="http://w.sharethis.com/button/buttons.js"></script> | |
<script type="text/javascript">stLight.options({publisher: "505a26ad-d820-47bd-a500-7f49d04a30f5", doNotHash: true, doNotCopy: false, hashAddressBar: false});</script> | |
</head> | |
<body onload="checksize();"> | |
<!--TITLELBAR--> | |
<div id="titlebar"> | |
<span id="titletext" class="pagetitle">Sankey Diagram</span> | |
</div> | |
<!--PAGE WRAPPER--> | |
<div id="content" style="opacity:1;"> | |
<!--SANKEY DIAGRAM CONTROLS--> | |
<div id="sourcecontrol" style="float:right; margin-right:10px; width:20%; margin-top:10px; min-width:220px; border-left:solid; border-color:#EEE; height:98%; padding-left:20px;line-height:26px;z-index:600000;"> | |
<!--LOAD--> | |
<div id="load" style="position:fixed;float:left;width:inherit;height:100%;background:#fff;color:#000;opacity:0;margin-top:0px;margin-left:0px;z-index:-1;"> | |
<div>Please paste the Sankey string below:</div> | |
<textarea id="loadtext" style="margin-top:20px;margin-bottom:20px;width:97%;height:50%;"/></textarea> | |
<div><button onclick="loadsubmit();">Done</button></div> | |
</div> | |
<!--SAVE--> | |
<div id="save" style="position:fixed;float:left;width:inherit;height:100%;background:#fff;color:#000;opacity:0;margin-top:0px;margin-left:0px;z-index:-1;"> | |
<div>Please copy and save the text below to load later:</div> | |
<textarea id="savetext" style="margin-top:20px;margin-bottom:20px;width:97%;height:50%;"/></textarea> | |
<div><button onclick="d3.select('#save').transition().style('opacity',0).style('z-index',-1)">Done</button></div> | |
</div> | |
<!--INCREMENTAL--> | |
<div style="display:block;overflow: hidden;white-space: nowrap;"><span style="float:left;">Move nodes:</span> | |
<div style="float:right; padding-right:0; margin-right:0;"> | |
<div style="font-size:20px;font-weight:bold;line-height:10px;"> | |
<input title="Enable horizontal node dragging" type="checkbox" id="xmove"> | |
<label for="xmove">↔</label> | |
<img alt="" style="width:5px;height:0px;"> | |
<input title="Enable vertical node dragging" type="checkbox" id="ymove" checked> | |
<label for="ymove">↕</label> | |
</div> | |
</div> | |
</div> | |
<div style="display:block;overflow: hidden;white-space: nowrap;"><span style="float:left;">Density:</span> | |
<div style="float:right;"> | |
<div id="pslider" title="Drag slider to set diagram node padding" class="dragdealer" style="margin-top:2px;margin-left:-130px;width:130px; height:18px; position:absolute;"> | |
<div class="handle orange-bar">↔ | |
<script> | |
var densityslider = new Dragdealer("pslider", { | |
x: 0.5, | |
steps: 5, | |
snap: true, | |
callback: function(a, b) { | |
padding = paddingmultiplier * (1 - a) + 3; | |
draw() | |
} | |
}); | |
</script> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div style="display:block;overflow: hidden;white-space: nowrap;"><span style="float:left;">Opacity:</span> | |
<div style="float:right;"> | |
<div id="oslider" title="Drag slider to set diagram flow opacities" class="dragdealer" style="margin-top:2px;margin-left:-130px;width:130px; height:18px; position:absolute;"> | |
<div class="handle orange-bar">↔ | |
<script> | |
new Dragdealer("oslider", { | |
x: 0.25, | |
steps: 5, | |
snap: true, | |
callback: function(a, b) { | |
lowopacity = 0.1 + 0.8 * a; | |
draw() | |
} | |
}); | |
</script> | |
</div> | |
</div> | |
</div> | |
</div> | |
<div style="display:block;overflow: hidden;white-space: nowrap;"> | |
<div> | |
<button onclick="draw()" style="width:100%; min-width:220px; margin-top:20px;margin-bottom:20px;font-size:20px;">Draw Sankey</button> | |
</div> | |
<div style="margin-bottom:20px;display:block;overflow: hidden;white-space: nowrap;"> | |
<span><button onclick="load()" style="width:45%; min-width:100px; float:left;">Load</button></span> | |
<span><button onclick="save()" style="width:45%; min-width:100px; float:right;">Save</button></span> | |
</div> | |
<div style="display:block;overflow: hidden;white-space: nowrap;"> | |
<span><button onclick="addnode()" style="width:45%; min-width:100px; float:left;">Add new node</button></span> | |
<span><button onclick="removenode()" style="width:45%; min-width:100px; float:right;">Remove node</button></span> | |
</div> | |
<div class="likeform" id="nodes-form"> | |
<div><input type="text" value="Oil"/></div> | |
<div><input type="text" value="Natural Gas" /></div> | |
<div><input type="text" value="Coal"/></div> | |
<div><input type="text" value="Fossil Fuels"/></div> | |
<div><input type="text" value="Electricity"/></div> | |
<div><input type="text" value="Energy"/></div> | |
</div> | |
</div> | |
<div style="display:block;overflow: hidden;white-space: nowrap;"> | |
<div style="display:block;overflow: hidden;white-space: nowrap;"> | |
<span><button onclick="addlink()" style="width:45%; min-width:100px; float:left;">Add new link</button></span> | |
<span><button onclick="removelink()" style="width:45%; min-width:100px; float:right;">Remove link</button></span> | |
</div> | |
<div class="likeform" id="links-form"> | |
<div><input type="text" value="0,3,15"/></div> | |
<div><input type="text" value="1,3,20"/></div> | |
<div><input type="text" value="2,3,25"/></div> | |
<div><input type="text" value="2,4,25"/></div> | |
<div><input type="text" value="3,5,60"/></div> | |
<div><input type="text" value="4,5,25"/></div> | |
<div><input type="text" value="4,4,5"/></div> | |
</div> | |
</div> | |
</div> | |
<!--SANKEY DIAGRAM--> | |
<div id="chart" style="z-index:5;"></div> | |
<!---------SCRIPTS-----------> | |
<script src="js/sankey.js"></script> | |
<script src="js/food.js"></script> | |
</div> | |
<!--SOCIAL--> | |
<div id="social" style="opacity:0; z-index:800;"> | |
<span class='st_facebook'></span> | |
<span class='st_twitter'></span> | |
<span class='st_googleplus'></span> | |
<span class='st_linkedin'></span> | |
<span class='st_tumblr'></span> | |
<span class='st_reddit'></span> | |
<span class='st_email'></span> | |
<span><a href="http://www.csaladen.es" title="created by" alt="created by">© D.C. 2014</a></span> | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
http://sankey.csaladen.es |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
d3.sankey=function(){function u(){i.forEach(function(e){e.sourceLinks=[];e.targetLinks=[]});s.forEach(function(e){var t=e.source,n=e.target;if(typeof t==="number")t=e.source=i[e.source];if(typeof n==="number")n=e.target=i[e.target];t.sourceLinks.push(e);n.targetLinks.push(e)})}function a(){i.forEach(function(e){e.value=Math.max(d3.sum(e.sourceLinks,g),d3.sum(e.targetLinks,g))})}function f(){function n(r){r.index=t++;r.lowIndex=r.index;r.onStack=true;e.push(r);if(r.sourceLinks){r.sourceLinks.forEach(function(e){var t=e.target;if(!t.hasOwnProperty("index")){n(t);r.lowIndex=Math.min(r.lowIndex,t.lowIndex)}else if(t.onStack){r.lowIndex=Math.min(r.lowIndex,t.index)}});if(r.lowIndex===r.index){var i=[],s;do{s=e.pop();s.onStack=false;i.push(s)}while(s!=r);o.push({root:r,scc:i})}}}var e=[],t=0;i.forEach(function(e){if(!e.index){n(e)}});o.forEach(function(e,t){e.index=t;e.scc.forEach(function(e){e.component=t})})}function l(){function u(e){return[].concat.apply([],e)}function a(){var e=o,t,n,r=0;while(e.length){t=[];n={};e.forEach(function(e){e.x=r;e.scc.forEach(function(r){r.sourceLinks.forEach(function(r){if(!n.hasOwnProperty(r.target.component)&&r.target.component!=e.index){t.push(o[r.target.component]);n[r.target.component]=true}})})});e=t;++r}}function f(e,n){var r=[e],i=1,s=0;var o=0;while(i>0){var u=r.shift();i--;if(!u.hasOwnProperty("x")){u.x=o;u.dx=t;var a=n(u);r=r.concat(a);s+=a.length}if(i==0){o++;i=s;s=0}}}a();o.forEach(function(e,t){f(e.root,function(e){var n=e.sourceLinks.filter(function(e){return e.target.component==t}).map(function(e){return e.target});return n})});var e=0;var n=d3.nest().key(function(e){return e.x}).sortKeys(d3.ascending).entries(o).map(function(e){return e.values});var e=-1,s=-1;n.forEach(function(t){t.forEach(function(t){t.x=e+1;t.scc.forEach(function(e){e.x=t.x+e.x;s=Math.max(s,e.x)})});e=s});i.filter(function(e){var t=e.sourceLinks.filter(function(e){return e.source.name!=e.target.name});return t.length==0}).forEach(function(t){t.x=e});p((r[0]-t)/Math.max(e,1))}function c(){i.forEach(function(e){if(!e.targetLinks.length){e.x=d3.min(e.sourceLinks,function(e){return e.target.x})-1}})}function h(e){i.forEach(function(t){if(!t.sourceLinks.length){t.x=e-1}})}function p(e){i.forEach(function(t){t.x*=e})}function d(e){function u(){var e=d3.min(t,function(e){return(r[1]-(e.length-1)*n)/d3.sum(e,g)});t.forEach(function(t){t.forEach(function(t,n){t.y=n;t.dy=t.value*e})});s.forEach(function(t){t.dy=t.value*e})}function a(e){function n(e){return m(e.source)*e.value}t.forEach(function(t,r){t.forEach(function(t){if(t.targetLinks.length){var r=d3.sum(t.targetLinks,n)/d3.sum(t.targetLinks,g);t.y+=(r-m(t))*e}})})}function f(e){function n(e){return m(e.target)*e.value}t.slice().reverse().forEach(function(t){t.forEach(function(t){if(t.sourceLinks.length){var r=d3.sum(t.sourceLinks,n)/d3.sum(t.sourceLinks,g);t.y+=(r-m(t))*e}})})}function l(){t.forEach(function(e){var t,i,s=0,o=e.length,u;e.sort(c);for(u=0;u<o;++u){t=e[u];i=s-t.y;if(i>0)t.y+=i;s=t.y+t.dy+n}i=s-n-r[1];if(i>0){s=t.y-=i;for(u=o-2;u>=0;--u){t=e[u];i=t.y+t.dy+n-s;if(i>0)t.y-=i;s=t.y}}})}function c(e,t){return e.y-t.y}var t=d3.nest().key(function(e){return e.x}).sortKeys(d3.ascending).entries(i).map(function(e){return e.values});u();l();for(var o=1;e>0;--e){f(o*=.99);l();a(o);l()}}function v(){function e(e,t){return e.source.y-t.source.y}function t(e,t){return e.target.y-t.target.y}i.forEach(function(n){n.sourceLinks.sort(t);n.targetLinks.sort(e)});i.forEach(function(e){var t=0,n=0;e.sourceLinks.forEach(function(e){e.sy=t;t+=e.dy});e.targetLinks.forEach(function(e){e.ty=n;n+=e.dy})})}function m(e){return e.y+e.dy/2}function g(e){return e.value}var e={},t=24,n=8,r=[1,1],i=[],s=[],o=[];e.nodeWidth=function(n){if(!arguments.length)return t;t=+n;return e};e.nodePadding=function(t){if(!arguments.length)return n;n=+t;return e};e.nodes=function(t){if(!arguments.length)return i;i=t;return e};e.links=function(t){if(!arguments.length)return s;s=t;return e};e.size=function(t){if(!arguments.length)return r;r=t;return e};e.layout=function(t){u();a();f();l();d(t);v();return e};e.relayout=function(){v();return e};e.reversibleLink=function(){function t(t,n){var r=n.source.x+n.source.dx,i=n.target.x,s=d3.interpolateNumber(r,i),o=s(e),u=s(1-e),a=n.source.y+n.sy,f=n.target.y+n.ty,l=n.source.y+n.sy+n.dy,c=n.target.y+n.ty+n.dy;switch(t){case 0:return"M"+r+","+a+"L"+r+","+(a+n.dy);case 1:return"M"+r+","+a+"C"+o+","+a+" "+u+","+f+" "+i+","+f+"L"+i+","+c+"C"+u+","+c+" "+o+","+l+" "+r+","+l+"Z";case 2:return"M"+i+","+f+"L"+i+","+(f+n.dy)}}function n(e,t){function i(e){return e.source.y+e.sy>e.target.y+e.ty?-1:1}function s(e,t){return e+","+t+" "}var n=30;var r=15;var o=i(t)*r,u=t.source.x+t.source.dx,a=t.source.y+t.sy,f=t.target.x,l=t.target.y+t.ty;switch(e){case 0:return"M"+s(u,a)+"C"+s(u,a)+s(u+n,a)+s(u+n,a+o)+"L"+s(u+n,a+o+t.dy)+"C"+s(u+n,a+t.dy)+s(u,a+t.dy)+s(u,a+t.dy)+"Z";case 1:return"M"+s(u+n,a+o)+"C"+s(u+n,a+3*o)+s(f-n,l-3*o)+s(f-n,l-o)+"L"+s(f-n,l-o+t.dy)+"C"+s(f-n,l-3*o+t.dy)+s(u+n,a+3*o+t.dy)+s(u+n,a+o+t.dy)+"Z";case 2:return"M"+s(f-n,l-o)+"C"+s(f-n,l)+s(f,l)+s(f,l)+"L"+s(f,l+t.dy)+"C"+s(f,l+t.dy)+s(f-n,l+t.dy)+s(f-n,l+t.dy-o)+"Z"}}var e=.5;return function(e){return function(r){if(r.source.x<r.target.x){return t(e,r)}else{return n(e,r)}}}};e.link=function(){function t(t){var n=t.source.x+t.source.dx,r=t.target.x,i=d3.interpolateNumber(n,r),s=i(e),o=i(1-e),u=t.source.y+t.sy+t.dy/2,a=t.target.y+t.ty+t.dy/2;return"M"+n+","+u+"C"+s+","+u+" "+o+","+a+" "+r+","+a}var e=.5;t.curvature=function(n){if(!arguments.length)return e;e=+n;return t};return t};return e} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment