<!DOCTYPE html>
<meta charset="utf-8">
<title>Causes of Death</title>
<script src="../jquery-1.10.2.js"></script>
<style>
#tooltip {
color: white;
opacity: .9;
background: #333;
padding: 5px;
border: 1px solid lightgrey;
border-radius: 5px;
position: absolute;
z-index: 10;
visibility: hidden;
white-space: nowrap;
pointer-events: none;
}
.node {
cursor: pointer;
stroke: #3182bd;
stroke-width: 1.5px;
}
.link {
fill: none;
stroke: #9ecae1;
stroke-width: 1.5px;
}
.c3-region.bground {
fill: green;
}
body{
font: 100% "Arial", sans-serif;
color:#3c0050;
}
#imgLinks
{
position:absolute;
left:5px;
top:0px;
height:75px;
}
#titlezone
{
position:absolute;
left:260px;
top:0px;
height:75px;
}
#dropzone
{
position:absolute;
width:600px;
top:50px;
}
h3{
margin:0;
}
#svgzone
{
position:absolute;
left:5px;
top:75px;
}
</style>
<body>
<link href="../c3/c3.css" rel="stylesheet" type="text/css">
<script src="../d3.v3.min.js"></script>
<script src="../c3/c3.min.js"></script>
<div id="tooltip"></div>
<div id="imgLinks">
<a href="http://www.nrscotland.gov.uk/" target="_blank"><img src="../NRS_smalllogo.png" border = "0" alt="NRS Home Page" height="75px" width="208px"/></a></div>
<div id="titlezone">
<h3 id="titleLine"></h3>
<a href="http://www.gro-scotland.gov.uk/statistics/theme/vital-events/general/ref-tables/2012/section-6-deaths-causes.html">National Records of Scotland (NRS) data on deaths (NRS website)</a>, Table 3.6.
<div id="dropzone"></div>
</div>
<div id="svgzone"></div>
<script>
//Style choices you may wish to change
var femaleColor="pink";
var maleColor="#6a6aff";
var minRadius = 5; //smallest possible radius for a node
//Layout Setup
var w = window,
d = document,
e = d.documentElement,
g = d.getElementsByTagName('body')[0],
pagex = w.innerWidth || e.clientWidth || g.clientWidth,
pagey = w.innerHeight|| e.clientHeight|| g.clientHeight;
var pageHeight=pagey-135;
var pageWidth=pagex-40;
var graphHeight=3*pageHeight/4;
var graphWidth=pageWidth;
var chartDim=pageHeight/4;
var textHeight=50;
var scaleFactor = 3200/Math.min(pageWidth,pageHeight);
var pieInner = 0.3*chartDim/2;
var pieOuter = 0.8*chartDim/2;
var padding=Math.min(graphHeight,graphWidth)/30;
//Global vars to be derived from initial data
var startYear,endYear,numYears,activeYear;
var pieNumerator,pieDenominator;
var xcol,yearLabels,yearIndices;
var activeM,activeF;
//Non data-driven setup
var key=0;
var labelling=0;
var labels = ["Both Sexes", "Males", "Females"];
var labelIndices = [0,1,2];
labelTypes=["Labels - Short name", "Labels- ICD10", "Labels - None"];
indices = [0,1,2];
var mcol=['Males'];
var fcol=['Females'];
//Detect touch environments
var isiOS = false;
var agent = navigator.userAgent.toLowerCase();
if( agent.indexOf('iphone') >= 0 || agent.indexOf('ipad') >= 0){
isiOS = true;
}
var isAndroid = agent.indexOf("android") > -1;
//Parse initial data and launch viz
d3.json("initial.json", function(json){
//File read
initData=json;
startYear=initData.startYear;
endYear=initData.endYear;
d3.select("#titleLine").text("Causes of death, Scotland, "+startYear+"-"+endYear);
activeM=initData.allM;
activeF=initData.allF;
//Compute variables based on data
numYears=endYear-startYear;
activeYear=numYears;
pieNumerator=activeM[activeYear];
pieDenominator=activeM[activeYear]+activeF[activeYear];
xcol = ['x'];
yearLabels=[];
yearIndices=[];
for (var i = 0; i <= numYears; i++) {
xcol.push(i+startYear);
yearIndices.push(i);
yearLabels.push(endYear-i);
}
//Got the initial data, let's start the interactive components!
runViz();
}); //Done with initial file parse
function runViz(){
// Build the dropdown menus
d3.select("#dropzone")
.append("select")
.attr("id","genderSelect")
.selectAll("option")
.data(indices)
.enter()
.append("option")
.text(function(d) {return labels[d];})
d3.select("#dropzone")
.append("select")
.attr("id","yearSelect")
.selectAll("option")
.data(yearIndices)
.enter()
.append("option")
.text(function(d) {return yearLabels[d];})
d3.select("#dropzone")
.append("select")
.attr("id","labelSelect")
.selectAll("option")
.data(labelIndices)
.enter()
.append("option")
.text(function(d) {return labelTypes[d];})
//Build region for force diagram
var svg = d3.select("#svgzone").append("svg")
.attr("width", graphWidth)
.attr("height", graphHeight);
svg.append("rect")
.attr("x",0)
.attr("y",0)
.attr("fill","rgba(120,120,120,0.2)")
.attr("stroke","rgb(0,0,0)")
.attr("stroke-width",2)
.attr("width", graphWidth)
.attr("height", graphHeight);
//Build text region
svg.append("text").attr("x",graphWidth/2-175).attr("y",graphHeight-50)
.attr("font-family","sans-serif").attr("font-size",30).attr("id","startupText").attr("fill","#3c0050")
.text(function(){
if(isiOS) return "Tap to explore!";
if(isAndroid) return "Tap to explore!";
return "Double-click to explore!"});
d3.select("#svgzone").append("div").attr("id","infoRegion").attr("style",function(){return "height:"+textHeight+"px;width:"+pageWidth+"px"});
var infosvg=d3.select("#infoRegion").append("svg").attr("width", pageWidth).attr("height", textHeight);
infosvg.append("text").attr("x",0).attr("y",textHeight/2-5).attr("font-family","sans-serif").attr("font-size",20).text("").attr("id","ICD10Text");
infosvg.append("text").attr("x",0).attr("y",textHeight/2+10).attr("font-family","sans-serif").attr("font-size",12).text("").attr("id","commentText");
//Build region for charts
d3.select("#svgzone").append("div").attr("id","pieRegion").attr("style",function(){return "height:"+chartDim+"px;width:"+chartDim+"px;float:left;"});
var svg2 = d3.select("#pieRegion").append("svg")
.attr("width", chartDim)
.attr("height", chartDim);
svg2.append("rect")
.attr("x",0)
.attr("y",0)
.attr("fill","rgba(120,120,120,0.2)")
.attr("stroke","rgb(0,0,0)")
.attr("stroke-width",2)
.attr("width", chartDim)
.attr("height", chartDim);
//Build Pie chart
svg2.append("text").attr("x",chartDim/5).attr("y",chartDim*0.1).attr("font-family","sans-serif").attr("font-size",0.075*chartDim).text("Gender ratio in "+(startYear+activeYear)).attr("id","pieYearText");
var femaleArc = d3.svg.arc()
.innerRadius(pieInner)
.outerRadius(pieOuter)
.startAngle(0)
.endAngle(2*Math.PI);
updateGenderPie();
//Build Time series chart
d3.select("#svgzone").append("div").attr("id","chart").attr("style",function(){return "height:"+chartDim+"px;width:"+pageWidth+"px"});
for (var i = 0; i <= numYears; i++) {
mcol.push(activeM[i]);
fcol.push(activeF[i]);
}
var chart = c3.generate({
bindto: '#chart',
data: {
x: 'x',
colors: {
Males: maleColor,
Females: femaleColor,
},
columns: [xcol,mcol,fcol]
},
size: {
width: pageWidth-chartDim
},
axis: {
y: {
min: 0,
padding: { top: 0, bottom: 0 },
}
}
});
//Build the force diagram
var root;
var force = d3.layout.force()
.linkDistance(Math.min(graphWidth,graphHeight)/11)
.charge(-1500)
.friction(0.9)
.size([graphWidth, graphHeight])
.on("tick", tick);
//NB SVG region initialised earlier
var link = svg.selectAll(".link"),
node = svg.selectAll(".node");
d3.json("scotland-other.json", function(json) {
root = json;
var nodes = flatten(root);
nodes.forEach(function(d) {
d._children = d.children;
d.children = null;
});
update();
});
//State maintenance for force diagram
var drag = force.drag().on("dragstart", dragstart);
function dragstart(d) {
d3.select(this).classed("fixed", d.fixed = true);
}
function tick() {
node.attr("cx", function(d) { return Math.min(graphWidth-d.r,Math.max(d.cx,d.r)); })
.attr("cy", function(d) { return Math.min(graphHeight-d.r,Math.max(d.cy,d.r)); });
node.attr("transform", function(d) { return "translate(" +Math.min(graphWidth-padding,Math.max(d.x,padding)) + "," + Math.min(graphHeight-padding,Math.max(d.y,padding)) + ")"; });
link.attr("x1", function(d) { return Math.min(graphWidth-padding,Math.max(d.source.x,padding)); })
.attr("y1", function(d) { return Math.min(graphHeight-padding,Math.max(d.source.y,padding)); })
.attr("x2", function(d) { return Math.min(graphWidth-padding,Math.max(d.target.x,padding)); })
.attr("y2", function(d) { return Math.min(graphHeight-padding,Math.max(d.target.y,padding)); });
}
//Selection control
// Node expansion
function expandNode(d) {
if (d3.event.defaultPrevented) return; // ignore drag
d3.select("#startupText").text("");
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
d3.select('#genderSelect')
.on("change", function() {
key = this.selectedIndex;
update();
});
d3.select('#yearSelect')
.on("change", function() {
activeYear = 10-this.selectedIndex;
d3.select("#pieYearText").remove();
svg2.append("text").attr("x",chartDim/5).attr("y",chartDim*0.1).attr("font-family","sans-serif").attr("font-size",0.075*chartDim).text("Gender ratio in "+(startYear+activeYear)).attr("id","pieYearText");
pieNumerator=activeM[activeYear];
pieDenominator=activeF[activeYear]+activeM[activeYear];
updateGenderPie();
update();
});
d3.select('#labelSelect')
.on("change", function() {
labelling = this.selectedIndex;
update();
});
function selectNode(d,i){
if (d3.event.defaultPrevented) return; // ignore drag
d3.select("#startupText").text("");
d3.select("#ICD10Text").text(function(){return d.ICD10+" "+d.name});
d3.select("#commentText").text(d.comment);
d3.select("#maleArc").remove();
pieNumerator= d.males[activeYear];
pieDenominator = d.males[activeYear]+d.females[activeYear];
updateGenderPie();
activeM=d.males;
activeF=d.females;
mcol=['Males'];
fcol=['Females'];
for (var i = 0; i <= numYears; i++) {
mcol.push(activeM[i]);
fcol.push(activeF[i]);
}
var chart = c3.generate({
bindto: '#chart',
data: {
x: 'x',
colors: {
Males: maleColor,
Females: femaleColor,
},
columns: [xcol,mcol,fcol]
},
size: {
width: pageWidth-chartDim
},
axis: {
y: {min: 0,padding: { top: 0, bottom: 0 },}
}
});
if(isiOS) expandNode(d); //We can't double tap on mobile
if(isAndroid) expandNode(d); //So single tap has to do perform both roles
}
//Update Functions
function update() {
var nodes = flatten(root),
links = d3.layout.tree().links(nodes);
// Restart the force layout.
force
.nodes(nodes)
.links(links)
.start();
// Update links.
link = link.data(links, function(d) { return d.target.id; });
link.exit().remove();
link.enter().insert("line", ".node")
.attr("class", "link");
// Update nodes.
node = node.data(nodes, function(d) { return d.id; });
node.exit().remove();
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on("dblclick", expandNode)
.on("mouseover",mouseOver)
.on("mouseout", function (d) { d3.select("#tooltip").style("visibility", "hidden") })
.on("click",selectNode)
.call(force.drag);
nodeEnter.append("circle")
.attr("r", nodeSize);
nodeEnter.append("text")
.attr("dy", "1.35em")
.attr("dx", function(d){return "-"+label(d).length/4+"em";})
.attr("stroke","black")
.text(function(d){return label(d)});
node.select("circle")
.style("fill", color);
node.select("text")
.attr("dx", function(d){return "-"+label(d).length/4+"em";})
.text(function(d){return label(d)});
d3.selectAll("circle").attr("r",nodeSize);
}
function updateGenderPie(){
var arc = d3.svg.arc()
.innerRadius(pieInner)
.outerRadius(pieOuter)
.startAngle(0)
.endAngle(2*Math.PI*pieNumerator/pieDenominator);
d3.select("#femaleArc").remove();
svg2.append("path")
.attr("d", femaleArc)
.attr("transform", function() { return "translate("+chartDim/2+","+1.1*chartDim/2+")"})
.attr("id","femaleArc").attr("fill",femaleColor);
svg2.append("path").attr("d", arc)
.attr("transform", function() { return "translate("+chartDim/2+","+1.1*chartDim/2+")"})
.attr("id","maleArc").attr("fill",maleColor);
};
}//end of runViz
//Decoration
function color(d) {
return d._children ? "#3182bd" // collapsed
: d.children ? "#c6dbef" // expanded
: "#fd8d3c"; // leaf
}
function label(d){
if(labelling==0) return d.sname;
if(labelling==1){
if(d.ICD10.length<=15) return d.ICD10;
return d.ICD10.substring(0,15)+"...";
};
return "";
}
function nodeSize(d) {
if (labels[key]=="Males")
return (d.children==null) ? Math.max(Math.sqrt(d.males[activeYear])/ scaleFactor,minRadius) : Math.min(minRadius,Math.sqrt(d.males[activeYear])/ scaleFactor)
if (labels[key]=="Females")
return (d.children==null) ? Math.max(Math.sqrt(d.females[activeYear])/ scaleFactor,minRadius) : Math.min(minRadius,Math.sqrt(d.females[activeYear])/ scaleFactor)
if (labels[key]=="Both Sexes")
return (d.children==null) ? Math.max(Math.sqrt((d.males[activeYear]+d.females[activeYear]))/ scaleFactor,minRadius) : Math.min(minRadius,Math.sqrt((d.males[activeYear]+d.females[activeYear]))/ scaleFactor)
}
function mouseOver(d,i) {
d3.select("#tooltip")
.style("visibility", "visible")
.html(
function() {
if (labels[key]=="Males")
return ""+d.ICD10+" "+d.name+": "+d.males[activeYear]+" males";
if (labels[key]=="Females")
return ""+d.ICD10+" "+d.name+": "+d.females[activeYear]+" females";
if (labels[key]=="Both Sexes")
return ""+d.ICD10+" "+d.name+": "+(d.males[activeYear]+d.females[activeYear]);
}
)
.style("top", function () { return (d3.event.pageY - 40)+"px"})
.style("left", function () { return (d3.event.pageX - 130)+"px";})
}
// Returns a list of all nodes under the root.
function flatten(root) {
var nodes = [], i = 0;
function recurse(node) {
if (node.children) node.children.forEach(recurse);
if (!node.id) node.id = ++i;
nodes.push(node);
}
recurse(root);
return nodes;
}
</script>