Interactive Internet Application for Analysis & Visualisation of EPSRC Research Grant Data

9532 words (38 pages) Full Dissertation in Full Dissertations

06/06/19 Full Dissertations Reference this

Disclaimer: This work has been submitted by a student. This is not an example of the work produced by our Dissertation Writing Service. You can view samples of our professional work here.

Any opinions, findings, conclusions or recommendations expressed in this material are those of the authors and do not necessarily reflect the views of UK Essays.

Table of Contents

INTRODUCTION

PROJECT GOALS

PROJECT DATA

GRAPHICAL USER MANUAL

GRAPHICAL USER INTERFACE AND RELATIONSHIPS

STYLE REVIEW

APPLICATION DESIGN

GENERAL UPDATE PATTERN

FUNCTIONAL PROGRAMMING PATTERN

CONCLUSION

SOURCE CODE

7.1 Index.html

7.2 pie_renderer.js

7.3 processPie.js

7.4 app.js

7.5 tree_renderer.js

7.6 processTree.js

7.7 pack_renderer.js

7.8 aggClustering.js

7.8 wrapTextSVG.js

7.9 findAllTopics.js

7.10 helper.js

INTRODUCTION

 

Data visualization is the representation of data in a pictorial or graphical format. It allows difficult concepts and new patterns to be identified as they are presented visually. Furthermore, with interactive visualization details of data and how it is processed are rendered.

Due to the way, the human brain processes information, utilizing charts and graphs to visualize large amounts of complex data is easier than reading numerical data in spreadsheets or reports. The vast number of layouts and charts available also allow for the experimentation with different scenarios.

Some uses of data visualization include: i) identifying areas that need attention or require improvement, ii) Clarify factors which influence customer behaviour, iii) understand which products to place where and finally iv) to predict sales volumes.

PROJECT GOALS

 

The aim of the project is to produce an interactive internet application for analysis & visualisation of EPSRC research grant data. The application will use a real-time data feed from a specified URL. A minimum of three layouts are required with one of scatter plot, pie chart or D3 stack required and any relational or hierarchical D3 layout e.g. partition.

Figure 1                                                    Figure 2

Figures 1(Pie layout) and 2(Partition layout) show an example of chart types required

PROJECT DATA

 

The data for the project is in JSON format and can be accessed at http://www.researchperspectives.org/DV/tModelArrayFormat.json

The data format is structured and is read by the application in real time. The data contains textual, relational and qualitative data in the form of Grant, Organisation, Researcher and topic information.

Emphasis has also been placed on Topic-to-topic, Topic-to-Project and Topic description in the Pie layout of the application.

GRAPHICAL USER MANUAL

A total of three layouts were implemented. Pie, Pack and Tree charts. Example representations are found below:

Tree Chart       Pie Chart

GRAPHICAL USER INTERFACE AND RELATIONSHIPS

The Graphical user interface of the web application is represented below

../Desktop/Screen%20Shot%202017-08-10%20at%2014.25.14.png

When the topics in the word cluster are hovered on the image is highlighted as displayed in Figure 1.1.

../Desktop/Screen%20Shot%202017-08-10%20at%2014.52.37.png

Figure 1.1

When the topic is selected a tree of investigators(Researchers) in that topic is generated in the tree charts as Figure 1.2.

../Desktop/Screen%20Shot%202017-08-10%20at%2014.55.22.png

Figure 1.2

Hovering over grant data in the Pie Chart also generates grant information as in Figure 1.3.

../Desktop/Screen%20Shot%202017-08-10%20at%2014.58.28.png

Figure 1.3

When a section of the pie chart is clicked, the tree chart is generated with a list of grant values within a specified research area (root node). (Figure 1.4)

../Desktop/Screen%20Shot%202017-08-10%20at%2015.01.14.png

Figure 1.4

Furthermore, hovering on the pie chart highlights words from the cluster related to the topic. (Figure 1.5)

../Desktop/Screen%20Shot%202017-08-10%20at%2015.07.56.png

Figure 1.5

When the hovering down the tree chart, i.e. from country to city to university and selecting on the leaf node (university), the pie chart is populated with all grants from the selected university. (Figure 1.6)

../Desktop/Screen%20Shot%202017-08-10%20at%2015.12.01.png

Figure 1.6

STYLE REVIEW

The design is implemented using Cascading Style sheets. Some of the benefits of using CSS in web design include

  • Easier to maintain and update
  • Greater consistency in design
  • More formatting options
  • Lightweight code
  • Faster download times
  • Search engine optimization benefits
  • Ease of presenting different styles to different viewers
  • Greater accessibility

The basic idea behind CSS is to separate the structure of a document from the presentation of the document. HTML is meant for structure. All the CSS can be moved to a separate file making it easier to update your styles as well.

APPLICATION DESIGN

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

The diagram above represent the relationship between the chart layout employed in the application. The file structure is represented below

 

 

 

 

 

 

 

 

The main and helper files are broken into modules to enable reuse and for the easy detection of errors. Furthermore, where more D3 layouts are to be added, the structure allows for easy expansion and updating.

GENERAL UPDATE PATTERN

 

In D3, the General Update Pattern is the name given to what happens when a data join is followed by operations on the enter, update, and exit selections. When a chart’s data changes over time and each update can both create new elements, and destroy existing ones, the General Update pattern can help convey meaning to users.

A simple General Update Pattern in D3 consists of 5 steps:

  1. Bind Data: join new data with old elements.
  2. Update 1:  update old DOM elements as desired.
  3. Enter: create new DOM elements if they don’t exist.
  4. Update 2: update new and old elements.
  5. Exit: remove unwanted elements.

FUNCTIONAL PROGRAMMING PATTERN

JavaScript due to its flexible nature can be used in various programming methodologies such as functional, imperative and even object-oriented programming.

Functional Programming is a programming methodology that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressionsor declarations instead of statements.

Functional programming is particularly suitable for this web application and D3 in general because data in functional programs should be immutable. So, the grant data shouldn’t change and where data in an array is to be manipulated, a new array with updated with values is created rather than revising the original.

Secondly, functional programs should be stateless, i.e. each task is performed as though it is the first time, with no prior knowledge of previous program’s execution. This requirement also bodes well with d3 and the web application that is developed. These requirements enable the production of modular code i.e. broken down into smaller, simpler pieces, which enables flexibility, ease of debugging and scalability of the codebase. The code is also easier to maintain and layouts can be easily added and removed from the application.

The use of functions is essential in d3. Various functions are used to handle the interaction and transitions within the layouts in the web application. Some examples of common functions include:

  • .on() : The on function is used by the application to listen to certain event such as mouse clicks, keyboard use, mouse hover etc. This function is usually used to start the transition once the event has occurred.
  • .enter(): used to create the initial join of data to elements, creating one element for each in the array. A
  • .exit() is used to remove elements that are no longer needed
  •  update() function performs two different sets of actions on the elements of the selection, depending on the group the corresponding datum belongs to:

For data items that are already represented by an arc in the pie chart (the update selection), this arc’s width is updated according to the linked datum’s value.

For data items that don’t have an arc yet (the enter selection), the pie element is created and attached to the chart, and the width updated accordingly.

D3’s selection.transition method makes it easy to animate transitions when changing the DOM. For example, to change the text colour to blue instantaneously, you can select the body element and set the colour style.

This is the same function applied in the pie chart to display the highlighted arcs.

d3.select(“body”).style(“color”, “blue”);

To instead animate the change over time, derive a transition:

d3.select(“body”).transition().style(“color”, “blue”);

 

CONCLUSION

 

I am most proud of completing the project and meeting the requirements. Although no new D3 chart layouts are included other than the ones from

the labs and tutorials, this was not due to a lack of trying. The map was tested and the partition chart which was beautiful was proving to be difficult to work with. The transitions and interactivity between charts was also a huge success and the most important part of the implementation.

During the implementation of the project, a lot was learnt about D3.js. For example, the General Update Pattern and the Functional programming paradigm involved in d3.

Overall there is few to be changed about the project, however, one aspect for future project would be to try and experiment with other d3 layouts not mentioned in class that enhance the visualization for users and the functionality of the project.

SOURCE CODE

 

File Name % student contribution % lab contribution % d3 contribution
Index.html 100 0 0
Pie_renderer.js 80 20 0
processPie.js 100 0 0
Tree_renderer.js 35 35 30
processTree.js 100 0 0
Pack_renderer.js 50 50 0
processPack.js 50 50 0
Helper.js 0 100 0
findAllTopics.js 0 100 0
aggClustering.js 0 100 0
wrapTextSvg.js 0 100 0
Style.css 100 0 0
App.js 100 0 0

7.1 Index.html

<!DOCTYPE html>

<html lang=”en”>

<head>

<meta charset=”UTF-8″>

<title></title>

<!–build:css css/styles.min.css–>

<link rel=”stylesheet” href=”css/styles.css”>

<link rel=”stylesheet” href=”https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css” integrity=”sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u” crossorigin=”anonymous”>

<!–endbuild–>

</head>

<body style=”background-color: darkslategrey;”>

<nav class=”navbar navbar-default navbar-fixed-top”>

<div class=”container-fluid”>

<!– Brand and toggle get grouped for better mobile display –>

<div class=”navbar-header”>

<a class=”navbar-brand” href=”#” style=”font-size: 30px;”>

Dashboard – EPSRC Research Grant Data

</a>

</div>

</div>

</nav><br><br><br>

<div class=”container-fluid”>

<div class=”row”>

<div class=”col-md-6″>

<div id=”packChart” class=”div5″>

<h4 style=”color:#ffcc00″>30 Topics – Word Cluster</h4>

</div>

</div>

<div class=”col-md-6″>

<div class=”row”>

<div class=”col-md-4″>

<span class=”pull-right”>

<div id=”pieChart”>

</div>

</span>

</div>

<div class=”col-md-8″ style=”color:white”>

<h4 style=”color:#ffcc00″>UK Organisation – All Grants</h4>

<div id=”uniTitle”>Data Loading…Please Wait

</div>

<div>Grant Detail</div>

<div>Title: <span id=”grantTitle” style=”color:lightyellow”>-</span></div>

<div>Grant Value: <span id=”grantValue” style=”color:lightyellow”>-</span></div>

<div>Research Area: <span id=”rArea” style=”color:lightyellow”>-</span></div>

<div>Principal Investigator: <span id=”pInvestigator” style=”color:lightyellow”>-</span></div>

</div>

</div>

<div class=”row”>

<div class=”col-md-12″>

<span class=”pull-right”>

<h4 style=”color:#ffcc00″>UK – Countries / City / Organisations</h4>

</span>

</div>

</div>

<div class=”row”>

<div class=”col-md-12″>

<span class=”pull-right”>

<div id=”treeChart” class=”div3″>

</div>

</span>

</div>

</div>

</div>

</div>

</div>

<script type=”text/javascript” src=”https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js”></script>

<script src=”js/processTree.js”></script>

<script src=”js/processPack.js”></script>

<script src=”js/processPie.js”></script>

<script src=”js/pie_renderer.js”></script>

<script src=”js/tree_renderer.js”></script>

<script src=”js/pack_renderer.js”></script>

<script src=”js/helper.js”></script>

<script src=”js/findAllTopics.js”></script>

<script src=”js/aggClustering.js”></script>

<script src=”js/wrapTextSvg.js”></script>

<script type=”text/javascript” src=”https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.20/topojson.min.js”></script>

<script src=”js/app.js”></script>

<!– endbuild –>

</body>

</html>

 

7.2 pie_renderer.js

“use strict”

function piechart(targetDOMelement) {

var piechartObject = {};

//=================== PUBLIC FUNCTIONS =========================

piechartObject.render = function() {

render();

return piechartObject;

}

piechartObject.loadDataset = function(d) {

dataset = d;

return piechartObject;

}

piechartObject.layoutData = function() {

return arcLayoutData;

}

piechartObject.pieClickCallback = function(callback) {

pieClicked = callback;

return piechartObject;

}

piechartObject.pieHoverCallback = function(callback) {

pieOverPack = callback;

return piechartObject;

}

//=================== PRIVATE VARIABLES====================================

//Width and height of svg canvas

var w = 200;

var h = 210;

var dataset = [];

var outerRadius;

var innerRadius;

var formatNumber = d3.format(“,d”);

var arcShapeGenerator = d3.svg.arc();

var pieLayoutGenerator = d3.layout.pie();

var color = d3.scale.ordinal()

.range([“#8dd3c7”, “#ffffb3”, “#bebada”, “#fb8072”, “#80b1d3”, “#fdb462”, “#b3de69”, “#fccde5 “,

“#d9d9d9”, “#bc80bd”, “#ccebc5”, “#bfdbdb”, “#67c063”, “#240c12”, “#34ee30”, “#35bab2”, “#345496”,

“#e69941”, “#cc73e2 “, “#ad4b50”, “#984024”, “#94fd2e”, “#f98cac”, “#8abd16”, “#17e866”, “#abf63d”,

“#78658c”, “#3d63f7”, “# e87af9 “, “#1edcb2”,

“#3351fa”, “#aa19df”, “#919772”, “#3dbd0a”, “#6b29f9”, “#d0b214”, “#d10550”, “#7b2943”, “#845ec6”, “#f4a7cf”,

“#cd6e57”, “#67d4bf”

]);

var svg = d3

.select(targetDOMelement)

.append(“svg”);

var grp = svg

.append(“g”);

var arcLayoutData;

//=================== PRIVATE FUNCTIONS====================================

var pieOverText = function(d, i) {

console.log(d.data);

d3.select(this).classed(“active”, true);

document.getElementById(“grantTitle”).innerHTML = d.data.Title;

document.getElementById(“grantValue”).innerHTML =

formatNumber(d.data.Value);

document.getElementById(“rArea”).innerHTML = d.data.ResearchArea;

document.getElementById(“pInvestigator”).innerHTML =

d.data.PI.Surname;

}

var pieOverPack = function(d, i) {

console.log(“pie to Pack: “, d);

}

var pieClicked = function(d, i) {

console.log(“clicked by pie”);

}

function render() {

arcLayoutData = pieLayoutGenerator(dataset);

pieLayoutGenerator.value(function(d) {

return d.Value;

});

GUP_Arcs(arcLayoutData, grp);

}

function resize() {

outerRadius = w / 2;

innerRadius = w / 5;

arcShapeGenerator

.innerRadius(innerRadius)

.outerRadius(outerRadius);

svg

.attr(“width”, w)

.attr(“height”, h);

grp

.attr(“transform”, “translate(” + outerRadius + “,” +

outerRadius + “)”);

render();

}

function GUP_Arcs(layoutData, _grp) {

//BIND DATA

var arcs = _grp.selectAll(“path”)

.data(layoutData);

//GUP: UPDATE 1

//GUP: ENTER: Add paths for new arcs

arcs

.enter()

.append(“path”)

.style(“opacity”, 0.0);

//GUP: UPDATE 2 [= UPDATE 1 + ENTER]

//Define shape and colour

arcs

.transition()

.duration(3000)

.attr(“d”, arcShapeGenerator)

.style(“opacity”, 1.0)

.attr(“fill”,

function(d, i) {

return color(i)

});

arcs

.on(‘mouseover’, function(d, i) {

pieOverText(d, i);

pieOverPack(d, i);

d3.select(this).classed(“active”, true)

})

.on(“mouseout”, function highlight(d) {

d3.select(this).classed(“active”, false)

})

.on(“click”, pieClicked);

//GUP: EXIT: Remove old arcs

arcs.exit()

.remove()

.transition()

.duration(2000)

.style(“opacity”, 0.0);

}

resize();

return piechartObject;

7.3 processPie.js

“use strict”

function processPie() {

var processpie = {};

processpie.initializePie = function() {

OrgGrants = GOP.organisations.filter(function(organ) {

return organ.OrgID === “40”;

});

pieChart.loadDataset(OrgGrants[0].grants).render();

}

processpie.pieClick = function(d, i) {

processpie.updateTree(d.data.ResearchArea);

treeChart.loadAndRenderNestDataset(treeData, d.data.ResearchArea);

}

processpie.pieHoverPack = function(d, i) {

console.log(“hover from pie for Pack: “, d.data.ID);

processpie.getTopicNumber(d.data.ID);

console.log(“topicArr from pie before: “, topicArr);

packChart.topicHoverNumber(topicArr);

topicArr = [];

}

processpie.updateTree = function(researchArea) {

var researchTreeData = Grants.filter(function(grant) {

return grant.ResearchArea === researchArea;

});

treeData = d3.nest().entries(researchTreeData);

}

processpie.getTopicNumber = function(grantID) {

TopicDist.forEach(function(topic) {

for (var i = 0; i < topic.length; i++) {

if (topic[i].docID === grantID) {

topicArr.push(i);

}

}

return topicArr;

});

}

return processpie;

}

7.4 app.js

“use strict”

var GOP = [],

UK = [],

TOPIC = [],

Grants = [],

Organisations = [],

Person = [],

Topics = [],

TopicDist = [],

TopicSim = [],

topicArr = [],

linkageTable = [];

var OrgGrants, BarOrgGrants, mapData, OrgVal, packGrants, nLeaves, pieData,

treeData, hwGrants;

var hierarchy = {},

augmentedSimMatrix = {};

var hlp = grantAndTopicDataHelperFunctions();

var pieHelper = processPie();

var treeHelper = processTree();

var packHelper = processPack();

var url1 = “http://www.researchperspectives.org/DV/?database=release”;

var url2 = “https://gist.githubusercontent.com/ChaituVR/d33947583a8292d252ab810421c8b22f/raw/6c3292c04b39c2a63791ade439d903ffaea130e8/data.json”;

var url3 = “https://gist.githubusercontent.com/ChaituVR/8c46491e18f5820d6697f43b544702a8/raw/04316fcb893b0b2918aefcda17566908ba4ec456/tModelArrayFormat.json”;

d3.json(url1, function(error, GOP) {

if (error) return console.log(“Failed: to load JSON from :” + url1);

d3.json(url2, function(error, UK) {

if (error) return console.log(“Failed: to load JSON from :” + url2);

d3.json(url3, function(error, TOPIC) {

if (error) return console.log(“Failed: to load JSON from :” +

url3);

processData(GOP, UK, TOPIC);

});

});

});

console.log(“Data loading from multiple source…Please Wait”);

function processData(data1, data2, data3) {

GOP = data1;

UK = data2;

TOPIC = data3;

Organisations = GOP.organisations;

Grants = GOP.grants;

Person = GOP.persons;

TopicDist = TOPIC.topicsDocsDistrib;

Topics = TOPIC.topics;

TopicSim = TOPIC.topicsSimilarities;

GOP.organisations.forEach(function(organisation) {

organisation.grants = Grants.filter(function(grant) {

return grant.OrgID === organisation.OrgID;

});

organisation.nGrants = organisation.grants.length;

});

hwGrants = Grants.filter(function(grant) {

return grant.OrgID === “40”;

});

//============= Create and render linkage table from topic similarities

var clusterInstance = agglomerativeClustering();

var linkageTable =

clusterInstance.makeLinkageTable(TOPIC.topicsSimilarities);

var groups = packHelper.clusterIntoGroups(6, linkageTable);

hierarchy = findAllTopicsOfEachGroupAndCreateHierarchy(groups,

linkageTable)

//===================Render====================================

packChart.loadAndRenderDataset(hierarchy);

hlp.insertOrganisationsIntoGrantsTable(GOP);

hlp.insertPIpersonsIntoGrantsTable(GOP);

render();

}

//Set up renderers

var pieChart = piechart(“#pieChart”);

var treeChart = treechart(“#treeChart”);

var packChart = packHelper.setUpPackRenderer(“#packChart”);

function render() {

pieHelper.initializePie();

treeHelper.initializeTree();

}

treeChart.treeClickCallback(treeHelper.treeClick);

pieChart.pieClickCallback(pieHelper.pieClick);

pieChart.pieHoverCallback(pieHelper.pieHoverPack);

7.5 tree_renderer.js

“use strict”

function treechart(targetDOMelement) {

var treechartObject = {};

//=================== PUBLIC FUNCTIONS =========================

treechartObject.loadAndRenderDataset = function(d) {

dataset = d;

render();

return treechartObject;

}

treechartObject.loadAndRenderNestDataset = function(data, rootName = “”) {

myTreeGenerator.children(function(d) {

return d.values;

})

dataset = {

“key”: rootName,

“values”: data

};

render();

return treechartObject;

}

treechartObject.treeClickCallback = function(callback) {

treeClick = callback;

return treechartObject;

}

//=================== PRIVATE VARIABLES =========================

var dataset;

var margin = {

top: 20,

right: 120,

bottom: 20,

left: 10

},

width = 660 – margin.right – margin.left,

height = 662.5 – margin.top – margin.bottom;

var formatNumber = d3.format(“,d”);

var i = 0,

duration = 750,

root;

var tree = d3.layout.tree()

.size([height, width]);

var diagonal = d3.svg.diagonal()

.projection(function(d) {

return [d.y, d.x];

});

var svg = d3.select(targetDOMelement).append(“svg”)

.attr(“width”, width + margin.right + margin.left)

.attr(“height”, height + margin.top + margin.bottom)

.append(“g”)

.attr(“transform”, “translate(” + margin.left + “,” + margin.top +

“)”);

var myTreeGenerator = d3.layout.tree().size([width, height]);

//========== PRIVATE FUNCTIONS ========================

var treeClick = function(d, i) {

console.log(“Tree: “, d);

}

function render() {

console.log(“Tree Current Dataset: “, dataset);

var myNodeLayout = myTreeGenerator.nodes(dataset);

myNodeLayout.forEach(function(d) {

d.y = d.depth * 200;

});

var myLinks = myTreeGenerator.links(myNodeLayout);

root = dataset;

root.x0 = height / 2;

root.y0 = 0;

function collapse(d) {

if (d.children) {

d._children = d.children;

d._children.forEach(collapse);

d.children = null;

}

}

root.children.forEach(collapse);

update(root);

}

function update(source) {

//BIND DATA

var nodes = tree.nodes(root).reverse(),

links = tree.links(nodes);

// Normalize for fixed-depth.

nodes.forEach(function(d) {

d.y = d.depth * 180;

});

//GUP: UPDATE 1

var node = svg.selectAll(“g.node”)

.data(nodes, function(d) {

return d.id || (d.id = ++i);

});

//GUP: ENTER

var nodeEnter = node.enter().append(“g”)

.attr(“class”, “node”)

.attr(“transform”, function(d) {

return “translate(” + source.y0 + “,” +

source.x0 + “)”;

})

.on(“mouseover”, click)

.on(“click”, treeClick);

nodeEnter.append(“circle”)

.attr(“r”, 1e-6)

.style(“fill”, function(d) {

return d._children ? “#00ff00” : “#000”;

});

nodeEnter.filter(function(d) {

return d.key

}).append(“text”)

.attr(“x”, 13)

.attr(“text-anchor”, “start”)

.text(function(d) {

return d.key;

});

nodeEnter.append(“text”)

.attr(“x”, function(d) {

return d.children || d._children ? -7 : 7;

})

.attr(“dy”, “.35em”)

.attr(“text-anchor”, function(d) {

return d.children || d._children ?

“end” : “start”;

})

.text(function(d) {

return d.Name || “V: ” + formatNumber(d.Value) + “(O:” + d.OrgID + “)” + ” (G: ” + d.grant + “)”;

})

.style(“fill-opacity”, 1e-6);

var nodeUpdate = node.transition()

.duration(duration)

.attr(“transform”, function(d) {

return “translate(” + d.y + “,” + d.x +

“)”;

});

nodeUpdate.select(“circle”)

.attr(“r”, 4.5)

.style(“fill”, function(d) {

return d._children ? “#FFFFFF” :

“#3B1E0E”;

});

nodeUpdate.select(“text”)

.style(“fill-opacity”, 1);

var nodeExit = node.exit().transition()

.duration(duration)

.attr(“transform”, function(d) {

return “translate(” + source.y + “,” +

source.x + “)”;

})

.remove();

//GUP: EXIT

nodeExit.select(“circle”)

.attr(“r”, 1e-6);

nodeExit.select(“text”)

.style(“fill-opacity”, 1e-6);

var link = svg.selectAll(“path.link”)

.data(links, function(d) {

return d.target.id;

});

link.enter().insert(“path”, “g”)

.attr(“class”, “link”)

.attr(“d”, function(d) {

var o = {

x: source.x0,

y: source.y0

};

return diagonal({

source: o,

target: o

});

});

link.transition()

.duration(duration)

.attr(“d”, diagonal);

link.exit().transition()

.duration(duration)

.attr(“d”, function(d) {

var o = {

x: source.x,

y: source.y

};

return diagonal({

source: o,

target: o

});

})

.remove();

nodes.forEach(function(d) {

d.x0 = d.x;

d.y0 = d.y;

});

}

function click(d) {

if (d.children) {

d._children = d.children;

d.children = null;

} else {

d.children = d._children;

d._children = null;

}

update(d);

}

return treechartObject;

}

7.6 processTree.js

“use strict”

function processTree() {

var processtree = {};

processtree.initializeTree = function() {

treeData = d3.nest()

.key(function(d) {

return d.Country;

})

.key(function(d) {

return d.City;

})

.entries(Organisations);

treeChart.loadAndRenderNestDataset(treeData, “Countries”);

}

processtree.treeClick = function(d, i) {

processtree.pieOrganisation(d.OrgID);

console.log(d.OrgID);

document.getElementById(“uniTitle”).innerHTML = OrgGrants[0].Name;

console.log(OrgGrants);

pieChart.loadDataset(OrgGrants[0].grants).render();

processtree.getTopicNumber(d.ID);

packChart.topicHoverNumber(topicArr);

topicArr = [];

}

processtree.getTopicNumber = function(grantID) {

TopicDist.forEach(function(topic) {

for (var i = 0; i < topic.length; i++) {

if (topic[i].docID === grantID) {

topicArr.push(i);

}

}

return topicArr;

});

}

processtree.pieOrganisation = function(org) {

OrgGrants = GOP.organisations.filter(function(organ) {

return organ.OrgID === org;

});

}

return processtree;

}

7.7 pack_renderer.js

“use strict”

function packRenderer(targetDOMelement) {

var packRendererObject = {};

//=================== PUBLIC METHODS =========================

packRendererObject.loadAndRenderDataset = function(data) {

dataset = data;

render();

return packRendererObject;

}

packRendererObject.loadAndRenderNestDataset = function(data, rootName =

“”) {

//Method to load d3.nest format hierarchy directly

packLayoutGenerator.children(function(d) { return d.values; })

dataset = { “key”: rootName, “values”: data };

render();

return packRendererObject;

}

packRendererObject.overideLeafLabel = function(labelFunction) {

leafLabel = labelFunction;

return packRendererObject;

}

packRendererObject.overideParentLabel = function(labelFunction) {

parentLabel = labelFunction;

return packRendererObject;

}

packRendererObject.overideTooltip = function(tooltipFunction) {

tooltip = tooltipFunction;

return packRendererObject;

}

packRendererObject.overideClickBehaviour = function(tooltipFunction) {

clickBehaviour = tooltipFunction;

return packRendererObject;

}

packRendererObject.topicHoverNumber = function(topicNumber) {

topicHover = topicNumber;

if (rendered) {

render();

}

return packRendererObject;

}

//=================== PRIVATE VARIABLES====================================

var dataset = [];

var svgWidth = 662.5;

var svgHeight = 662.5;

var margin = { top: 50, right: 10, bottom: 20, left: 10 },

width = svgWidth – margin.right – margin.left,

height = svgHeight – margin.top – margin.bottom;

var topicHover = [];

//==================== ‘Constructor’ code ====================

var diameter = 662.5,

format = d3.format(“,d”);

var packLayoutGenerator = d3.layout.pack()

.size([diameter – 4, diameter – 4]).value(function(d) { return d.n; })

var svg = d3.select(targetDOMelement)

.append(“svg”)

.attr(“width”, diameter)

.attr(“height”, diameter)

.append(“g”)

.attr(“transform”, “translate(2,2)”);

var rendered = false;

//=================== PRIVATE FUNCTIONS====================================

var leafLabel = function(d, i) {

//return 3 element array – one element for each label

return [“leaf” + i, “”, “”]

};

var parentLabel = function(d, i) { return “parent ” + i };

var tooltip = function(d, i) { return “tooltip text for ” + i };

var clickBehaviour = function(d, i) {

console.log(“clickBehaviour, d = “,

d)

};

function render() {

console.log(“topicHover in pack file: “, topicHover);

//Mainly Bostock Code below

//UPDATE 1

//BIND DATA and GUP: ENTER

//Create and transform group to hold circle and text

var node = svg.datum(dataset)

.selectAll(“.node”)

.data(packLayoutGenerator.nodes)

.enter()

.append(“g”)

.attr(“transform”, function(d) {

return “translate(” + d.x + “,” +

d.y + “)”;

})

.on(“click”, clickBehaviour);

node

.append(“circle”)

.attr(“r”, function(d) { return d.r; });

node

.append(“title”)

.text(tooltip);

//Filter out and process top level 0 node

node

.filter(function(d) { return d.depth === 0; })

.classed(“node level_0”, true).append(“text”)

.attr(“dy”, function(d) { return -(d.r – 10); })

.style(“text-anchor”, “middle”)

.text(parentLabel);

//Filter out and process level 1 nodes

node

.filter(function(d) { return d.depth === 1; })

.classed(“node level_1”, true)

.append(“text”)

.attr(“dy”, function(d) { return -(d.r + 5); })

.style(“text-anchor”, “middle”)

.text(parentLabel);

//Filter out and process leaf nodes

var leaves = node

.filter(function(d) { return !d.children; })

.classed(“node leaf”, true)

.on(“mouseover”, function(d) {

d3.select(this).classed(“hover”, true);

})

.on(“mouseout”, function(d) {

d3.select(this).classed(“hover”, false);

});

svg.selectAll(‘.leaf’)

.selectAll(“circle”)

.classed(“hover”, function(d) {

//console.log(“d: “,d, “topicHover: “,topicHover);

for (var i = 0; i < topicHover.length; i++) {

if (d.value === topicHover[i]) {

return true;

}

return false;

}

});

svg.selectAll(‘.leaf’)

.selectAll(“circle”)

.on(“mouseover”, function(d) {

d3.select(this).classed(“hover”, true);

})

.on(“mouseout”, function(d) {

d3.select(this).classed(“hover”, false);

});

leaves

.append(‘text’)

.attr(‘transform’, ‘translate(0, -10)’)

.text(leafLabel)

.call(wrapTextSvg, 20);

//GUP: EXIT

rendered = true;

}

return packRendererObject;

}

7.7 processPack.js

“use strict”

function processPack() {

var processpack = {};

var div = d3.select(“body”).append(“div”);

processpack.setUpPackRenderer = function(div) {

return packRenderer(div)

.overideClickBehaviour(function(d) {

if (!d.children)

d3.select(“#userUpdateDIV”).html(processpack.topicInfo(d.n))

})

.overideLeafLabel(function(d) {

if (d.r > 25) {

return d.words[1] +

” ” + d.words[0] +

” ” + d.words[2];

} else {

return d.words[0];

}

})

.overideParentLabel(function(d) {

return “”

})

.overideTooltip(function(d) {

if (d.words) return d.name + ” ” + d.words;

else return d.name;

})

.topicHoverNumber([]);

}

processpack.topicInfo = function(topicNumber) {

var grants = hlp.getGrantObjsForATopic(topicNumber, GOP, TOPIC);

var all = new Array();

var arrayLength = grants.length;

for (var i = 0; i < arrayLength; i++) {

all[i] = grants[i].OrgID;

}

var randOrgID = all[Math.floor(Math.random() * all.length)];

processpack.pieOrganisation(randOrgID);

document.getElementById(“uniTitle”).innerHTML = OrgGrants[0].Name;

pieChart.loadDataset(OrgGrants[0].grants).render();

packGrants = Grants.filter(function(grant) {

for (var i = 0; i < all.length; all++) {

return grant.OrgID === all[i];

}

})

treeData = d3.nest()

.key(function(d) {

return d.PI.Surname;

})

.entries(packGrants);

treeChart.loadAndRenderNestDataset(treeData, “Topic: ” + topicNumber);

div.attr(“class”, “tooltip”);

return “<h3>Topic ” +

topicNumber +

“</h3> <p> ” +

hlp.wordcloud(topicNumber, TOPIC) +

“</p>” +

“<p>More detail of grants on console</p>”;

}

processpack.clusterIntoGroups = function(minNumberGroups, linkageTable) {

var topNode = linkageTable.length – 1;

nLeaves = linkageTable.length + 1;

function expandNodes(nodesToExpand, groups, nLeaves, linkageTable) {

var nodesRequiringExpansion = [];

nodesToExpand.forEach(function(currentNode) {

if (currentNode >= 0) {

var index = groups.indexOf(currentNode);

groups.splice(index, 1);

var g1 = linkageTable[currentNode].cluster1 – nLeaves;

var g2 = linkageTable[currentNode].cluster2 – nLeaves;

groups.push(g1);

groups.push(g2);

if (g1 >= 0) nodesRequiringExpansion.push(g1);

if (g2 >= 0) nodesRequiringExpansion.push(g2);

}

})

return {

“groups”: groups,

“nodesRequiringExpansion”: nodesRequiringExpansion

}

}

var expansion = {

“groups”: [],

“nodesRequiringExpansion”: [topNode]

}

while (expansion.groups.length < minNumberGroups) {

expansion = expandNodes(expansion.nodesRequiringExpansion,

expansion.groups, nLeaves, linkageTable)

};

return expansion.groups;

}

processpack.pieOrganisation = function(org) {

OrgGrants = GOP.organisations.filter(function(organ) {

return organ.OrgID === org;

});

}

return processpack;

}

7.8                aggClustering.js

function agglomerativeClustering() {

var clusterObject = {};

//’Instance’ Obj to be returned to caller

//================== Public methods ==========================

clusterObject.makeLinkageTable = function(sMatrixArg) {

var sMatrix = cloneSimData(sMatrixArg);

var nNodes = sMatrix.length;

augmentedSimMatrix = {

“sMatrix”: sMatrix,

“lastRow”: nNodes – 1,

“nNodes”: nNodes

};

while (augmentedSimMatrix.nNodes > 1) {

var maxS = maxSim(augmentedSimMatrix.sMatrix);

linkageTable.push({

“cluster1”: maxS.col,

“cluster2”: maxS.row,

“similarity”: maxS.maximum

});

addNewNodeAsNewColRow(augmentedSimMatrix, maxS);

deleteRowsAndCols(augmentedSimMatrix, maxS);

}

return linkageTable;

}

//================== Private variables ==========================

var linkageTable = [];

var augmentedSimMatrix = {};

//================== Private methods ==========================

function cloneSimData(similarities) {

// Function to clone 2D similarity matrix

// as clustering will destroy it.

var clone = [];

similarities.forEach(processRow);

function processRow(row, i) {

clone[i] = []

row.forEach(function(sim, j) {

clone[i][j] = sim;

if (i === j) clone[i][j] = 0; //zero if similarity of node to

// itself

})

}

var lastRow = similarities.length – 1;

return clone;

}

function addNewNodeAsNewColRow(augmentedSimMatrix, _max) {

//Function to add a new cluster to the matrix by

//adding a new row and column for the node

var dist = Math.min;

var mRow = _max.row,

mCol = _max.col,

max = _max.maximum,

sMatrix = augmentedSimMatrix.sMatrix,

newRowCol = Number(augmentedSimMatrix.lastRow) + 1;

//Add new column for new node

for (var rowIndex in sMatrix) {

var r = Number(rowIndex);

var sim1 = Number(sMatrix[rowIndex][mCol]);

var sim2 = Number(sMatrix[rowIndex][mRow]);

if (sim1 && sim2) {

sMatrix[rowIndex][newRowCol] = dist(sim1, sim2);

}

}

//Add new row for new node

sMatrix[newRowCol] = [] //Add the new row onto end of sMatrix

//Populate new row sim values

for (var colIndex = 0; colIndex < newRowCol; colIndex++) {

var sim1 = Number(sMatrix[mCol][colIndex]);

var sim2 = Number(sMatrix[mRow][colIndex]);

if (sim1 && sim2) {

sMatrix[newRowCol][colIndex] = dist(sim1, sim2);

}

}

//Update lastRow to include the new row

augmentedSimMatrix.lastRow = newRowCol;

}

function deleteRowsAndCols(augmentedSimMatrix, _max) {

//Function to delete old rows and columns that have been

//used to form the new node

var mRow = _max.row,

mCol = _max.col,

sMatrix = augmentedSimMatrix.sMatrix,

newRowCol = Number(augmentedSimMatrix.lastRow) + 1;

//Delete rows

delete sMatrix[mRow];

delete sMatrix[mCol];

//Delete colums

for (var rowIndex in sMatrix) {

delete sMatrix[rowIndex][mRow];

delete sMatrix[rowIndex][mCol];

}

augmentedSimMatrix.nNodes = Number(augmentedSimMatrix.nNodes) – 1;

}

function maxSim(sMatrix) {

//Function to find maximum value in similarity matrix

var mRow, mCol, max = 0;

for (var rowIndex in sMatrix) {

var rowData = sMatrix[rowIndex];

for (var colIndex in rowData) {

var sim = sMatrix[rowIndex][colIndex]

if (sim > max) {

max = sim;

mRow = rowIndex;

mCol = colIndex;

}

}

}

return { “row”: mRow, “col”: mCol, “maximum”: max };

}

return clusterObject;

}

7.8                wrapTextSVG.js

var wrapTextSvg = function(text, width) {

text.each(function() {

var text = d3.select(this),

words = text.text().split(/\s+/).reverse(),

word,

line = [],

lineNumber = 0,

lineHeight = 1, // ems

y = text.attr(‘y’) || 0,

dy = parseFloat(text.attr(‘dy’)) || 0,

tspan = text.text(null)

.append(‘tspan’)

.attr(‘x’, 0)

.attr(‘y’, y)

.attr(‘dy’, dy + ’em’);

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);

}

}

var ln = -lineNumber / 2 – 0.5;

text.selectAll(‘tspan’).each(function() {

d3.select(this)

.attr(‘dy’, ++ln * lineHeight + dy + ’em’)

});

});

}

//Useage:

function exampleUse() {

let text = d3

.select(‘body’)

.append(‘svg’)

.append(‘text’)

.attr(‘transform’, ‘translate(100, 50’)

.text(‘My very long text here’)

.call(wrapTextSvg, 200);

}

7.9                findAllTopics.js

“use strict”

function findAllTopicsOfEachGroupAndCreateHierarchy(groups, linkageTable) {

//Use the linkagetable to find all the leaf nodes (i.e. the topics) of

// each

//group in groups and return result as flattened hierarchy

var hierarchy = { “name”: “topic clusters”, children: [] };

groups.forEach(findTopics);

function findTopics(node) {

//console.log(“Parent: “+node)

var hChild = {

“name”: “cluster ” + node,

“number”: Number(node),

children: []

}

var leaves = [];

if (node >= 0) {

getLeafNodes([node + nLeaves], linkageTable, nLeaves, leaves);

//console.log(“–topics=”+leaves)

} else {

//Have singleton topic, so just list the single topic itself

leaves = [Number(node + nLeaves)]

hChild.name = “Singleton: ” + hChild.name;

}

hChild.children = leaves.map(function(leaf) {

var topicNumber = Number(leaf);

var tWords = TOPIC.topics[topicNumber]

var weight = 0;

tWords.forEach(function(word) { weight += word.weight })

return {

“name”: “Topic ” + leaf,

“n”: topicNumber,

“size”: weight,

“words”: tWords.map(function(t) { return t.label })

}

})

hierarchy.children.push(hChild);

};

return hierarchy

}

//=========================== HELPER FUNCTIONS =============================

function getLeafNodes(groups, linkageTable, nLeaves, leaves) {

//Finds all leaf nodes given an array of parent nodes in groups

//The leaf nodes are returns as ‘leaves’ (an array of leaf nodes)

//nLeaves is the totoal number of leaves in the linkage table

groups.forEach(function(node) {

var lIndex = Number(node) – Number(nLeaves);

if (lIndex >= 0) {

//console.log(“lIndex = “, lIndex)

var child1 = linkageTable[lIndex].cluster1;

if (child1 >= nLeaves) getLeafNodes([child1], linkageTable,

nLeaves, leaves);

else leaves.push(child1);

var child2 = linkageTable[lIndex].cluster2;

if (child2 >= nLeaves) getLeafNodes([child2], linkageTable,

nLeaves, leaves);

else leaves.push(child2);

}

})

}

7.10              helper.js

“use strict”

function grantAndTopicDataHelperFunctions() {

var helpers = {};

helpers.insertOrganisationsIntoGrantsTable = function(grantsData) {

//Function to combine organisations into grants table

//It inserts ‘organisation’ ref to a grant’s organisation obj

//in each grant obj

function getOrgByID(ID) {

var organisation = grantsData.organisations.find(function(org) {

return org.OrgID === ID

})

return organisation

}

grantsData.grants.forEach(function(grant) {

grant.organisation = getOrgByID(grant.OrgID)

})

}

helpers.insertPIpersonsIntoGrantsTable = function(grantsData) {

//Function to combine person records into grants table

//It inserts ref to persons obj coresponding to The PIID number inthe grant

//(this is the ID of the person who is the Principle Investigator onthe grant)

//in each grant obj as a “PI” attribute

function getPersonByID(ID) {

var person = grantsData.persons.find(function(person) {

return person.ID === ID

})

return person;

}

grantsData.grants.forEach(function(grant) {

grant.PI = getPersonByID(grant.PIID)

})

}

helpers.insertGrantListsIntoPersonsTable = function(grantData) {

//Link persons back to grants by providing a

//’grants’ field in each persons record that contains

//a array of grant objects (strictly an array of references to grant objects)

persons = grantData.persons;

grants = grantData.grants;

persons.forEach(function(person) {

person.grants = grants.filter(function(grant) {

function personIsInvestigator(investigator) {

return investigator.ID === person.ID

}

return (grant.Investigators.some(personIsInvestigator));

})

person.nGrants = person.grants.length;

})

console.log(persons)

}

helpers.getGrantObjsByID = function(arrayOfIDs, grantsData) {

//Given an array of grant object ‘ID’

//returns array of those grant objects

var grants = grantsData.grants;

function grantInIdList(grant) {

//console.log(grant)

return arrayOfIDs.some(function(id) {

return grant.ID === id

})

}

return grants.filter(grantInIdList);

}

helpers.getGrantObjsForATopic = function(topicNumber, grantsData,

topicData) {

//Given a topic’s number, return an array of the topic’s grant objects

var grantIds = helpers.getGrantsIdsForATopic(topicNumber, topicData);

return helpers.getGrantObjsByID(grantIds, grantsData);

}

helpers.getGrantsIdsForATopic = function(topicNumber, topicData) {

var grantIdList = topicData.topicsDocsDistrib[topicNumber]

.map(function(topicDist) {

return topicDist.docID

})

return grantIdList;

}

helpers.wordcloud = function(topicNumber, topicData) {

return topicData.topics[topicNumber].map(function(word) {

return

word.label

})

}

return helpers;

}

7.11 processPack.js

“use strict”

function processPack() {

var processpack = {};

var div = d3.select(“body”).append(“div”);

processpack.setUpPackRenderer = function(div) {

return packRenderer(div)

.overideClickBehaviour(function(d) {

if (!d.children)

d3.select(“#userUpdateDIV”).html(processpack.topicInfo(d.n))

})

.overideLeafLabel(function(d) {

if (d.r > 25) {

return d.words[1] +

” ” + d.words[0] +

” ” + d.words[2];

} else {

return d.words[0];

}

})

.overideParentLabel(function(d) {

return “”

})

.overideTooltip(function(d) {

if (d.words) return d.name + ” ” + d.words;

else return d.name;

})

.topicHoverNumber([]);

}

processpack.topicInfo = function(topicNumber) {

var grants = hlp.getGrantObjsForATopic(topicNumber, GOP, TOPIC);

var all = new Array();

var arrayLength = grants.length;

for (var i = 0; i < arrayLength; i++) {

all[i] = grants[i].OrgID;

}

var randOrgID = all[Math.floor(Math.random() * all.length)];

processpack.pieOrganisation(randOrgID);

document.getElementById(“uniTitle”).innerHTML = OrgGrants[0].Name;

pieChart.loadDataset(OrgGrants[0].grants).render();

packGrants = Grants.filter(function(grant) {

for (var i = 0; i < all.length; all++) {

return grant.OrgID === all[i];

}

})

treeData = d3.nest()

.key(function(d) {

return d.PI.Surname;

})

.entries(packGrants);

treeChart.loadAndRenderNestDataset(treeData, “Topic: ” + topicNumber);

div.attr(“class”, “tooltip”);

return “<h3>Topic ” +

topicNumber +

“</h3> <p> ” +

hlp.wordcloud(topicNumber, TOPIC) +

“</p>” +

“<p>More detail of grants on console</p>”;

}

processpack.clusterIntoGroups = function(minNumberGroups, linkageTable) {

var topNode = linkageTable.length – 1;

nLeaves = linkageTable.length + 1;

function expandNodes(nodesToExpand, groups, nLeaves, linkageTable) {

var nodesRequiringExpansion = [];

nodesToExpand.forEach(function(currentNode) {

if (currentNode >= 0) {

var index = groups.indexOf(currentNode);

groups.splice(index, 1);

var g1 = linkageTable[currentNode].cluster1 – nLeaves;

var g2 = linkageTable[currentNode].cluster2 – nLeaves;

groups.push(g1);

groups.push(g2);

if (g1 >= 0) nodesRequiringExpansion.push(g1);

if (g2 >= 0) nodesRequiringExpansion.push(g2);

}

})

return {

“groups”: groups,

“nodesRequiringExpansion”: nodesRequiringExpansion

}

}

var expansion = {

“groups”: [],

“nodesRequiringExpansion”: [topNode]

}

while (expansion.groups.length < minNumberGroups) {

expansion = expandNodes(expansion.nodesRequiringExpansion,

expansion.groups, nLeaves, linkageTable)

};

return expansion.groups;

}

processpack.pieOrganisation = function(org) {

OrgGrants = GOP.organisations.filter(function(organ) {

return organ.OrgID === org;

});

}

return processpack;

}

7.12 styles.css

div.treetitle {

width: 100%;

float: left;

height: 20px;

text-align: center;

color: #fff;

margin-bottom: 2px;

background-color: #3B1E0E;

}

div.div3 {

width: 100%;

float: left;

height: 662.5px;

/* background-color: #3B1E0E; */

margin-bottom: 4px;

}

div.div5 {

width: 100%;

float: left;

height: 662.5px;

/* background-color: #009473; */

margin-bottom: 4px;

}

.axis path,

.axis line {

fill: none;

stroke: #fff;

shape-rendering: crispEdges;

}

.axis text {

fill: rgb(255, 255, 255);

}

.subunit.SCT {

fill: rgba(0, 255, 255, 0.9);

box-shadow: 10px 10px;

}

.subunit.WLS {

fill: rgba(0, 255, 255, 0.7);

box-shadow: 10px 10px;

}

.subunit.NIR {

fill: rgba(0, 255, 255, 0.5);

box-shadow: 10px 10px;

}

.subunit.ENG {

fill: rgba(0, 255, 255, 1);

}

.subunit.IRL {

fill: rgba(0, 255, 255, 0.3);

}

.subunit-label.SCT {

fill: #000

}

.subunit-label.WLS {

fill: #000

}

.subunit-label.NIR {

fill: #fff;

}

.subunit-label.ENG {

fill: #000

}

.subunit-label.IRL {

fill: #fff;

}

.subunit.active {

fill: rgba(255, 255, 0, 1);

}

.subunit.active {

fill: rgba(255, 255, 0, 1);

}

div.tooltip {

position: absolute;

width: 150px;

padding: 10px;

text-align: center;

border: 1px solid #ddd;

box-shadow: 0 8px 16px rgba(0, 0, 0, 0.5);

background: #fff;

color: #333;

z-index: 50;

font: 13px sans-serif;

}

.hidden {

display: none;

}

div.div3 circle {

fill: #ffffff;

fill-opacity: 1;

stroke: #ffffff;

stroke-width: 1px;

}

div.div5 circle {

fill: rgb(0, 0, 0);

fill-opacity: .25;

stroke: rgb(255, 255, 255);

stroke-width: 1px;

}

div.div5 circle.hover {

fill: #fff !important;

fill-opacity: 0.5;

stroke: #fff;

stroke-width: 1px;

}

/* Pack Chart */

.leaf circle {

fill: #9C8AA5;

fill-opacity: 1;

}

.leaf tspan {

fill: #fff;

fill-opacity: 1;

}

.level_1 {

font: 40px sans-serif;

}

.leaf text {

font: 12px sans-serif;

text-anchor: middle;

}

/* Tree Chart */

.node {

cursor: pointer;

}

.node circle {

fill: #fff;

stroke: steelblue;

stroke-width: 1.5px;

}

.node text {

font: 10px sans-serif;

fill: #fff;

}

.link {

fill: none;

stroke: #ccc;

stroke-width: 1.5px;

}

#pieChart path.active {

fill: #004869 !important;

}

#barChart .active {

fill: #004869 !important;

}

/*Navbar*/

body {

margin: 0;

}

Cite This Work

To export a reference to this article please select a referencing stye below:

Reference Copied to Clipboard.
Reference Copied to Clipboard.
Reference Copied to Clipboard.
Reference Copied to Clipboard.
Reference Copied to Clipboard.
Reference Copied to Clipboard.
Reference Copied to Clipboard.

Related Services

View all

DMCA / Removal Request

If you are the original writer of this essay and no longer wish to have the essay published on the UK Essays website then please:

McAfee SECURE sites help keep you safe from identity theft, credit card fraud, spyware, spam, viruses and online scams Prices from
£29

Undergraduate 2:2 • 250 words • 7 day delivery

Order now

Delivered on-time or your money back

Rated 4.0 out of 5 by
Reviews.co.uk Logo (23 Reviews)

Get help with your dissertation