Related
I am trying to build a force directed graph in D3. Here is the code for it -
index.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
stroke: #FFFF;
stroke-width: 1.5px;
}
.link {
stroke: #111;
}
</style>
<body>
<h1>Hello there</h1>
<script src="d3.v3.min.js"></script>
<script>
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("copy.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { console.log(d.value); return d.value; });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 10)
.style("fill", function(d) { console.log(d.name); return color(d.group); })
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
</html>
This code works with copy.json file. I just copy pasted the json from here into that file. I have tried to create my own json by parsing through a txt file. The same code is giving me the error when I use my json file. Here is my json file (part of it) -
{
"nodes":[
"group": 5,
"name": "Nancie"
},
{
"group": 5,
"name": "Jonell"
}
],
"links": [
{
"source": 1,
"target": 29,
"value": 2
},
{
"source": 1,
"target": 43,
"value": 3
}
]
}
I have tested this json on jsonlint. Does the newline make a difference? I did read a couple of answer that are there on SO about this but none of them match. My source and target nodes are within range, I have no null values and I have taken care of the case when creating my JSON.
Well as it turns out I made a stupid mistake while creating my json file. In the links part of the json the numbering for source starts from 0. Hence for the last element I was exceeding the value. And hence I was getting that error.
First sorry for my question, because I think this is because I don't understand very well D3 library, especially the working of Selections.
Here's what I want to do:
Showing a graph that links publications with people who tweet about the publications.
Update the graph when the data change (in this case when use clicks the paragraph above that says "click to update").
Here I don't want to do complete redraw of the graph; I want the existing nodes and links stay where they are, and new nodes and links fly in to the scene. That's why I keep the instance of force layout outside of the drawGraph function (as global variable).
Task #1 done without a problem.
The problem is with task #2; I could make the new node enter the scene..., but for some reason I cannot drag the existing nodes. I can only drag the new nodes (Eduardo).
I have debugged it in Chrome, and saw this "var a" has 9 elements (after clicking). So, I suppose the function "force.layout" is supposed to be invoked by D3 for all those 9 elements. So, if I undestand correctly, I should've been able to drag all the 9 circles.
But that's not the case, so something is wrong with my code. Can anybody please point out where I got it wrong?
Here is the code. And after that comes the JSON (two separate jsons, news.json and news3.json).
Additional question:
I don't quite understand why this block (the key function) was executed 17 times (8 + 9) on the second invocation of drawGraph function (when the json is updated to news3.json)? My expectation is 9 times.
var lineSelections = svg.selectAll('line')
.data(dataset.edges, function(d){
console.log(d);
return d.source.index + '.' + d.target.index;
});
Thanks in advance!,
Raka
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Force layout</title>
<script type="text/javascript" src="../d3/d3.v3.js"></script>
<style type="text/css">
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="tooltip" class="hidden">
<p><span id="type"></span></p>
<p><span id="name"></span></p>
</div>
<p id="refresh">Click on this text to update the chart with new data values (once).</p>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 300;
//http://stackoverflow.com/questions/16455194/how-to-store-a-json-object-loaded-from-a-file
var force = d3.layout.force()
.size([w, h])
.linkDistance([20])
.charge([-50]);
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
drawGraph = function(dataset) {
force
.nodes(dataset.nodes)
.links(dataset.edges)
.start();
var lineSelections = svg.selectAll('line')
.data(dataset.edges, function(d){
console.log(d);
return d.source.index + '.' + d.target.index;
});
//Create edges as lines
var edges = lineSelections
.enter()
.append('line')
.style('stroke', function(d) {
if (d.target.type === 'publication') {
return 'red';
} else {
return 'blue';
}
})
.style('stroke-width', function(d) {
return d.weight;
});
var groups = svg.selectAll('g')
.data(dataset.nodes, function(d) {
console.log(d.name);
return d.name;
})
.enter()
.append('g');
var nodes = groups
.append('circle')
.attr('r', function(d) {
if (d.type === 'publication') {
var radius = d.weight / 6;
if (radius < 5) {
radius = 5;
}
return radius;
} else {
return d.weight * 3;
}
})
.style('fill', function(d, i) {
if (d.type === 'publication') {
return 'black';
} else {
return 'green';
}
});
var a = svg.selectAll('g circle');
a.call(force.drag);
//Create labels
var text = groups
.append('text')
.text(function(d) {
if (d.type === 'publication') {
return d.name;
} else {
return '';
}
})
.attr('x', function(d, i) {
d.x;
})
.attr('y', function(d) {
d.x;
})
.attr('font-family', 'sans-serif')
.attr('font-size', '24px')
.attr('fill', 'orange');
groups
.on("mouseover", function(d) {
//Get this bar's x/y values, then augment for the tooltip
//var hmm = d3.select(this).select('circle').attr('cx');
var xPosition = d3.select(this).select('circle').attr('cx');
var yPosition = d3.select(this).select('circle').attr('cy');
//Update the tooltip position and value
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#type")
.text(d.type);
d3.select("#tooltip")
.style("left", xPosition + "px")
.style("top", yPosition + "px")
.select("#name")
.text(d.name);
//Show the tooltip
//d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
//Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
})
//Every time the simulation "ticks", this will be called
force.on('tick', function() {
edges.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; });
nodes.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
//Update all labels
svg.selectAll('text')
.data(dataset.nodes)
.attr('x', function(d, i) {
return d.x;
})
.attr('y', function(d) {
return d.y;
});
});
}
d3.json('news.json', function(dataset) {
drawGraph(dataset);
});
d3.select("p")
.on("click", function() {
});
d3.select("#refresh")
.on("click", function() {
d3.json('news3.json', function(dataset) {
console.log(dataset);
drawGraph(dataset);
});
});
</script>
</body>
</html>
news.json
{
"nodes": [
{ "name": "El Universal", "type": "publication", "weight": 10 },
{ "name": "Milenio", "type": "publication", "weight": 4},
{ "name": "Proceso", "type": "publication", "weight": 4},
{ "name": "Paco", "type": "person", "weight": 12},
{ "name": "Juan", "type": "person", "weight": 5},
{ "name": "Alberto", "type": "person", "weight": 5 },
{ "name": "Xochitl", "type": "person", "weight": 3 },
{ "name": "Reforma", "type": "publication", "weight": 2}
],
"edges": [
{ "source": 3, "target": 0, "weight": 9},
{ "source": 3, "target": 1, "weight": 3},
{ "source": 4, "target": 2, "weight": 4},
{ "source": 4, "target": 0, "weight": 1},
{ "source": 5, "target": 3, "weight": 5},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 6, "target": 7, "weight": 2},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 3, "target": 5, "weight": 4},
{ "source": 4, "target": 5, "weight": 1}
]
}
news3.json
{
"nodes": [
{ "name": "El Universal", "type": "publication", "weight": 10 },
{ "name": "Milenio", "type": "publication", "weight": 4},
{ "name": "Proceso", "type": "publication", "weight": 4},
{ "name": "Paco", "type": "person", "weight": 12},
{ "name": "Juan", "type": "person", "weight": 5},
{ "name": "Alberto", "type": "person", "weight": 5 },
{ "name": "Xochitl", "type": "person", "weight": 3 },
{ "name": "Reforma", "type": "publication", "weight": 2},
{ "name": "Eduardo", "type": "person", "weight": 2}
],
"edges": [
{ "source": 3, "target": 0, "weight": 9},
{ "source": 3, "target": 1, "weight": 3},
{ "source": 4, "target": 2, "weight": 4},
{ "source": 4, "target": 0, "weight": 1},
{ "source": 5, "target": 3, "weight": 5},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 6, "target": 7, "weight": 2},
{ "source": 6, "target": 1, "weight": 1},
{ "source": 3, "target": 5, "weight": 4},
{ "source": 4, "target": 5, "weight": 1},
{ "source": 8, "target": 7, "weight": 2}
]
}
I got it working, after I modified the function assigned to the tick event on force.on('tick', ...);. Small modif: instead of using groupSelection.selectAll('circle') and groupSelection.selectAll('text'), now I use groupSelection.select('circle') and groupSelection.select('text').
You can see a working demo here.
Here is the code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Force layout</title>
<script type="text/javascript" src="../d3/d3.v3.js"></script>
<style type="text/css">
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="tooltip" class="hidden">
<p><span id="type"></span></p>
<p><span id="name"></span></p>
</div>
<p id="refresh">Click on this text to update the chart with new data values (once).</p>
<script type="text/javascript">
//Width and height
var w = 300;
var h = 200;
//http://stackoverflow.com/questions/16455194/how-to-store-a-json-object-loaded-from-a-file
var force = d3.layout.force()
.size([w, h])
.linkDistance([20])
.charge([-50]);
var svg = d3.select('body')
.append('svg')
.attr('width', w)
.attr('height', h);
drawGraph = function(dataset) {
force.nodes(dataset.nodes);
force.links(dataset.edges);
force.start();
var lineSelections = svg.selectAll('line')
.data(dataset.edges, function(d){
return '.'.concat(d.source.name, '.', d.target.name);
});
lineSelections
.enter()
.append('line')
.style('stroke', function(d) {
if (d.target.type === 'publication') {
return 'red';
} else {
return 'blue';
}
})
.style('stroke-width', function(d) {
return d.weight;
});
var groupSelection = svg.selectAll('g')
.data(dataset.nodes, function(d) {
return d.name;
})
.call(force.drag);
var groups = groupSelection
.enter()
.append('g')
.call(force.drag);
groups
.append('circle')
.attr('r', function(d) {
if (d.type === 'publication') {
var radius = d.weight / 6;
if (radius < 5) {
radius = 5;
}
return radius;
} else {
return d.weight * 3;
}
})
.style('fill', function(d, i) {
if (d.type === 'publication') {
return 'black';
} else {
return 'green';
}
});
//Create labels
groups
.append('text')
.text(function(d) {
if (d.type === 'publication') {
return d.name;
} else {
return d.name;
}
})
.attr('x', function(d, i) {
d.x;
})
.attr('y', function(d) {
d.x;
})
.attr('font-family', 'sans-serif')
.attr('font-size', '14px')
.attr('fill', 'orange');
//Every time the simulation "ticks", this will be called
force.on('tick', function() {
lineSelections
.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;
});
groupSelection
.select('circle')
.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
});
//Update all labels
groupSelection
.select('text')
.attr('x', function(d, i) {
return d.x;
})
.attr('y', function(d) {
return d.y;
});
});
}
d3.json('news.json', function(dataset) {
drawGraph(dataset);
});
d3.select("p")
.on("click", function() {
});
d3.select("#refresh")
.on("click", function() {
console.log('==================');
d3.json('news3.json', function(dataset) {
drawGraph(dataset);
});
});
</script>
</body>
</html>
I am trying to draw nodes (circles) in D3 Javascript library based on the following JSON file:
{
"nodes":[{"name":["Node1", "Node2", "Node3"]},
{"name":["Node4"]},
{"name":["Node5"]}]
}
Here are my codes:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Sample2</title>
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<script>
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("folder/jsonsample.json", function(error, graph) {
force
.nodes(graph.nodes)
.start();
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 10)
.call(force.drag);
node.append("title")
.text(function(d) { return d.name; });
force.on("tick", function() {
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
</script>
</body>
</html>
With this HTML file, I drew only three circles because the code is based on the fact that there are three names. I would like to draw five nodes (circles) since I have Node1, Node2, Node3, Node4 and Node5 inside the "name, name, and name". Could anyone please help me draw five circles. Your help is very much appreciated.
Maybe you can use Collapsible Force Layout, see example at mbostock block. With that example and the following json file (changed his readme.json file):
{
"name": "example",
"children": [
{
"name": "group1",
"children": [
{"name": "node1", "size": 3000},
{"name": "node2", "size": 3000},
{"name": "node3", "size": 3000}
]
},
{
"name": "node4", "size": 5000
},
{
"name": "node5", "size": 10000
}
]
}
you can get something similar. You get one root element with 3 children, one has 3 nodes. See
If you select root node everything colapse.
There is example how to have a label on the node in a D3 forced graph. What I try to do is to have a label on the line instead.
Example of the label on node: http://bl.ocks.org/mbostock/2706022
This code will display the text for the line up in the left corner. It seems that it takes the x, y cordinates from the canvas and not from my line. How to fix this?
var labelLine = olinks.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text("eeeeee");
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<body>
<script src="./Script/d3.v3/d3.v3.min.js"></script>
<script>
var graph = {
"nodes": [
{ "name": "App1-main", "group": 1 },
{ "name": "App2", "group": 1 },
{ "name": "App3", "group": 1 },
{ "name": "App4", "group": 1 },
{ "name": "Content-1", "group": 3 },
{ "name": "Content-1", "group": 3 },
{ "name": "Content-1", "group": 3 },
{ "name": "Content-1", "group": 3 },
{ "name": "Pontmercy", "group": 3 }
],
"links": [
{ "source": 1, "target": 0, "value": 1 },
{ "source": 2, "target": 0, "value": 1 },
{ "source": 0, "target": 3, "value": 1 }
]
};
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-300)
.linkDistance(60)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var drawGraph = function (graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var olinks = svg.selectAll("g.link")
.data(graph.links)
.enter().append("g")
.call(force.drag);
var link = olinks.append("line")
.attr("class", "link")
.style("stroke-width", function (d) { return Math.sqrt(d.value); });
var labelLine = olinks.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text("eeeeee");
var gnodes = svg.selectAll('g.gnode')
.data(graph.nodes)
.enter()
.append('g')
.classed('gnode', true);
var node = gnodes.append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function (d) { return color(d.group); })
.call(force.drag);
var labels = gnodes.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function (d) { return d.name; });
console.log(labels);
force.on("tick", function () {
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; });
gnodes.attr("transform", function (d) {
return 'translate(' + [d.x, d.y] + ')';
});
});
};
drawGraph(graph);
</script>
This is an example that has correct behavior.
The key points are here:
1) You need to define link as SVG "g" element, so that you can define both lines and labels for each link, and that coordinates are computed correctly.
2) Label text must be centered horizontally (code: .attr("text-anchor", "middle")).
3) Inside tick(), you need to compute coordinate of the labels., as arithmetic mean between source and target node.
Hope this helps.
There also was another similar question recently.
I am trying to make a force directed graph in d3.js interactive.
I created the html that displays it and also wrote the python scripts that generate the content. But I can't get it to work together.
I can call the python script from the browser and it returns valid json but how do i get it into d3?
Somehow it always calls the url with the result appended (*GET http://localhost/vici/[object%20Object] 404 (Not Found)*) but the alert displays the result of the python script.
This is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<script type="text/javascript" src="d3.v2.js?2.1.3"></script>
<script src="jquery-1.8.3.js"></script>
<style type="text/css">
circle {
stroke-width: 1.5px;
}
line {
stroke: #999;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
</style>
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type: "GET",
url: "/vici/kellner.py/display",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {name: 302,level: 2}
})
.success(function(pyjson) {
alert(pyjson);
var data = pyjson;
var w = 960,
h = 600,
r = 6;
var svg = d3.select("#d3vis").append("svg:svg")
.attr("width", w)
.attr("height", h);
d3.json(pyjson, function(json) {
var force = d3.layout.force()
.nodes(json.nodes)
.links(json.links)
.gravity(.02)
.distance(50)
.charge(-400)
.size([w, h])
.start();
var link = svg.selectAll("line.link")
.data(json.links)
.attr("class", "link")
.enter().append("svg:line");
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("image")
.attr("xlink:href", function(d) { return d.avatar })
.attr("x", -8)
.attr("y", -8)
.attr("width", function(d) { return d.avatarWidth })
.attr("height", function(d) { return d.avatarHeight });
node.append("text")
.attr("dx", 12)
.attr("dy", ".70em")
.text(function(d) { return d.avatarName });
node.append("svg:title")
.text(function(d) { return d.author; }, function(d) { return d.institute; } );
force.on("tick", function() {
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 + ")"; });
});
});
});
});
</script>
<div id="d3vis"></div>
</body>
</html>
But not just that: How can I get d3 to reload after clicking a node? Every node has a name ("author":"Some Name") and my python script can search for that name and return a new result.
I have search for this and found some things (e.g. Updating links on a force directed graph from dynamic json data) what seems would be really nice and what I would want to do, but I do not understand it so I can't modify it to my needs. (Where does the data come from or where can i call my python script to provide the data?)
I just need to get the json into d3 and change the graph when clicking on a node.
I am new to Java Script so any help would be greatly appreciated.
EDIT:
Here still some test data:
{
"nodes": [
{"institute": "None", "avatarWidth": "16", "avatar": "img/user.png", "avatarHeight": "16", "author": "Some Name"},
{"institute": "None", "avatarWidth": "16", "avatar": "img/user.png", "avatarHeight": "16", "author": "Some Name"},
{"institute": "None", "avatarWidth": "16", "avatar": "img/user.png", "avatarHeight": "16", "author": "Some Name"},
{"author": "Main Name", "institute": "None", "avatarName": "Main Name", "avatar": "img/main.png", "avatarHeight": "24", "avatarWidth": "24"},
{"institute": "None", "avatarWidth": "16", "avatar": "img/user.png", "avatarHeight": "16", "author": "Some Name"}
],
"links": [
{"color": "red", "source": 0, "target": 2, "weight": 1},
{"color": "orange", "source": 1, "target": 2, "weight": 1},
{"color": "yellow", "source": 2, "target": 3, "weight": 1},
{"color": "yellow", "source": 2, "target": 4, "weight": 1}
]
}
Okay, so apparently the d3.json(pyjson, function(json) { Line was the "bad" one. Is this d3 trying to make its own ajax request?
Anyway I removed it and it seems to be working now. Also my idea of clicking a node to reload is not really ideal since you have to be able to pull them around and clicking would stop that.
Here my functioning code for future reference: (And others that have the same problem)
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<script type="text/javascript" src="d3.v2.js?2.1.3"></script>
<script src="jquery-1.8.3.js"></script>
<style type="text/css">
circle {
stroke-width: 1.5px;
}
line {
stroke: #999;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
</style>
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
$.ajax({
type: "GET",
url: "/vici/kellner.py/display",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: {name: 302,level: 2}
})
.done(function(pyjson) {
var w = 960,
h = 600,
r = 6;
var svg = d3.select("#d3vis").append("svg:svg")
.attr("width", w)
.attr("height", h);
var force = d3.layout.force()
.nodes(pyjson.nodes)
.links(pyjson.links)
.gravity(.02)
.distance(50)
.charge(-400)
.size([w, h])
.start();
var link = svg.selectAll("line.link")
.data(pyjson.links)
.attr("class", "link")
.enter().append("svg:line");
var node = svg.selectAll(".node")
.data(pyjson.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("image")
.attr("xlink:href", function(d) { return d.avatar })
.attr("x", -8)
.attr("y", -8)
.attr("width", function(d) { return d.avatarWidth })
.attr("height", function(d) { return d.avatarHeight });
node.append("text")
.attr("dx", 12)
.attr("dy", ".70em")
.text(function(d) { return d.avatarName });
node.append("svg:title")
.text(function(d) { return d.author; }, function(d) { return d.institute; } );
force.on("tick", function() {
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 + ")"; });
}); //End of force.on("tick", function() {
}); //End of .done(function(pyjson) {
}); //End of $(document).ready(function() {
</script>
<div id="d3vis"></div>
</body>
</html>