Skip to content

Instantly share code, notes, and snippets.

@tomshanley
Last active May 11, 2018 00:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomshanley/e3f754dbfdeefdf7c1f5177aae5afb81 to your computer and use it in GitHub Desktop.
Save tomshanley/e3f754dbfdeefdf7c1f5177aae5afb81 to your computer and use it in GitHub Desktop.
Network flow with happy path
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
body { margin:50;top:50;right:50;bottom:50;left:50; }
</style>
</head>
<body>
<script>
let data = [
{
"source": "node1",
"target": "node2",
"value": 20,
"mainflow": true
},
{
"source": "node1",
"target": "node3",
"value": 8,
"mainflow": false
},
{
"source": "node1",
"target": "node4",
"value": 5,
"mainflow": false
},
{
"source": "node2",
"target": "node1",
"value": 9,
"mainflow": false
},
{
"source": "node2",
"target": "node3",
"value": 18,
"mainflow": true
},
{
"source": "node2",
"target": "node4",
"value": 5,
"mainflow": false
},
{
"source": "node3",
"target": "node1",
"value": 5,
"mainflow": false
},
{
"source": "node3",
"target": "node2",
"value": 3,
"mainflow": false
},
{
"source": "node3",
"target": "node4",
"value": 15,
"mainflow": true
},
{
"source": "node4",
"target": "node1",
"value": 5,
"mainflow": false
},
{
"source": "node4",
"target": "node2",
"value": 8,
"mainflow": false
},
{
"source": "node4",
"target": "node3",
"value": 5,
"mainflow": false
}
]
let radians = 0.0174532925
let width = 1000
let height = 300
let centre = height/2
let nestedData = d3.nest()
.key(function(d){ return d.source })
.entries(data)
nestedData.forEach(function(d){
d.total = d.values.reduce(function(sum, v){ return sum + v.value }, 0)
})
let series = nestedData.map(function(d){ return d.key })
let radius = d3.scaleSqrt()
.domain([0, d3.max(nestedData, function(d){ return d.total })])
.range([0, 50])
let angle = d3.scalePoint()
.domain(series)
.range([0, 90])
let strokeWidth = d3.scaleLinear()
.domain([0, d3.max(data, function(d){ return d.value })])
.range([0, 50])
let nodeCentreX = d3.scalePoint()
.padding(0.5)
.domain(series)
.range([0,width])
let colour = d3.scaleOrdinal()
.domain(series)
.range(["rgb(53,97,143)", "rgb(42,210,127)", "rgb(108,33,142)", "rgb(100,212,253)"])
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var g = svg.append("g")
var nodes = g.selectAll("circle")
.data(nestedData)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + nodeCentreX(d.key) + "," + centre + ")"
})
var links = nodes.selectAll("line")
.data(function(d){ return d.values })
.enter()
.append("line")
.style("stroke", function(d) { return colour(d.target) })
.style("stroke-width", function(d) { return strokeWidth(d.value) })
.attr("x2", function(d){ return x2(d.source, d.target, d.mainflow) })
.attr("y2", function(d){ return y2(d.source, d.target, d.mainflow) })
nodes.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", function(d){ return radius(d.total) })
.style("fill", function(d) { return colour(d.key) })
.style("stroke", "white")
.style("stroke-width", 5)
nodes.append("text")
.text(function(d){ return d.key })
.style("text-anchor", "middle")
.style("fill", "white")
function x2(source, target, main){
return linkLength(main) * Math.sin(getAngle(source, target) * radians)
}
function y2(source, target, main){
return linkLength(main) * Math.cos(getAngle(source, target) * radians)
}
function getAngle(source, target){
let offset1 = 120 + angle(source)
let offset2 = angle(target)
let totalOffset = offset1 - offset2
return totalOffset
}
function linkLength(isMainFlow) {
return isMainFlow ? nodeCentreX.step() : nodeCentreX.step() * 0.6
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment