sankey-init.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*This software is released under the MIT License
  2. MIT License 2014 Denes Csala http://www.csaladen.es
  3. The following software uses the javascript frameworks below,
  4. all of which are distributed under the MIT or GNU/GPL license:
  5. D3.js http://d3js.org/ data-oriented javascript framework.
  6. - Sankey plugin http://bost.ocks.org/mike/sankey/ for D3.js (modified) by Mike Bostock,
  7. which is based on the initial version http://tamc.github.io/Sankey/ by Thomas Counsell.
  8. I have incorporated the ability to render Sankey cycles, as pioneered by https://github.com/cfergus
  9. - Dragdealer.js href="http://skidding.github.io/dragdealer/ by Ovidiu Chereches
  10. */
  11. //<!--DATA ENTRY-->
  12. nodesform=d3.select("#nodes-form");
  13. function addnode() {
  14. var a=nodesform.append("div");
  15. a.text(nodesform[0][0].children.length-1+' ');
  16. a.append("input").attr("value",'{"name":"New Node"}');
  17. }
  18. function removenode() {
  19. nodesform[0][0].children[nodesform[0][0].children.length-1].remove("div")
  20. }
  21. linksform=d3.select("#links-form");
  22. function addlink() {
  23. linksform.append("div").append("input").attr("value",'{"source":0,"target":1,"value":0.52}');
  24. }
  25. function removelink() {
  26. linksform[0][0].children[linksform[0][0].children.length-1].remove("div")
  27. }
  28. function draw() {
  29. change(data);
  30. }
  31. function save(){
  32. d3.select('#save').style('z-index',100).transition().style('opacity',0.9);
  33. st='{"sankey":{"nodes":['
  34. for (i = 0; i < nodesform[0][0].children.length; i++) {
  35. st=st+nodesform[0][0].children[i].children[0].value+',';
  36. }
  37. st=st.substring(0, st.length - 1)+'],"links":[';
  38. for (i = 0; i < linksform[0][0].children.length; i++) {
  39. st=st+linksform[0][0].children[i].children[0].value+',';
  40. }
  41. st = st.substring(0, st.length - 1)+']},"params":['+densityslider.value.current[0]+','+opacityslider.value.current[0]+','+labelformat+','+labeltextformat+','+showlinkcount+']';
  42. if (document.getElementById("fixedlayout").checked){
  43. var coords=[]
  44. sankey.nodes().forEach(function(d){
  45. coords.push([d.x,d.y])
  46. })
  47. st=st+',"fixedlayout":'+JSON.stringify(coords);
  48. }
  49. st=st+'}';
  50. d3.select('#savetext').text(st);
  51. }
  52. function load(){
  53. d3.select('#load').style('z-index',100).transition().style('opacity',0.9);
  54. }
  55. function loadsubmit(){
  56. d3.select('#load').transition().style('opacity',0).style('z-index',-1);
  57. var rawtext=d3.select('#load')[0][0].children[1].value;
  58. if (rawtext!="") {
  59. //parse data
  60. var rawdata=JSON.parse(rawtext);
  61. if ("sankey" in rawdata) {
  62. var newdata=rawdata.sankey;
  63. }
  64. else {
  65. var newdata=rawdata;
  66. }
  67. var loadtext=JSON.stringify(newdata)
  68. //remove existing node entry boxes
  69. var n=nodesform[0][0].children.length;
  70. for (i = 0; i < n; i++) {
  71. nodesform[0][0].children[0].remove("div");
  72. }
  73. //remove existing link entry boxes
  74. var n=linksform[0][0].children.length;
  75. for (i = 0; i < n; i++) {
  76. linksform[0][0].children[0].remove("div");
  77. }
  78. //add new node entry boxes
  79. var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"nodes":[')+8, loadtext.indexOf('"links":[')-1));
  80. for (i = 0; i < newdata2.length; i++) {
  81. var a=nodesform.append("div");
  82. a.text(nodesform[0][0].children.length-1+' ');
  83. a.append("input").attr("value",JSON.stringify(newdata2[i]));
  84. }
  85. //add new link entry boxes
  86. var newdata2=JSON.parse(loadtext.substring(loadtext.indexOf('"links":[')+8, loadtext.length - 1))
  87. for (i = 0; i < newdata2.length; i++) {
  88. linksform.append("div").append("input").attr("value",JSON.stringify(newdata2[i]));
  89. }
  90. //set parameters
  91. if ("fixedlayout" in rawdata) {
  92. fixedlayout=document.getElementById("ignorelayout").checked?[]:rawdata.fixedlayout;
  93. } else {
  94. fixedlayout=[];
  95. }
  96. if ("params" in rawdata) {
  97. labelformat=rawdata.params[2];
  98. labeltextformat=rawdata.params[3];
  99. if (rawdata.params.length>4) showlinkcount=rawdata.params[4];
  100. else showlinkcount=0;
  101. document.getElementById("vlabel").checked=(labelformat==0)?true:false;
  102. document.getElementById("tlabel").checked=(labeltextformat==0)?true:false;
  103. document.getElementById("clabel").checked=(showlinkcount==1)?true:false;
  104. densityslider.setValue(rawdata.params[0]);
  105. opacityslider.setValue(rawdata.params[1]);
  106. }
  107. else {
  108. change(newdata);
  109. }
  110. }
  111. }
  112. //<!--SANKEY DIAGRAM-->
  113. var parallelrendering=false;
  114. var minnodewidth = 50;
  115. var padding = 28;
  116. var labelformat = 0;
  117. var labeltextformat = 0;
  118. var showlinkcount = 0;
  119. var paddingmultiplier = 100;
  120. var lowopacity = 0.3;
  121. var highopacity = 0.7;
  122. var fixedlayout=[];
  123. var format2Number = d3.format(",.2f"),
  124. format1Number = d3.format(",.1f"),
  125. format3Number = d3.format(",.3f"),
  126. formatNumber = d3.format(",.0f"),
  127. format = function(a) {
  128. return formatNumber(a)
  129. },color = d3.scale.category20();
  130. linkformat= function(a) {
  131. // if (d3.select("#ldec").node().value==0) return formatNumber(a);
  132. // if (d3.select("#ldec").node().value==1) return format1Number(a);
  133. // if (d3.select("#ldec").node().value==2) return format2Number(a);
  134. return formatNumber(a);
  135. },
  136. nodeformat= function(a) {
  137. // if (d3.select("#ndec").node().value==0) return formatNumber(a);
  138. // if (d3.select("#ndec").node().value==1) return format1Number(a);
  139. // if (d3.select("#ndec").node().value==2) return format2Number(a);
  140. return formatNumber(a);
  141. };
  142. //
  143. // d3.select("#ndec")
  144. // .on("change",draw);
  145. // d3.select("#ldec")
  146. // .on("change",draw);
  147. d3.select("#chart").style("width", document.getElementById("chart").offsetWidth)
  148. var margin = {
  149. top: 10,
  150. right: 10,
  151. bottom: 10,
  152. left: 40
  153. },
  154. width = document.getElementById("chart").offsetWidth - margin.left - margin.right,
  155. height = document.getElementById("chart").offsetHeight - margin.bottom;
  156. var svg = d3.select("#chart").append("svg")
  157. svg.append("rect").attr("x",0).attr("y",0).attr("width","100%").attr("height","100%").attr("fill","white").attr('class','background').on('mouseup', () => redraw())
  158. svg=svg.attr("width", width + margin.left + margin.right).attr("height", height + margin.top + margin.bottom).append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
  159. //set svg background color via rectangle trick
  160. d3.select("#chart").select("svg")
  161. var sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
  162. var path = sankey.reversibleLink();
  163. var change = function(d) {
  164. svg.selectAll("g").remove();
  165. sankey = d3.sankey().nodeWidth(30).nodePadding(padding).size([width, height]);
  166. sankey.nodes(d.nodes).links(d.links).layout(500);
  167. var g = svg.append("g") //link
  168. .selectAll(".link").data(d.links).enter().append("g").attr("class", "link").sort(function(j, i) {
  169. return i.dy - j.dy
  170. });
  171. var h = g.append("path") //path0
  172. .attr("d", path(0));
  173. var f = g.append("path") //path1
  174. .attr("d", path(1));
  175. var e = g.append("path") //path2
  176. .attr("d", path(2));
  177. g.attr("fill", function(i) {
  178. if (i.fill) return i.fill;
  179. else if (i.source.fill) return i.source.fill;
  180. else return i.source.color = color(i.source.name.replace(/ .*/, ""))
  181. }).attr("opacity", lowopacity).on("mouseover", function(d) {
  182. d3.select(this).style('opacity', highopacity);
  183. }).on("mouseout", function(d) {
  184. d3.select(this).style('opacity', lowopacity);
  185. }).append("title") //link
  186. .text(function(i) {
  187. return i.source.name + " → " + i.target.name + "\n" + linkformat(i.value)
  188. });
  189. var c = svg.append("g") //node
  190. .selectAll(".node").data(d.nodes).enter().append("g").attr("class", "node").attr("transform", function(i) {
  191. return "translate(" + i.x + "," + i.y + ")"
  192. }).call(d3.behavior.drag().origin(function(i) {
  193. return i
  194. }).on("dragstart", function() {
  195. this.parentNode.appendChild(this)
  196. }).on("drag", b));
  197. c.append("rect") //node
  198. .attr("height", function(i) {
  199. return i.dy
  200. }).attr("width", sankey.nodeWidth()).style("fill", function(i) {
  201. if (i.fill) return i.color = i.fill;
  202. else return i.color = color(i.name.replace(/ .*/, ""))
  203. }).style("stroke", function(i) {
  204. return d3.rgb(i.color).darker(2)
  205. }).on("mouseover", function(d) {
  206. svg.selectAll(".link").filter(function(l) {
  207. return l.source == d || l.target == d;
  208. }).transition().style('opacity', highopacity);
  209. }).on("mouseout", function(d) {
  210. svg.selectAll(".link").filter(function(l) {
  211. return l.source == d || l.target == d;
  212. }).transition().style('opacity', lowopacity);
  213. }).on("dblclick", function(d) {
  214. svg.selectAll(".link").filter(function(l) {
  215. return l.target == d;
  216. }).attr("display", function() {
  217. if (d3.select(this).attr("display") == "none") return "inline"
  218. else return "none"
  219. });
  220. }).append("title").text(function(i) {
  221. return i.name + "\n" + nodeformat(i.value)
  222. });
  223. c.append("text") //node
  224. .attr("x", -6).attr("y", function(i) {
  225. return i.dy / 2
  226. }).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
  227. .text(function(i) {
  228. if (labeltextformat<1){
  229. return i.name;
  230. } else {
  231. return "";
  232. }
  233. }).filter(function(i) {
  234. return i.x < width / 2
  235. }).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
  236. if (showlinkcount>0) c.append("text") //node
  237. .attr("x", -6).attr("y", function(i) {
  238. return i.dy / 2 + 20
  239. }).attr("dy", ".35em").attr("text-anchor", "end").attr("font-size","16px")
  240. .text(function(i) {
  241. return "→ "+(i.targetLinks.length)+" | "+(i.sourceLinks.length)+" →";
  242. }).filter(function(i) {
  243. return i.x < width / 2
  244. }).attr("x", 6 + sankey.nodeWidth()).attr("text-anchor", "start")
  245. c.append("text") //node
  246. .attr("x", function(i) {return -i.dy / 2})
  247. .attr("y", function(i) {return i.dx / 2 + 9})
  248. .attr("transform", "rotate(270)").attr("text-anchor", "middle").attr("font-size","23px").text(function(i) {
  249. if ((i.dy>minnodewidth)&&(labelformat<1)){
  250. return nodeformat(i.value);
  251. }
  252. }).attr("fill",function(d){
  253. return d3.rgb(d["color"]).brighter(2)
  254. }).attr("stroke",function(d){
  255. return d3.rgb(d["color"]).darker(2)
  256. }).attr("stroke-width","1px");
  257. svg.selectAll('rect')
  258. .on('mouseover', (a, b, c, d, e) => {console.log('mouseover', a, b, c, d, e)})
  259. .on('mouseout', (a, b, c, d, e) => {console.log('mouseout', a, b, c, d, e)})
  260. .on('mousedown', (a, b, c, d, e) => {console.log('mousedown', a, b, c, d, e)})
  261. .on('mouseup', a => !a.expanded && redraw({type: a.name, criteria: 'ID'}));
  262. function b(i) { //dragmove
  263. // if (document.getElementById("ymove").checked) {
  264. // if (document.getElementById("xmove").checked) {
  265. // 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))) + ")")
  266. // } else {
  267. dragged = true;
  268. d3.select(this).attr("transform", "translate(" + i.x + "," + (i.y = Math.max(0, Math.min(height - i.dy, d3.event.y))) + ")")
  269. // }
  270. // } else {
  271. // if (document.getElementById("xmove").checked) {
  272. // d3.select(this).attr("transform", "translate(" + (i.x = Math.max(0, Math.min(width - i.dx, d3.event.x))) + "," + i.y + ")")
  273. // }
  274. // }
  275. sankey.relayout();
  276. f.attr("d", path(1));
  277. h.attr("d", path(0));
  278. e.attr("d", path(2))
  279. };
  280. };
  281. draw();
  282. function seturl(){
  283. exportInlineSVG(d3.select("#chart").select("svg").node(), function(data) {
  284. d3.select("#pngdownload").node().href=data;
  285. });
  286. }