#ITS Network Visualization
####Force Layout
This visualization is based on the force layout by Mike Bostock, described as:
This example demonstrates how to create a force layout of a hierarchy whose internal nodes are collapsible. Leaf nodes are shown in orange, while internal nodes (packages) are shown in blue. Clicking on an internal node (without dragging) causes that node to expand or collapse, toggling the visibility of its descendant nodes.
We use the force layout as the basis of our visualization. Departments are shown by the larger, blue nodes, while individuals are shown as smaller orange nodes.
The layout can be dragged around as the user sees fit. The department nodes are pinned so that they will stay wherever they are dragged to.
####Tipsy
We have added Tipsy tooltips to the layout. When hovering over a node, the tooltip will automatically come up and show the text contained by the node.
####Future Plans
xxxxxxxxxx
<meta charset="utf-8">
<!--I changed the format because I noticed there were some unclosed tags, and this seemed a little more organized. Also I was trying to get Tipsy to be more friendly-->
<html>
<head>
<link rel="stylesheet" href="tipsy.css" type="text/css" />
<script type='text/javascript' src="//code.jquery.com/jquery-1.6.2.min.js"></script>
<script type='text/javascript' src="jquery.tipsy.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="d3.superformula.v0.min.js"></script>
<script src="miso.ds.deps.0.4.1.js"></script>
<style>
.node text {
font: 10px sans-serif;
pointer-events: none;
text-anchor: middle;
}
line.link {
fill: none;
stroke: #9ecae1;
stroke-width: 1.5px;
}
.transformer {
cursor: pointer;
stroke: #3182bd;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script type="text/javascript">
var width = 960,
height = 500,
root;
var SPREADSHEET_ID = "0Agy_OAo_DS4PdGxqSG1Fa3lSNnlXemM1MllBN2hENEE";
var SPREADSHEET_TAB = "1";
var force = d3.layout.force()
.linkDistance(80)
.charge(-120)
.gravity(.05)
.size([width, height])
.on("tick", tick);
var svg = d3.select("body").append("svg:svg")
.attr("width", width)
.attr("height", height);
var link = svg.selectAll(".link"),
node = svg.selectAll(".node");
d3.json("its.json", function(error, json) {
root = json;
update();
});
loadGSS(SPREADSHEET_ID, SPREADSHEET_TAB);
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; });
//size = node.data(nodes, function(d) {return d.size; });
transformer = d3.superformula()
.type(function(d) {return d.shape;})
.size(function(d) {return determineNodeSize(d);})
.segments(180);
function determineNodeSize(d) {
if (d.shape === "circle") {
return d.size/10;
} else {
return d.size*5;
}
}
node.exit().remove();
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on("click", click)
.on("dblclick", click2) //the two clicks sometime conflict, but its enough to show functionality
.call(force.drag);
nodeEnter.append("path")
.attr("class", "transformer")
.attr("d", transformer)
;
node.select("path")
.style("fill", color)
.append("title")
.text(function(d) { return d.name; })
;
$("svg path").tipsy({
gravity: 'w',
html: true,
title: function() {
var d = this.__data__
return d.name;}
});
nodeEnter.append("text")
.attr("dy", ".35em")
.text(function(d) { return determineText(d); })
.style("pointer-events", "none");
node.select("text")
//.text(function(d) { return determineText(d); })
.style("font-weight", fontweight)
function determineText(d) {
if (d.shape == "square")
return d.info;
return d.name;
}
function click2(d) {
if (d3.event.defaultPrevented) return;
if (d.shape == "circle") {
d.shape = "square";
node.select("text").text(function(d) { return determineText(d); }); //updates text in transformation
d3.selectAll("path")
.transition()
.duration(500)
.attr("d", transformer);
return
} else {
d.shape = "circle";
node.select("text").text(function(d) { return determineText(d); });
d3.selectAll("path")
.transition()
.duration(500)
.attr("d", transformer);
return
}
}
}
function tick() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
}
function fontweight(d) {
return d._children ? "bold"
: d.children ? "bold"
: "normal";
}
function color(d) {
return d._children ? "#3182bd" // collapsed package
: d.children ? "#c6dbef" // expanded package
: "#fd8d3c"; // leaf node
}
// Toggle children on click.
function click(d) {
if (d3.event.defaultPrevented) return; // ignore drag
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
// 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;
}
function loadGSS(ssid, tab) {
//connect to spreadsheet
var lsheet = new Miso.Dataset({
importer : Miso.Dataset.Importers.GoogleSpreadsheet,
parser : Miso.Dataset.Parsers.GoogleSpreadsheet,
key : ssid,
worksheet : tab
});
//load data from spreadsheet
lsheet.fetch({
success : function() {
this.each(function(row){
// console.log(row);
});
},
error : function() {
console.log("Data failed to load from GSS.");
}
});
}
</script>
</body>
</html>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://code.jquery.com/jquery-1.6.2.min.js
https://d3js.org/d3.v3.min.js