Skip to content

Instantly share code, notes, and snippets.

@fryford
Last active January 11, 2016 20:54
Show Gist options
  • Save fryford/7bf8aa8534672f7aa5bb to your computer and use it in GitHub Desktop.
Save fryford/7bf8aa8534672f7aa5bb to your computer and use it in GitHub Desktop.
Relationships among teams 2
<!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