D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
tomshanley
Full window
Github gist
Sankey with circular references
<!DOCTYPE html> <html> <head> <script type="text/javascript" src="https://d3js.org/d3.v2.js"></script> <script type="text/javascript" src="sankey.js"></script> <title>Sankey Diagram</title> <style> #chart { height: 800px; } .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; } .cycleLink { fill: #600; opacity: .2; stroke: none; stroke-linejoin: "round"; } .cycleLink:hover { opacity: .5; } .link:hover { stroke-opacity: .5; } </style> </head> <body> <h2>Random Chart</h2> <p>The chart below is made from randomly generated data. If it looks strange, try refreshing a few times. </p> <p> The hope is to generate cycles in the data. The cycles are styled as red to call them out for this example. This rendering contains what I arrived at as the most reasonable representation of cycles, featuring a smaller 'lane' at the top of the chart where cycles travel. </p> <p id="chart"></p> <script> var margin = {top: 1, right: 1, bottom: 6, left: 1}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var formatNumber = d3.format(",.0f"), format = function(d) { return formatNumber(d) + " tuples"; }, color = d3.scale.category20(); var sankey = d3.sankey() .nodeWidth(15) .nodePadding(10) .size([width, height]); var svg = d3.select("#chart").append("svg") .attr( "preserveAspectRatio", "xMinYMid meet" ) .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom); var rootGraphic = svg .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var path = sankey.link(); function createChart( energy ) { sankey .nodes(energy.nodes) .links(energy.links) .layout(32); var allgraphics = svg.append("g").attr("id", "node-and-link-container" ); var link = allgraphics.append("g").attr("id", "link-container") .selectAll(".link") .data(energy.links) .enter().append("path") .attr("class", function(d) { return (d.causesCycle ? "cycleLink" : "link") }) .attr("d", path) .sort(function(a, b) { return b.dy - a.dy; }) ; link.filter( function(d) { return !d.causesCycle} ) .style("stroke-width", function(d) { return Math.max(1, d.dy); }) link.append("title") .text(function(d) { return d.source.name + " -> " + d.target.name + "\n" + format(d.value); }); var node = allgraphics.append("g").attr("id", "node-container") .selectAll(".node") .data(energy.nodes) .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)); 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(/ .*/, "")); }) .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("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 dragmove(d) { d3.select(this).attr("transform", "translate(" + d.x + "," + (d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))) + ")"); sankey.relayout(); link.attr("d", path); } // I need to learn javascript var numCycles = 0; for( var i = 0; i< sankey.links().length; i++ ) { if( sankey.links()[i].causesCycle ) { numCycles++; } } var cycleTopMarginSize = (sankey.cycleLaneDistFromFwdPaths() - ( (sankey.cycleLaneNarrowWidth() + sankey.cycleSmallWidthBuffer() ) * numCycles ) ) var horizontalMarginSize = ( sankey.cycleDistFromNode() + sankey.cycleControlPointDist() ); svg = d3.select("#chart").select("svg") .attr( "viewBox", "" + (0 - horizontalMarginSize ) + " " // left + cycleTopMarginSize + " " // top + (960 + horizontalMarginSize * 2 ) + " " // width + (500 + (-1 * cycleTopMarginSize)) + " " ); // height }; function generateRandomData() { var dataObject = new Object(); var mostNodes = 20; var mostLinks = 40; var numNodes = Math.floor((Math.random()*mostNodes)+1); var numLinks = Math.floor((Math.random()*mostLinks)+1); // Generate nodes dataObject.nodes = new Array(); for( var n = 0; n < numNodes; n++ ) { var node = new Object(); node.name = "Node-" + n; dataObject.nodes[n] = node; } // Generate links dataObject.links = new Array(); for( var i = 0; i < numLinks; i++ ) { var link = new Object(); link.target = link.source = Math.floor((Math.random()*numNodes)); while( link.source === link.target ) { link.target = Math.floor((Math.random()*numNodes)); } link.value = Math.floor((Math.random() * 100) + 1); dataObject.links[i] = link; } return dataObject; } var energyData = generateRandomData(); createChart( energyData ); </script> </body> </html>
Modified
http://d3js.org/d3.v2.js
to a secure url
https://d3js.org/d3.v2.js