d3.js force layout doesn't run - d3.js

I'm just starting with d3.js and can't get a simple demo to work correctly.
It's tough to debug: If there’s a bug, it often crashes in d3′s minified code with no stack trace. In this case, it doesn’t print any errors to the error console at all.
When I run this code, all my nodes are stuck in position (0,0) instead of being laid out by force(). What's wrong?
<html>
<meta charset="UTF-8">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
$(function() {
var svg = d3.select('#graph').append('svg').attr('width', 900).attr('height', 900)
var myNodes = [{name:'a'}, {name:'b'}, {name:'c'}]
var myLinks = [{source:myNodes[0], target:myNodes[1]}, {source:myNodes[1], target:myNodes[2]}, {source:myNodes[2], target:myNodes[0]}]
svg.append("text").text("myNodes[0].name=" + myNodes[0].name).attr('y', 50)
var force = d3.layout.force().charge(-120).linkDistance(30).size([900, 900])
var link = svg.selectAll("line")
.data(myLinks)
.enter().append("svg:line");
var node = svg.selectAll("circle")
.data(myNodes)
.enter().append("svg:circle")
.attr("r", 6)
.call(force.drag)
force.nodes(myNodes).links(myLinks).start()
});
</script>
</head>
<body>
<div id="graph"></div>
</body>
</html>

Force just handles setting x/y attributes on the nodes and links:
... The initial x and y coordinates, if not already set externally to a valid number, are computed by examining neighboring node..
It does not actually handle updating of the corresponding svg shapes. This you would typically do in the tick event:
Listen to tick events to update the displayed positions of nodes
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; });
});
The only other piece missing from your example is setting the style of the lines. By default they have no stroke color so appear invisible. You can either set the color directly on the line, like this:
var link = svg.selectAll("line")
.data(myLinks)
.enter().append("svg:line")
.attr('stroke', 'red')
.attr('stroke-width', 2)
Or make a CSS class for them:
<style>
.link {
stroke: blue;
stroke-width: 1.5px;
}
</style>
And tag them with it when you create them:
var link = svg.selectAll("line")
.data(myLinks)
.enter().append("svg:line")
.attr('class', 'link')

Related

How to use hover and add text over node in d3 force simulation

I am trying to make d3 force simulation where i am parsing data from csv file and i am trying use hover and showing text over node but i am unable to show it though i am able to parsing csv file.
Here is my code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.link {
stroke: #000;
}
.node {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script>
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.size([width, height]);
d3.csv("data.csv", function(error, links) {
if (error) throw error;
var nodesByName = {};
// Create nodes for each unique source and target.
links.forEach(function(link) {
link.source = nodeByName(link.source);
link.target = nodeByName(link.target);
});
// Extract the array of nodes from the map by name.
var nodes = d3.values(nodesByName);
// Create the link lines.
var link = svg.selectAll(".link")
.data(links)
.enter().append("line")
.attr("class", "link");
// Create the node circles.
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 4.5)
.call(force.drag);
// Start the force layout.
force
.nodes(nodes)
.links(links)
.on("tick", tick)
.start();
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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function nodeByName(name) {
return nodesByName[name] || (nodesByName[name] = {name: name});
}
});
</script>
Here, Is my output
Actually, I am new in d3 force simulation, any suggestion is highly appreciated.
Thanks
Perhaps this would work,
var node = svg.selectAll(".node")
.data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 4.5)
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut);
.call(force.drag);
and then call the functions as in the example here http://bl.ocks.org/WilliamQLiu/76ae20060e19bf42d774

D3.js v4 Force layout and fixed node anomaly

I have a D3.js V4 Force layout, comprising two nodes linked by a single edge. One node is fixed near top left, the other free to move.
When the layout is run, the non-fixed node starts in the middle of the layout, and moves away from the fixed node, as though repelled. It ends up in the opposite corner from the fixed node.
I would expect the free node to end up between the centre of the layout (where gravity pulls it) and the fixed node (pulled towards it by the link force). What am I missing, please?
var width = 240,
height = 150;
var nodes = [
{ // Larger node, fixed
fx: 20, fy: 20, r: 10
},
{ // Small node, free
r: 5
}];
var links = [{ // Link the two nodes
source: 0, target: 1
}
];
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
// Create simulation with layout-centring and link forces
var force = d3.forceSimulation()
.force("centre", d3.forceCenter(width / 2, height / 2))
.force("link", d3.forceLink())
.nodes(nodes);
force.force("link").links(links);
// Draw stuff
var link = svg.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var node = svg.selectAll('.node')
.data(nodes)
.enter().append('circle')
.attr('class', 'node')
force.on('tick', function() {
node.attr('r', function(d) {
return d.r;
})
.attr('cx', function(d) {
return d.x;
})
.attr('cy', function(d) {
return d.y;
});
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;
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<style>
.node {
fill: #f00;
stroke: #fff;
stroke-width: 2px;
}
.link {
stroke: #777;
stroke-width: 2px;
}
</style>
</head>
<body>
</body>
</html>
The Centering force will try to have the center of mass of all nodes in the coordinate given. As you have 2 nodes and 1 is fixed, the other will be symmetric to the fixed node.
d3 Centering force documentation
Centering
The centering force translates nodes uniformly so that the mean
position of all nodes (the center of mass if all nodes have equal
weight) is at the given position ⟨x,y⟩.

web2py d3.json() where to put the json file?

I'm trying to use this sample code.
My question is for this line : d3.json("miserables.json", function(error, graph){}
Where should I put the miserables.json file?
My d3.js is in static/js/d3.py
Should I just put the file under static/js?
I have tried this, but all I got is a blank page.
I'm not familiar with how path works for d3.json()
My html file:
{{extend 'layout.html'}}
<div id="vi"></div>
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<body>
<script src="http://d3js.org/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.select("#vi").json("miserables.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) { return Math.sqrt(d.value); });
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { 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>
<p>k<p>
This response.files.append(URL('static', 'plugin_d3/d3/d3.js')) is in my function code already. And there's an copy of the json code under static/plugin_d3/d3 also!
Please bear me for this simple question.
Thanks!!
Yes, you can put miserable.json in the /static folder (perhaps in a subfolder within /static). You then have to use the correct URL to refer to it. Given that you code is in a web2py template, you can use the URL() function to generate the URL:
d3.select("#vi").json("{{=URL('static', 'miserables.json')}}", ...)
In your original code:
d3.select("#vi").json("miserables.json", ...)
"miserable.json" is a relative URL, so the browser will just append that to the URL of the current page, which would end up being something like /yourapp/default/index/miserables.json. Instead, you need to specify the full URL, such as /yourapp/static/miserables.json (which the URL() function will generate).

d3 force layout nodes are pinning on dblick but appearance does not modify

Using and playing around with the Les Miserables Json data, I coded from modifications in d3. I was able to code everything except highlighting/marking a node if it is in the "fixed" state (vs the force graph layout). When you double click a node, it will freeze, a drag and move will pin it somewhere while all the other nodes (by default) in the force layout. When you double click the node again, the node is unpinned, and floating in the force layout as usual. The second thing I wanted to do is that if the node is in a “pinned” state, the node should be a different color, or highlighted somehow. I tried a few ways within the method chaining, checking with conditional statements for the node state to modify the node features, but they are not working. I also separated out the dblclick handler as an outside function (which is my current version.
I put my code here of the original attempt within the method chains:
http://pastebin.com/SqrqgVET
I also tried another way, using a variable “pinned” to determine the state of the node, within the double-click, to modify the nodes border, and change the state accordingly but that is not changing the node’s appearance either. There is a console.log() statement, and the function is going into the if else portions accordingly.
http://pastebin.com/dzEw42mQ
Below is the current version.
Any feedback would be great. Thanks!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
cursor: move;
stroke: #fff;
stroke-width: 1.5px;
}
.node.fixed {
fill: #f00;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<head>
<title>Victor Hugo Had No Internet</title>
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="http://d3js.org/colorbrewer.v1.min.js"></script>
</head>
<body>
<script type="text/javascript">
//Size of region to render on
var width = 960,
height = 500;
var color = d3.scale.ordinal()
.domain([1, 10])
.range(colorbrewer.BrBG[9]);
//D3 force directed layout
//Try playing with the charge and link distance
var force = d3.layout.force()
.charge(-100)
.linkDistance(40)
//.on("tick", tick) //event ADDED
.size([width, height]);
//Add our canvas
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Select the miserables data ;)
d3.json("miserables.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
//Add the links
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
//Add the nodes
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function(d){ return Math.sqrt(d.coolness);})
.attr("stroke", "#ffffff")
.on("dblclick", dblclick)
.call(force.drag)
.style("fill", function(d) {return color(d.group);}); //});
//.style("border", 5);//function(d) {
//if (d.fixed==false) {return 4}; });
// node.append("text") //label
// .attr("dx", 6)
// .attr("dy", ".10em")
// .text(function(d) { return d.name; });
//add labels
var labels = svg.selectAll("text")
.data(graph.nodes)
.enter()
.append("text")
.attr({"x":function(d){return d.x;},
"y":function(d){return d.y;}})
.text(function(d) {
if (d.coolness>25 && d.name.length > 6) {return d.name.substring(0,6)+'...'}
if (d.coolness>25 && d.name.length < 6) {return d.name}
else { return null } ;})
.call(force.drag);
//Update stuff for animation:
// This takes the physics simulation for the force directed graph and
// sets the location of the nodes and edges to the new positions
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; });
labels.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
});
// action to take on mouse double click
function dblclick(d) { //color, stroke not working.
//var pinned = d3.select(this).attr("stroke");
console.log("dblclick")
if (d.fixed == true) { //pinned state
console.log("pinned")
d3.select(this)
.attr("stroke", "#ffffff")
.attr("stroke-width", 1.5)
.classed("fixed", d.fixed = false);//now unpin
} else { //else not pinned state
console.log("not pinned")
d3.select(this)
.attr("stroke", "#000000")
.attr("stroke-width", 4)
.classed("fixed", d.fixed = true);
}
}//end dbl click
});
</script>`
Here is the output of the console log (where I added console.log(this) to check the right element is being passed to the dblclick function and if parameters are being set accordingly:
[Log] dblclick (miserables_graph.html, line 131)
[Log] <circle class=​"node fixed" r=​"9.1104335791443" stroke=​"#000000" style=​"fill:​ #f6e8c3;​" cx=​"630.38114584665" cy=​"98.39845698676822" stroke-width=​"4">​</circle>​ (miserables_graph.html, line 132)
[Log] not pinned (miserables_graph.html, line 140)
[Log] dblclick (miserables_graph.html, line 131)
[Log] <circle class=​"node" r=​"9.1104335791443" stroke=​"#ffffff" style=​"fill:​ #f6e8c3;​" cx=​"630.38114584665" cy=​"98.39845698676822" stroke-width=​"1.5">​</circle>​ (miserables_graph.html, line 132)
[Log] pinned (miserables_graph.html, line 134)

Drawing D3 Rectangles

I am a beginner with D3 and JS in general.
I am trying to do a simple rectangle visualisation with a small csv file as a source.
price, units
80.67, 100
80.87, 99
79.34, 47
File, csv are in the same folder.
I am using Python's SimpleHTTPServer to serve locally in this folder.
This is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Data</title>
<script type="text/javascript" src="../d3/d3.v3.js"></script>
</head>
<body>
<script type="text/javascript">
// load csv from the same directory
d3.csv("test.csv", function (data){
return {
price: +data.price, // convert to number with +
units: +data.units, // convert to number with +
};
var canvas = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500)
canvas.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", function (d) { return d.price; })
.attr("height", 48)
.attr("y", function (d) { return d.units; })
.attr("fill", "blue");
canvas.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("fill", "white")
.attr("y", function (d) { return d.units + 24; })
.text( function (d) { return d.units;})
});
</script>
</body>
I am getting no errors, just a blank page.
What is wrong with this code?
The first thing you do in your callback is to return. None of the code after that is being executed. I'm referring to
return {
price: +data.price, // convert to number with +
units: +data.units, // convert to number with +
};
which should probably be
data.forEach(function(d) {
d.price = +d.price;
d.units = +d.units;
});
The signature of the callback should also be function(error, data) instead of function(data).

Resources