Demo a Sankey Diagram in CodePen to prep for use as a Widget.
A Pen by Luke Kuenneke on CodePen.
<p id="chart"> |
Demo a Sankey Diagram in CodePen to prep for use as a Widget.
A Pen by Luke Kuenneke on CodePen.
function getViewPortWidth() { | |
return Math.max(document.documentElement.clientWidth, window.innerWidth || 0) * 0.99; | |
} | |
function getViewPortHeight() { | |
return Math.max(document.documentElement.clientHeight, window.innerHeight || 0) * 0.985; | |
} | |
var margin = { | |
top: 1, | |
right: 1, | |
bottom: 6, | |
left: 1 | |
}, | |
width = getViewPortWidth() - margin.left - margin.right, | |
height = getViewPortHeight() - margin.top - margin.bottom; | |
var formatNumber = d3.format(",.0f"), | |
format = function (d) { | |
return formatNumber(d) + " TWh"; | |
}, | |
color = d3.scale.category20(); | |
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(20) | |
.nodePadding(50) | |
.size([width, height]); | |
var path = sankey.link(); | |
var json_data = { | |
nodes: [ | |
// Columns | |
{ "id": 0, "name": "Object" }, | |
{ "id": 1, "name": "Behavior" }, | |
{ "id": 2, "name": "System" }, | |
{ "id": 3, "name": "Enviornment" }, | |
{ "id": 4, "name": "Purpose" }, | |
{ "id": 5, "name": "Theory" }, | |
{ "id": 6, "name": "Consequence" }, | |
// NULL Nodes | |
{ "id": 7, "name": "NULL Object" }, | |
{ "id": 8, "name": "NULL Behavior" }, | |
{ "id": 9, "name": "NULL System" }, | |
{ "id": 10, "name": "NULL Enviornment" }, | |
{ "id": 11, "name": "NULL Purpose" }, | |
{ "id": 12, "name": "NULL Theory" }, | |
{ "id": 13, "name": "NULL Consequence" }, | |
// Objects | |
{ "id": 14, "name": "Housing" }, | |
{ "id": 15, "name": "3 Layers of Struct" }, | |
{ "id": 16, "name": "Military Base" }, | |
{ "id": 17, "name": "Hardened Underground Structures" }, | |
{ "id": 18, "name": "Much Smaller" }, | |
{ "id": 19, "name": "No Docks" }, | |
{ "id": 20, "name": "Tarmac" }, | |
{ "id": 21, "name": "Tower" }, | |
{ "id": 22, "name": "Helicopter Hangar" }, | |
// Behaviors | |
{ "id": 23, "name": "More Fortified Than USA, France, Japan" }, | |
{ "id": 24, "name": "Ongoing Construction" }, | |
{ "id": 25, "name": "No Docks" }, | |
{ "id": 26, "name": "All False" }, | |
// Systems | |
{ "id": 27, "name": "China" }, | |
{ "id": 28, "name": "Djibouti" }, | |
{ "id": 29, "name": "Hardened Underground Structures" }, | |
{ "id": 30, "name": "Much Smaller" }, | |
// Enviornments | |
{ "id": 31, "name": "Chinese Deception" }, | |
{ "id": 32, "name": "Horn of Africa Resources" }, | |
{ "id": 33, "name": "Much Smaller" }, | |
{ "id": 34, "name": "Pirates In The Region" }, | |
// Purposes | |
{ "id": 35, "name": "Humanitarian" }, | |
{ "id": 36, "name": "Anti Piracy" }, | |
{ "id": 37, "name": "Global Naval Force" }, | |
{ "id": 38, "name": "Unknown Purpose of Underground Facilities" }, | |
{ "id": 39, "name": "Military!" }, | |
{ "id": 40, "name": "Presence Among Other Nations (Allied Partners)" }, | |
{ "id": 41, "name": "Masked by Construction" }, | |
{ "id": 42, "name": "Deception" }, | |
// Theories | |
{ "id": 43, "name": "Global Naval Force" }, | |
{ "id": 44, "name": "Naval Force Have Mark of A Super Power" }, | |
{ "id": 45, "name": "Small Naval Force" }, | |
{ "id": 46, "name": "Non Military" }, | |
{ "id": 47, "name": "Non Civil or Military O/D Posture" }, | |
{ "id": 48, "name": "Level of Security Preperation" }, | |
{ "id": 49, "name": "Facade" }, | |
// Consequences | |
{ "id": 50, "name": "Establishing a Global Naval Force" }, | |
{ "id": 51, "name": "Humanitarian Aid / Anti-Piracy Policing" }, | |
{ "id": 52, "name": "Establishing a Global Military Force" }, | |
{ "id": 53, "name": "Draw Attention" }, | |
], | |
"links": [ | |
// Headers | |
{ "source": 0, "target": 1, "value": 1 }, | |
{ "source": 1, "target": 2, "value": 1 }, | |
{ "source": 2, "target": 3, "value": 1 }, | |
{ "source": 3, "target": 4, "value": 1 }, | |
{ "source": 4, "target": 5, "value": 1 }, | |
{ "source": 5, "target": 6, "value": 1 }, | |
// Null Nodes | |
{ "source": 7, "target": 8, "value": 1 }, | |
{ "source": 8, "target": 9, "value": 1 }, | |
{ "source": 9, "target": 10, "value": 1 }, | |
{ "source": 10, "target": 11, "value": 1 }, | |
{ "source": 11, "target": 12, "value": 1 }, | |
{ "source": 12, "target": 13, "value": 1 }, | |
// Object -> Behavior | |
// Housing, More Fortified | |
{ "source": 14, "target": 23, "value": 1 }, | |
// Housing, Ongoing Construction | |
{ "source": 14, "target": 24, "value": 1 }, | |
// Housing, No Docks | |
{ "source": 14, "target": 25, "value": 1 }, | |
// 3 Layers of Struct, More Fortified | |
{ "source": 15, "target": 23, "value": 1 }, | |
// 3 Layers of Struct, Ongoing Construction | |
{ "source": 15, "target": 24, "value": 1 }, | |
// 3 Layers of Struct, No Docks | |
{ "source": 15, "target": 25, "value": 1 }, | |
// Military Base, More Fortified | |
{ "source": 16, "target": 23, "value": 1 }, | |
// Military Base, Ongoing Construction | |
{ "source": 16, "target": 24, "value": 1 }, | |
// Military Base, No Docks | |
{ "source": 16, "target": 25, "value": 1 }, | |
// Hardened Underground Structures, More Fortified | |
{ "source": 17, "target": 23, "value": 1 }, | |
// Hardened Underground Structures, Ongoing Construction | |
{ "source": 17, "target": 24, "value": 1 }, | |
// Hardened Underground Structures, No Docks | |
{ "source": 17, "target": 25, "value": 1 }, | |
// Much Smaller, Ongoing Construction | |
{ "source": 18, "target": 24, "value": 1 }, | |
// Much Smaller, No Docks | |
{ "source": 18, "target": 25, "value": 1 }, | |
// No Docks, Ongoing Construction | |
{ "source": 19, "target": 24, "value": 1 }, | |
// No Docks, No Docks | |
{ "source": 19, "target": 25, "value": 1 }, | |
// Tarmac, More Fortified | |
{ "source": 20, "target": 23, "value": 1 }, | |
// Tarmac, Ongoing Construction | |
{ "source": 20, "target": 24, "value": 1 }, | |
// Tower, More Fortified | |
{ "source": 21, "target": 23, "value": 1 }, | |
// Tower, Ongoing Construction | |
{ "source": 21, "target": 24, "value": 1 }, | |
// Tower, No Docks | |
{ "source": 21, "target": 25, "value": 1 }, | |
// Helicopter Hangar, More Fortified | |
{ "source": 22, "target": 23, "value": 1 }, | |
// Helicopter Hangar, Ongoing Construction | |
{ "source": 22, "target": 24, "value": 1 }, | |
// Helicopter Hangar, No Docks | |
{ "source": 22, "target": 25, "value": 1 }, | |
// Helicopter Hangar, All False | |
{ "source": 22, "target": 26, "value": 1 }, | |
// Behavior -> System | |
// More Fortified, China | |
{ "source": 23, "target": 27, "value": 1 }, | |
// More Fortified, Djibouti | |
{ "source": 23, "target": 28, "value": 1 }, | |
// More Fortified, Hardened Underground Structures | |
{ "source": 23, "target": 29, "value": 1 }, | |
// Ongoing Construction, China | |
{ "source": 24, "target": 27, "value": 1 }, | |
// Ongoing Construction, Djibouti | |
{ "source": 24, "target": 28, "value": 1 }, | |
// Ongoing Construction, Hardened Underground Structures | |
{ "source": 24, "target": 29, "value": 1 }, | |
// Ongoing Construction, Much Smaller | |
{ "source": 24, "target": 30, "value": 1 }, | |
// No Docks, China | |
{ "source": 25, "target": 27, "value": 1 }, | |
// No Docks, Djibouti | |
{ "source": 25, "target": 28, "value": 1 }, | |
// No Docks, Hardened Underground Structures | |
{ "source": 25, "target": 29, "value": 1 }, | |
// No Docks, Much Smaller | |
{ "source": 25, "target": 30, "value": 1 }, | |
// All False, China | |
{ "source": 26, "target": 27, "value": 1 }, | |
// All False, Djibouti | |
{ "source": 26, "target": 28, "value": 1 }, | |
// All False, Hardened Underground Structures | |
{ "source": 26, "target": 29, "value": 1 }, | |
// All False, Much Smaller | |
{ "source": 26, "target": 30, "value": 1 }, | |
// System -> Environment | |
// China, Chinese Deception | |
{ "source": 27, "target": 31, "value": 1 }, | |
// China, Horn of Africa Resources | |
{ "source": 27, "target": 32, "value": 1 }, | |
// China, Much Smaller | |
{ "source": 27, "target": 33, "value": 1 }, | |
// Djibouti, Chinese Deception | |
{ "source": 28, "target": 31, "value": 1 }, | |
// Djibouti, Horn of Africa Resources | |
{ "source": 28, "target": 32, "value": 1 }, | |
// Djibouti, Much Smaller | |
{ "source": 28, "target": 33, "value": 1 }, | |
// Djibouti, Pirates in the region | |
{ "source": 28, "target": 34, "value": 1 }, | |
// Hardened Underground Structures, Chinese Deception | |
{ "source": 29, "target": 31, "value": 1 }, | |
// Hardened Underground Structures, Horn of Africa Resources | |
{ "source": 29, "target": 32, "value": 1 }, | |
// Hardened Underground Structures, Much Smaller | |
{ "source": 29, "target": 33, "value": 1 }, | |
// Much Smaller, Chinese Deception | |
{ "source": 30, "target": 31, "value": 1 }, | |
// Much Smaller, Horn of Africa Resources | |
{ "source": 30, "target": 32, "value": 1 }, | |
// Much Smaller, Much Smaller | |
{ "source": 30, "target": 33, "value": 1 }, | |
// Environment -> Purpose | |
// Chinese Deception, Masked by Construction | |
{ "source": 31, "target": 41, "value": 1 }, | |
// Chinese Deception, Deception | |
{ "source": 31, "target": 42, "value": 1 }, | |
// Chinese Deception, Anti Piracy | |
{ "source": 31, "target": 36, "value": 1 }, | |
// Chinese Deception, Global Navy Force | |
{ "source": 31, "target": 37, "value": 1 }, | |
// Chinese Deception, Masked by Construction | |
{ "source": 31, "target": 41, "value": 1 }, | |
// Chinese Deception, Unknown Purpose of Underground Facilities | |
{ "source": 31, "target": 38, "value": 1 }, | |
// Horn of Africa, Anti Piracy | |
{ "source": 32, "target": 36, "value": 1 }, | |
// Horn of Africa, Global Navy Force | |
{ "source": 32, "target": 37, "value": 1 }, | |
// Horn of Africa, Unknown Purpose of Underground Facilities | |
{ "source": 32, "target": 38, "value": 1 }, | |
// Much Smaller, Global Naval Force | |
{ "source": 33, "target": 37, "value": 1 }, | |
// Much Smaller, Unknown Purpose of Underground Facilities | |
{ "source": 33, "target": 38, "value": 1 }, | |
// Pirates in The Region. Humanitarian | |
{ "source": 34, "target": 35, "value": 1 }, | |
// Pirates in The Region. Anti Piracy | |
{ "source": 34, "target": 36, "value": 1 }, | |
//NULL Environment, Military! | |
{ "source": 10, "target": 39, "value": 1 }, | |
//NULL Environment, Presence Among Other Allied Nations | |
{ "source": 10, "target": 40, "value": 1 }, | |
// Purpose -> Theory | |
// Humanitarian, Non Military | |
{ "source": 35, "target": 46, "value": 1 }, | |
// Humanitarian, Facade | |
{ "source": 35, "target": 49, "value": 1 }, | |
// Anti Piracy, Facade | |
{ "source": 36, "target": 49, "value": 1 }, | |
// Anti Piracy, Non Military | |
{ "source": 36, "target": 46, "value": 1 }, | |
// Global Naval Force, Global Naval Force | |
{ "source": 37, "target": 43, "value": 1 }, | |
// Global Naval Force, Naval Force Hallmark of a Super Power | |
{ "source": 37, "target": 44, "value": 1 }, | |
// Global Naval Force, Small Naval Force | |
{ "source": 37, "target": 45, "value": 1 }, | |
// Unknown Purpose of Underground Facilities, Facade | |
{ "source": 38, "target": 49, "value": 1 }, | |
// Unknown Purpose of Underground Facilities, Non Civil / Military O/D Posture | |
{ "source": 38, "target": 47, "value": 1 }, | |
// Unknown Purpose of Underground Facilities, Level of Security / Preparation | |
{ "source": 38, "target": 48, "value": 1 }, | |
// Military!, Facade | |
{ "source": 39, "target": 49, "value": 1 }, | |
// Military!, Non Civil / Military O/D Posture | |
{ "source": 39, "target": 47, "value": 1 }, | |
// Military!, Level of Security / Preparation | |
{ "source": 39, "target": 48, "value": 1 }, | |
// Presence Among Other Allied Nations, Non Civil / Military O/D Posture | |
{ "source": 40, "target": 47, "value": 1 }, | |
// Presence Among Other Allied Nations, Level of Security / Preparation | |
{ "source": 40, "target": 48, "value": 1 }, | |
// Masked By Construction, Facade | |
{ "source": 41, "target": 49, "value": 1 }, | |
// Deception, Facade | |
{ "source": 42, "target": 49, "value": 1 }, | |
// Theory -> Consequence | |
// Global Naval Force, Establishing a Global Naval Force | |
{ "source": 43, "target": 50, "value": 1 }, | |
// Naval Force Hallmark of a Super Power, Establishing a Global Naval Force | |
{ "source": 44, "target": 50, "value": 1 }, | |
// Small Naval Force, Establishing a Global Naval Force | |
{ "source": 45, "target": 50, "value": 1 }, | |
// Non Military, Humanitarian Aid / Anti Piracy | |
{ "source": 46, "target": 51, "value": 1 }, | |
// Non Civil / Military O/D Posture, Establish Global Military Force | |
{ "source": 47, "target": 52, "value": 1 }, | |
// Level of Security / Preparation. Establish Global Military Force | |
{ "source": 48, "target": 52, "value": 1 }, | |
// Facade, Draw Attention | |
{ "source": 49, "target": 53, "value": 1 }, | |
] | |
}; | |
d3.json(JSON.stringify(json_data), function (error) { | |
sankey | |
.nodes(json_data.nodes) | |
.links(json_data.links) | |
.layout(32); | |
var link = svg.append("g").selectAll(".link") | |
.data(json_data.links) | |
.enter().append("path") | |
.attr("class", "link") | |
.attr("d", path) | |
.attr("id", function(d,i){ | |
d.id = i; | |
return "link-"+i; | |
}) | |
.style("stroke-width", function(d) { return Math.max(1, d.dy); }) | |
.sort(function(a, b) { return b.dy - a.dy; }); | |
link.append("title") | |
.text(function(d) { return d.source.name + " → " + d.target.name + "\n" + format(d.value); }); | |
var node = svg.append("g").selectAll(".node") | |
.data(json_data.nodes) | |
.enter().append("g") | |
.attr("class", "node") | |
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }) | |
.on("click", highlightNodeLinks) | |
.call(d3.behavior.drag() | |
.origin(function(d) { return d; }) | |
.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 = 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 highlightNodeLinks(node,i){ | |
var remainingNodes=[], | |
nextNodes=[]; | |
var stroke_opacity = 0; | |
if( d3.select(this).attr("data-clicked") == "1" ){ | |
d3.select(this).attr("data-clicked","0"); | |
stroke_opacity = 0.2; | |
}else{ | |
d3.select(this).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); | |
} | |
}); | |
<script src="https://d3js.org/d3.v2.min.js?2.9.1"></script> | |
<script src="https://bost.ocks.org/mike/sankey/sankey.js"></script> |
#chart { | |
height: 500px; | |
} | |
.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: .5 !important; | |
} | |