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" }, | |
// Objects | |
{ "id": 7, "name": "NULL Object" }, | |
{ "id": 8, "name": "Tarmac" }, | |
{ "id": 9, "name": "No Dock" }, | |
{ "id": 10, "name": "Underground Buildings" }, | |
{ "id": 11, "name": "Hangars" }, | |
{ "id": 12, "name": "Perimiter Walls" }, | |
{ "id": 13, "name": "Perimiter Tower" }, | |
{ "id": 14, "name": "Perimiter Fences" }, | |
// Behaviors | |
{ "id": 15, "name": "NULL Behavior" }, | |
{ "id": 16, "name": "Organization Of The Base" }, | |
{ "id": 17, "name": "Controlling Movements" }, | |
{ "id": 18, "name": "Housing Airplanes" }, | |
{ "id": 19, "name": "Housing Objects" }, | |
{ "id": 20, "name": "Not Landing - Fixed Wing" }, | |
{ "id": 21, "name": "Landing Helicopter" }, | |
{ "id": 22, "name": "Stability" }, | |
// Systems | |
{ "id": 23, "name": "NULL System" }, | |
{ "id": 24, "name": "Chinese Military Base" }, | |
{ "id": 25, "name": "Shoreline" }, | |
{ "id": 26, "name": "Base And Port" }, | |
{ "id": 27, "name": "Horn of Africa Bases" }, | |
{ "id": 28, "name": "Perimiter Security" }, | |
{ "id": 29, "name": "Air Traffic" }, | |
{ "id": 30, "name": "Road Network" }, | |
// // Enviornments | |
{ "id": 31, "name": "NULL Enviornment" }, | |
{ "id": 32, "name": "Commercial Port Nextdoor" }, | |
{ "id": 33, "name": "Djibouti" }, | |
{ "id": 34, "name": "April - July Timeframe" }, | |
// // Purposes | |
{ "id": 35, "name": "NULL Purpose" }, | |
{ "id": 36, "name": "Conduct Operations Unobserved" }, | |
{ "id": 37, "name": "Facade" }, | |
{ "id": 38, "name": "Anti Piracy" }, | |
{ "id": 39, "name": "Storage for Humanitarian Supplies" }, | |
{ "id": 40, "name": "Security" }, | |
{ "id": 41, "name": "Intel" }, | |
// Theories | |
{ "id": 42, "name": "NULL Theory" }, | |
{ "id": 43, "name": "Chinese Goverment Approach To Africa" }, | |
{ "id": 44, "name": "Physical Propertium" }, | |
// // Consequences | |
{ "id": 45, "name": "NULL Consequence" }, | |
{ "id": 46, "name": "Anti Piracy / Humanitarian" }, | |
{ "id": 47, "name": "Secure Facility" }, | |
{ "id": 48, "name": "Intel Outpost" }, | |
{ "id": 49, "name": "Not Big Exercises" } | |
], | |
"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 Entities | |
{ "source": 7, "target": 15, "value": 1 }, | |
{ "source": 15, "target": 23, "value": 1 }, | |
{ "source": 23, "target": 31, "value": 1 }, | |
{ "source": 31, "target": 35, "value": 1 }, | |
{ "source": 35, "target": 42, "value": 1 }, | |
{ "source": 42, "target": 45, "value": 1 }, | |
// NULL Object, Organization of the base | |
{ "source": 7, "target": 16, "value": 1 }, | |
// Organization of the base, Null System | |
{ "source": 16, "target": 23, "value": 1 }, | |
// Tarmac, Stability | |
{ "source": 8, "target": 22, "value": 1 }, | |
// Stability, NULL System | |
{ "source": 22, "target": 23, "value": 1 }, | |
// Tarmac, Landing Helicopter | |
{ "source": 8, "target": 21, "value": 1 }, | |
// Tarmac, Not Landing - Fixed Wing | |
{ "source": 8, "target": 20, "value": 1 }, | |
// No Dock, Not Landing - Fixed Wing | |
{ "source": 9, "target": 20, "value": 1 }, | |
// Underground Buildings, Landing Helicopter | |
{ "source": 10, "target": 21, "value": 1 }, | |
// Underground Buildings, Not Landing - Fixed Wing | |
{ "source": 10, "target": 20, "value": 1 }, | |
// Underground Buildings, NULL Behaviour | |
{ "source": 10, "target": 15, "value": 1 }, | |
// Hangars, Housing Objects | |
{ "source": 11, "target": 19, "value": 1 }, | |
// Housing Object, Null System | |
{ "source": 19, "target": 23, "value": 1 }, | |
// Hangars, Housing Airplanes | |
{ "source": 11, "target": 18, "value": 1 }, | |
// Housing Airplanes, NULL System | |
{ "source": 18, "target": 23, "value": 1 }, | |
// Perimiter Walls, Controlling Movements | |
{ "source": 12, "target": 17, "value": 1 }, | |
// Perimiter Fences, Controlling Movements | |
{ "source": 14, "target": 17, "value": 1 }, | |
// Perimiter Tower, Controlling Movements | |
{ "source": 13, "target": 17, "value": 1 }, | |
// NULL Object, NULL Behavior | |
{ "source": 7, "target": 15, "value": 1 }, | |
// Landing Helicopter, Base + Port | |
{ "source": 21, "target": 26, "value": 1 }, | |
// Not Landing Fixed Wing, Chinese Military Base | |
{ "source": 20, "target": 24, "value": 1 }, | |
// Not Landing fixed wing, Base + Port | |
{ "source": 20, "target": 26, "value": 1 }, | |
// Not Landing fixed wing, Horn of Africa Base | |
{ "source": 20, "target": 27, "value": 1 }, | |
// Controlling Movements, Perimeter Security | |
{ "source": 17, "target": 28, "value": 1 }, | |
// NULL Object, Air Traffic | |
{ "source": 13, "target": 29, "value": 1 }, | |
// Air Traffic, Null System | |
{ "source": 29, "target": 23, "value": 1 }, | |
// NULL Behavior, Road Network | |
{ "source": 13, "target": 30, "value": 1 }, | |
// Road Network, Null System | |
{ "source": 30, "target": 23, "value": 1 }, | |
// Chinese Military Base, Djibouti | |
{ "source": 24, "target": 33, "value": 1 }, | |
// Shoreline, NULL Behavior | |
{ "source": 15, "target": 25, "value": 1 }, | |
// Shoreline, Djibouti | |
{ "source": 25, "target": 33, "value": 1 }, | |
// Base + Ports, Commercial Port Next Door | |
{ "source": 26, "target": 32, "value": 1 }, | |
// Base + Ports, Djibouti | |
{ "source": 26, "target": 33, "value": 1 }, | |
// Horn of Africa Bases, Djibouti | |
{ "source": 27, "target": 33, "value": 1 }, | |
// Perimeter Security, Commercial Port Next Door | |
{ "source": 28, "target": 32, "value": 1 }, | |
// Perimeter Security, Djibouti | |
{ "source": 28, "target": 33, "value": 1 }, | |
// Perimeter Security, April - July Timeframe | |
{ "source": 28, "target": 34, "value": 1 }, | |
// Perimeter Security, NULL Enviorment | |
{ "source": 28, "target": 31, "value": 1 }, | |
// Commercial Port Next Door, Anti Piracy | |
{ "source": 32, "target": 38, "value": 1 }, | |
// Commercial Port Next Door, Storage for humanitarian supplies | |
{ "source": 32, "target": 39, "value": 1 }, | |
// Djibouti, Facade | |
{ "source": 33, "target": 37, "value": 1 }, | |
// Djibouti, Anti Piracy | |
{ "source": 33, "target": 38, "value": 1 }, | |
// Djibouti, Storage for humanitarian supplies | |
{ "source": 33, "target": 39, "value": 1 }, | |
// Djibouti, Intel | |
{ "source": 33, "target": 41, "value": 1 }, | |
// April - June Timeframe, Security | |
{ "source": 34, "target": 40, "value": 1 }, | |
// NULL Enviorment, Security | |
{ "source": 31, "target": 40, "value": 1 }, | |
// Facade, Chinese Gov't Apporoach to Africa | |
{ "source": 37, "target": 43, "value": 1 }, | |
// Anti Piracy, Chinese Gov't Apporoach to Africa | |
{ "source": 38, "target": 43, "value": 1 }, | |
// Anti Piracy, Physical Propertium | |
{ "source": 38, "target": 44, "value": 1 }, | |
// Storage for Humanitarian Supplies, Chinese Gov't Apporoach to Africa | |
{ "source": 39, "target": 43, "value": 1 }, | |
// Security, Physical Propertium | |
{ "source": 40, "target": 44, "value": 1 }, | |
// Intel, Null Theory | |
{ "source": 41, "target": 42, "value": 1 }, | |
// Chinese Gov't Approach to Africa, Anti Piracy / Humanitarian | |
{ "source": 43, "target": 46, "value": 1 }, | |
// Physical Propertium, Secure Facility | |
{ "source": 44, "target": 47, "value": 1 }, | |
// Chinese Gov't Approach to Africa, Not Big Exercises | |
{ "source": 43, "target": 49, "value": 1 }, | |
// NULL Theory, Intel Outpost | |
{ "source": 42, "target": 48, "value": 1 }, | |
// Null Env, Conduct operations Unobserved | |
{ "source": 31, "target": 36, "value": 1 }, | |
// Conduct operations Unobserved, NULL Theory | |
{ "source": 36, "target": 42, "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; | |
} |