forked from AlessandraSozzi's block: Relationships among teams
Last active
January 11, 2016 20:54
-
-
Save fryford/7bf8aa8534672f7aa5bb to your computer and use it in GitHub Desktop.
Relationships among teams 2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<link href='https://fonts.googleapis.com/css?family=Noto+Sans:400,700' rel='stylesheet' type='text/css'> | |
<head> | |
<style> | |
.background { | |
fill: #fff; | |
} | |
h1 { | |
text-align: center; | |
font-family: 'Noto Sans', sans-serif; | |
color: black; | |
font-size: 30px; | |
margin-left: 0.3cm; | |
} | |
text { | |
text-shadow: 0 0.5px 0 #000, 0.5px 0 0 #000, 0 -0.5px 0 #000, -0.5px 0 0 #000; | |
pointer-events: none; | |
color: #fff; | |
} | |
p { | |
margin-left: 0.3cm; | |
} | |
.labels { | |
font-size: 30px; | |
font-family: 'Noto Sans', sans-serif; | |
color: slategray; | |
} | |
.leglabels { | |
font-family: 'Noto Sans', sans-serif; | |
color: slategray; | |
} | |
.link { | |
stroke: slategray; | |
stroke-opacity: 0.6; | |
fill: none; | |
} | |
.leglink { | |
stroke: slategray; | |
stroke-opacity: 0.6; | |
} | |
.row { | |
width: 100%; | |
margin: 0 auto; | |
} | |
#Legend { | |
margin-top: 0px; | |
display: inline-block; | |
width: 300px; | |
height: 800px; | |
vertical-align: top; | |
} | |
#Chart { | |
margin-top: 0px; | |
display: inline-block; | |
width: 1000px; | |
height: 800px; | |
} | |
</style> | |
<title> WHO YOU WORK WITH </title> | |
</head> | |
<body> | |
<script src="http://d3js.org/d3.v3.min.js" type="text/javascript"></script> | |
<div class= "row"> | |
<div id="Legend"> | |
<br> | |
<br> | |
<br> | |
<div id = "title"><h1>Who you work with</h1></div> | |
<br> | |
<br> | |
<div class = "description"> | |
<p>The chart shows the relationships within the teams of the methodology division and among the other disions in ONS. To see the connections move the mouse over each team.<p> | |
<br> | |
<p>The size of the arrow relates to the strength of the relationship between two nodes.<p> | |
<br> | |
<p>The relationship can be classified in: <p> | |
</div> | |
<div id = "Arrows"> | |
</div> | |
</div> | |
<div id="Chart"></div> | |
</div> | |
<script> | |
// Extend array prototype | |
Array.prototype.contains = function(obj) { | |
var i = this.length; | |
while (i--) { | |
if (this[i] === obj) { | |
return true; | |
} | |
} | |
return false; | |
} | |
graph = | |
{ | |
"nodes":[ | |
{"name":"Big Data", "group":0, "id": 0}, | |
{"name":"SDC", "group":0, "id": 1}, | |
{"name":"TU", "group":0, "id": 2}, | |
{"name":"SAE", "group":0, "id": 3}, | |
{"name":"DLAF", "group":0, "id": 4}, | |
{"name":"EI", "group":0, "id": 5}, | |
{"name":"DM", "group":0, "id": 6}, | |
{"name":"Population", "group":0, "id": 7}, | |
{"name":"DCM", "group":0, "id": 8}, | |
{"name":"QC", "group":0, "id": 9}, | |
{"name":"Geography", "group":0, "id": 10}, | |
{"name":"Census", "group":1, "id": 11}, | |
{"name":"Prices", "group":1, "id": 12}, | |
{"name":"Technical Services", "group":1, "id": 13}, | |
{"name":"ADD", "group":1, "id": 14}, | |
{"name":"GSS", "group":1, "id": 15}, | |
{"name":"Population Statistics", "group":1, "id": 16}, | |
{"name":"National Accounts", "group":1, "id": 17}, | |
{"name":"BSS", "group":1, "id": 18}, | |
{"name":"Labour Market", "group":1, "id": 19}, | |
{"name":"CP Secretariat", "group":1, "id": 20}, | |
{"name":"Communi- cations", "group":1, "id": 21}, | |
{"name":"Well Being", "group":1, "id": 22}, | |
{"name":"Life Events", "group":1, "id": 23}, | |
{"name":"Service Centre", "group":1, "id": 24}, | |
{"name":"Business Data", "group":1, "id": 25}, | |
{"name":"EHEI", "group":1, "id": 26}, | |
{"name":"Public Policy", "group":1, "id": 27}, | |
{"name":"UKSA", "group":1, "id": 28}, | |
{"name":"Digital Publishing", "group":1, "id": 29}, | |
{"name":"Social Survey", "group":1, "id": 30} | |
], | |
"links":[ | |
{"source":0, "target":11, "value":1}, | |
{"source":0, "target":12, "value":1}, | |
{"source":0, "target":13, "value":1}, | |
{"source":0, "target":14, "value":1}, | |
{"source":0, "target":15, "value":1}, | |
{"source":0, "target":16, "value":2}, | |
{"source":0, "target":17, "value":2}, | |
{"source":0, "target":18, "value":2}, | |
{"source":0, "target":19, "value":2}, | |
{"source":0, "target":20, "value":3}, | |
{"source":0, "target":21, "value":3}, | |
{"source":0, "target":22, "value":3}, | |
{"source":0, "target":30, "value":3}, | |
{"source":4, "target":14, "value":2}, | |
{"source":4, "target":18, "value":2}, | |
{"source":4, "target":23, "value":2}, | |
{"source":4, "target":30, "value":2}, | |
{"source":4, "target":24, "value":2}, | |
{"source":4, "target":11, "value":2}, | |
{"source":1, "target":25, "value":1}, | |
{"source":1, "target":26, "value":3}, | |
{"source":1, "target":11, "value":1}, | |
{"source":1, "target":27, "value":2}, | |
{"source":1, "target":16, "value":3}, | |
{"source":1, "target":23, "value":2}, | |
{"source":1, "target":20, "value":2}, | |
{"source":1, "target":30, "value":2}, | |
{"source":1, "target":2, "value":1}, | |
{"source":1, "target":5, "value":2}, | |
{"source":7, "target":11, "value":1}, | |
{"source":7, "target":16, "value":3}, | |
{"source":7, "target":27, "value":3}, | |
{"source":7, "target":13, "value":2}, | |
{"source":7, "target":0, "value":1}, | |
{"source":7, "target":4, "value":2}, | |
{"source":7, "target":3, "value":2}, | |
{"source":7, "target":8, "value":3}, | |
{"source":3, "target":11, "value":1}, | |
{"source":3, "target":16, "value":2}, | |
{"source":3, "target":27, "value":1}, | |
{"source":3, "target":22, "value":2}, | |
{"source":3, "target":19, "value":3}, | |
{"source":3, "target":0, "value":1}, | |
{"source":3, "target":7, "value":1}, | |
{"source":3, "target":6, "value":1}, | |
{"source":6, "target":16, "value":1}, | |
{"source":6, "target":11, "value":1}, | |
{"source":6, "target":14, "value":2}, | |
{"source":6, "target":30, "value":3}, | |
{"source":6, "target":24, "value":2}, | |
{"source":6, "target":2, "value":2}, | |
{"source":6, "target":3, "value":2}, | |
{"source":6, "target":1, "value":2}, | |
{"source":6, "target":4, "value":2}, | |
{"source":6, "target":9, "value":2}, | |
{"source":10, "target":11, "value":1}, | |
{"source":10, "target":27, "value":1}, | |
{"source":10, "target":7, "value":1}, | |
{"source":10, "target":29, "value":3}, | |
{"source":10, "target":23, "value":2}, | |
{"source":10, "target":30, "value":2}, | |
{"source":10, "target":28, "value":1}, | |
{"source":10, "target":19, "value":2}, | |
{"source":10, "target":22, "value":3} | |
] | |
}; | |
var width = 1000, | |
height = 800; | |
var svg = d3.select("#Chart").append("svg") | |
.attr("width", width) | |
.attr("height", height); | |
var originX = width/2, | |
originY = height/2, | |
innerCircleRadius = 150, | |
outerCircleRadius = 300; | |
var RADtoDEG = 180/Math.PI; // Conversion from radiants to degrees | |
var inOriginX = function(r) { return originX + ((innerCircleRadius) * Math.sin(r))}; | |
var inOriginY = function(r) { return originY - ((innerCircleRadius) * Math.cos(r))}; | |
var outOriginX = function(r) { return originX + ((outerCircleRadius) * Math.sin(r))}; | |
var outOriginY = function(r) { return originY - ((outerCircleRadius) * Math.cos(r))}; | |
var circ = Math.PI * 2; | |
innerRad = d3.range(0, circ, circ/11); | |
outerRad = d3.range(0, circ, circ/20); | |
graph.nodes.forEach( function(node) { | |
rad = node.group === 0 ? innerRad[node.id] : outerRad[node.id-11]; | |
// console.log(rad); | |
node.pos = node.group === 0 ? [inOriginX(rad), inOriginY(rad)] : [outOriginX(rad), outOriginY(rad)]; | |
}); | |
var nodes = graph.nodes, | |
links = graph.links; | |
var force = d3.layout.force() | |
.charge(-400) | |
.linkDistance(300) | |
.size([width, height]) | |
.nodes(nodes) | |
.links(links) | |
.on("tick", tick); | |
var node = svg.selectAll(".node"), | |
link = svg.selectAll(".link") | |
marker = svg.selectAll(".marker"); | |
var markerPaths = d3.scale.ordinal().domain([3,2,1]).range(["M0,-5L10,0L0,5", "M0,-7.5L15,0L0,7.5", "M0,-10L20,0L0,10"]), | |
markerEndPoint = d3.scale.ordinal().domain([3,2,1]).range([40, 45, 50]); | |
// Legend | |
var legend = d3.select("#Arrows") | |
.append("svg") | |
.attr("width", 300) | |
.attr("height", 300); | |
var arrows = ["WEAK", "MEDIUM", "STRONG"]; | |
var posArrow = []; | |
arrows.forEach(function (d, i) { | |
var x = 80; | |
var y = 50 * (i + 1); | |
posArrow.push({x, y}); | |
}); | |
// console.log(posArrow); | |
legend.selectAll(".leglink") | |
.data(arrows) | |
.enter() | |
.append("svg:line") | |
.attr("class", "leglink") | |
.attr("x1", 10) | |
.attr("y1", function (d, i) { return posArrow[i].y; }) | |
.attr("x2", 80) | |
.attr("y2", function (d, i) { return posArrow[i].y; }) | |
.attr("stroke-width", function (d, i) { | |
return Math.pow(i + 1, 2); }); | |
legend.selectAll(".gmarker") | |
.data(arrows) | |
.enter().append("g") | |
.attr("class", "gmarker") | |
.append("path") | |
.attr("class", "marker") // This section adds in the arrows | |
.attr("d", function (d, i) { return markerPaths(3 - i); }) | |
.attr("transform", function (d, i) { | |
var pos = [posArrow[i].x, posArrow[i].y]; | |
return "translate(" + pos + ")"; }) | |
.style("fill", "slategray"); | |
legend.selectAll(".leglabels") | |
.data(arrows) | |
.enter() | |
.append("text") | |
.attr("class", "leglabels") | |
.attr("text-anchor", "start") | |
.attr("x", 130) | |
.attr("y", function (d, i) { return posArrow[i].y + 4; }) | |
.text(function (d) { return d; }) | |
.attr("font-size", "15px"); | |
drawGraph(); | |
function drawGraph() | |
{ | |
//Add nodes with texts in them | |
node = node.data(force.nodes()); | |
node.enter().append("g").attr("id", function(d) { return d.id }) | |
.attr("class", "gnode") | |
.on("mouseover", handleMouseOver) | |
.on("mouseout", handleMouseOut); | |
node.append("circle") | |
.attr("class", function(d) { return "node group" + d.group }) | |
.attr("r", 30) | |
.style("fill", function(d) { if(d.group == 0) return "mediumaquamarine"; else return "LightSalmon" }); | |
node.append("text") | |
.attr("text-anchor", "middle") | |
.attr("dy", "1") | |
.text(function(d) { return d.name; }) | |
.attr("font-size", "12px") | |
.call(wrap, 60) | |
// .attr("font-weight", "bold") | |
.style("fill", "white"); | |
node.exit().remove(); | |
link = link.data(force.links()); | |
link.enter().append("g").attr("class", function(d) { return "glink n" + d.source + " " + "n" + d.target }).style("opacity", 0); | |
link.append("path") | |
.attr("class", "link") | |
.attr("stroke-width", function(d) { return Math.pow(4 - d.value, 2); }); | |
link.append("g") | |
.attr("class", "gmarker") | |
.append("path") | |
.attr("class", "marker") // This section adds in the arrows | |
.attr("d", function (d) { return markerPaths(d.value); }) | |
.attr("transform", function (d) { | |
var rad = innerRad[d.source]; | |
pos = [inOriginX(rad), inOriginY(rad)] | |
return "translate(" + pos + ")"; }) | |
.style("fill", "slategray"); | |
link.exit().remove(); | |
var arc = d3.svg.arc() | |
.outerRadius(function(d,i) { return i === 0 ? 190 : 340 }) | |
.startAngle(0) | |
.endAngle(Math.PI); | |
svg.selectAll(".arcs").data(["METHODOLOGY DIVISION", "ONS"]).enter().append("path") | |
.attr("class", "arcs") | |
.attr("d", arc) | |
.style("fill", "none") | |
.attr("transform", "translate(500, 400)") | |
.attr("id", function(d,i) { return "wavy" + i; }); | |
svg.selectAll(".labels").data(["METHODOLOGY DIVISION", "ONS"]).enter().append("text") | |
.attr("dy", "0") | |
.attr("class", "labels") | |
.append("textPath") //append a textPath to the text element | |
.attr("xlink:href", function(d,i) { return "#wavy" + i; }) //place the ID of the path here | |
.style("text-anchor","middle") //place the text halfway on the arc | |
.attr("startOffset", "20%") | |
.text(function(d) { return d; }); | |
force.start(); | |
} | |
function tick() | |
{ | |
node.attr("transform", function(d) { return "translate(" + d.pos + ")";}); | |
svg.selectAll(".link").attr("d", function(d) { | |
var dx = d.target.pos[0] - d.source.pos[0], | |
dy = d.target.pos[1] - d.source.pos[1], | |
dr = Math.sqrt(dx * dx + dy * dy); | |
return "M" + d.source.pos[0] + "," + d.source.pos[1] + "A" + dr + "," + dr + " 0 0,1 " + d.target.pos[0] + "," + d.target.pos[1]; | |
}); | |
} | |
function handleMouseOver(d, i) { // Add interactivity | |
svg.selectAll(".n" + i).style("opacity", 1).transition().duration(300).attrTween("stroke-dasharray", tweenDash); | |
var ids = [i]; | |
links.forEach(function (link) { | |
// console.log(link); | |
if (link.source.id === i || link.target.id === i) | |
ids.push(link.target.id, link.source.id); | |
}) | |
// console.log(ids); | |
svg.selectAll(".gnode").transition().duration(300).style("opacity", function () { | |
return ids.contains(+this.id) ? 1 : .3; | |
}); | |
svg.selectAll(".labels").transition().duration(300).style("opacity", .2); | |
} | |
function handleMouseOut(d, i) { | |
svg.selectAll(".n" + i).style("opacity", 0); | |
svg.selectAll(".gnode").transition().duration(300).style("opacity", 1); | |
svg.selectAll(".labels").transition().duration(300).style("opacity", 1); | |
} | |
// Returns an attrTween for translating along the specified path element. | |
function tweenDash() { | |
var that = this; | |
// console.log(that); | |
var arrow = d3.select(that).select(".marker"); | |
// console.log(arrow); | |
var line = d3.select(that).select(".link"); | |
var diff = markerEndPoint(line.data()[0].value); | |
// console.log(markerEndPoint(line.data()[0].value)); | |
var l = line.node().getTotalLength() - diff; | |
// console.log(line.node().getTotalLength()); | |
// console.log(l); | |
var i = d3.interpolateString("0," + l, l + "," + l); | |
var start = line.node().getPointAtLength(0); | |
var startAngle = 0; | |
return function(t) { | |
var p = line.node().getPointAtLength(t * l); | |
var dx = p.x-start.x, | |
dy = p.y-start.y; | |
var rotAngle = Math.atan(dy/dx) * RADtoDEG; | |
if (dx < 0) { | |
rotAngle += 180; | |
} | |
// console.log(rotAngle); | |
if (isNaN(rotAngle)) { // Avoid 0/0 | |
rotAngle = startAngle; | |
// console.log("angle: " + rotAngle); | |
// console.log("dx: " + dx); | |
// console.log("dy: " + dy); | |
} | |
arrow.attr("transform", "translate(" + p.x + "," + p.y + ") rotate(" + rotAngle + ")");//move marker | |
start.x = p.x; | |
start.y = p.y; | |
startAngle = rotAngle; | |
return i(t); | |
}; | |
} | |
// From https://gist.github.com/mbostock/7555321 | |
function wrap(text, width) { | |
text.each(function() { | |
var text = d3.select(this), | |
words = text.text().split(/\s+/).reverse(), | |
word, | |
line = [], | |
lineNumber = 0, | |
lineHeight = 0, // ems | |
y = text.attr("y"), | |
dy = parseFloat(text.attr("dy")), | |
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", 0 + "em"); | |
// console.log(tspan); | |
while (word = words.pop()) { | |
line.push(word); | |
tspan.text(line.join(" ")); | |
if (tspan.node().getComputedTextLength() > width) { | |
line.pop(); | |
tspan.text(line.join(" ")); | |
line = [word]; | |
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); | |
} | |
} | |
}); | |
} | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment