Last active
August 30, 2016 03:13
-
-
Save AlessandraSozzi/6b44ab81fb2692f8aa4a to your computer and use it in GitHub Desktop.
Relationships among teams
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: 20%; | |
height: 100%; | |
vertical-align: top; | |
} | |
#Chart { | |
margin-top: 0px; | |
display: inline-block; | |
width: 75%; | |
height: 100%; | |
} | |
</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":29, "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 = 800 * 3/16, | |
outerCircleRadius = 800 * 3/8; | |
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]) | |
.links(links) | |
.nodes(nodes) | |
.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 of the arrows in the description | |
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: 80, y: 50 * (i + 1)}); // explicit the objects to prevent error in some browsers | |
}); | |
// 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() | |
{ | |
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(); | |
//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(); | |
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)); | |
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, // delta X | |
dy = p.y-start.y; // delta Y | |
var rotAngle = Math.atan(dy/dx) * RADtoDEG; | |
if (dx < 0) { | |
rotAngle += 180; | |
} | |
if (dx == 0) { // Avoid 0/0 | |
rotAngle = startAngle; | |
// console.log("angle: " + rotAngle); | |
// console.log("dx: " + dx); | |
// console.log("dy: " + dy); | |
} | |
// 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