Related
I'm able to get images and labels to show for the nodes, but they show at the top left of the screen.
Nodes show up in the correct position when I use this
.enter().append("circle")
Labels and node images show at the top left (incorrect) when I use this:
.enter().append("g")
This works with append "circle" (commented out in the code below):
When I comment out append circle and use append "g" (in order to use node images and labels) the images and labels all show up near (0,0) instead of near the node:
Also, what exactly is append "g"? Where is the documentation to find out what's possible with append "g"?
Here is all the code:
<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);
var graph = getData();
var nodeMap = {};
graph.nodes.forEach(function(d) { nodeMap\[d.name\] = d; });
graph.links.forEach(function(l) {
l.source = nodeMap\[l.source\];
l.target = nodeMap\[l.target\];
})
force.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke", function(d) {
return d.line_color;
})
.style("stroke-width", function(d) {
return Math.sqrt(d.value)+1;
});
var node = svg.selectAll(".node")
.data(graph.nodes)
// .enter().append("circle")
// .attr("class", "node")
// .attr("r", 10)
// .style("fill", function(d) { return d.fill_color; })
// .call(force.drag);
.enter().append("g")
.attr("class", "node")
.attr("r", 15)
.style("fill", function(d) { return d.fill_color; })
.on("click", function(d){
alert("You clicked on node " + d.name);
})
.call(force.drag);
node.append("title")
.text(function(d) { return d.label; });
node.append("image")
.attr("xlink:href", function(d) { return d.image_url })
.attr("x", -8)
.attr("y", -8)
.attr("width", 26)
.attr("height", 26);
node.append("text")
.attr("dx", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return 16;
}
})
.attr("dy", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return ".35em";
}
})
// .attr("dy", ".35em")
.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; });
});
function getData() {
return {
"nodes":\[
{"name":"user1","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user2","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user3","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"tag1","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag2","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag3","image_url":"","fill_color":"blue","text_color":"black"}
\],
"links":\[
{"source":"tag1","target":"user1","value":1,"line_color":"green"},
{"source":"tag2","target":"user1","value":1,"line_color":"green"},
{"source":"tag3","target":"user1","value":1,"line_color":"green"},
{"source":"tag1","target":"user2","value":1,"line_color":"green"},
{"source":"tag2","target":"user2","value":1,"line_color":"green"}
\]
};
}
</script>
With .append("g") you insert a SVG Group Element.
The problem is, that you try to apply attributes that are for circles, like the radius with .attr("r",15), to the group element.
You have to use circles if you want to draw a circle. Group elements do not have any shape. They are used to group elements like circles.
A solution would be to append the g element and transform it to the location of the node. I updated your code in the following snippet. I used the group elements and added the circle, image and text inside the group elements.
Moreover I removed the backslashes before each angular bracket and set the title to the field name instead of label.
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);
var graph = getData();
var nodeMap = {};
graph.nodes.forEach(function(d) { nodeMap[d.name] = d; });
graph.links.forEach(function(l) {
l.source = nodeMap[l.source];
l.target = nodeMap[l.target];
})
force.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter()
.append("line")
.attr("class", "link")
.style("stroke", function(d) {
return d.line_color;
})
.style("stroke-width", function(d) {
return Math.sqrt(d.value)+1;
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter()
.append("g")
.attr("transform", function(d){return "translate("+d.x+","+d.y+")"})
.call(force.drag);
node.append("circle")
.attr("class", "node")
.attr("r", 15)
.style("fill", function(d) { return d.fill_color; })
.on("click", function(d){
alert("You clicked on node " + d.name);
});
node.append("title")
.text(function(d) { return d.name; });
node.append("image")
.attr("xlink:href", function(d) { return d.image_url })
.attr("x", -8)
.attr("y", -8)
.attr("width", 26)
.attr("height", 26);
node.append("text")
.attr("dx", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return 16;
}
})
.attr("dy", function(d) {
if (d.image_url == "/profile.png"){
return 100;
}
else{
return ".35em";
}
})
// .attr("dy", ".35em")
.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("transform", function(d){return "translate("+d.x+","+d.y+")"});
});
function getData() {
return {
"nodes":[
{"name":"user1","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user2","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"user3","image_url":"http://upload.wikimedia.org/wikipedia/en/thumb/8/80/Wikipedia-logo-v2.svg/103px-Wikipedia-logo-v2.svg.png","fill_color":"blue","text_color":"black"},
{"name":"tag1","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag2","image_url":"","fill_color":"blue","text_color":"black"},
{"name":"tag3","image_url":"","fill_color":"blue","text_color":"black"}
],
"links":[
{"source":"tag1","target":"user1","value":1,"line_color":"green"},
{"source":"tag2","target":"user1","value":1,"line_color":"green"},
{"source":"tag3","target":"user1","value":1,"line_color":"green"},
{"source":"tag1","target":"user2","value":1,"line_color":"green"},
{"source":"tag2","target":"user2","value":1,"line_color":"green"}
]
};
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I've been trying to glue ideas from various d3 examples into what i need, starting with the basic example using miserable.json data and then adding:
use of a key function for the data joins
modifying the underlying graph ala this example
making the links' linkDistance() function depend on an attribute of the graph's links'
adding labels to the nodes ala this example
So far i'm only 3 out of 4: something about using the g elements -- using code taken directly from the Mike's "Labeled Force Layout" example -- breaks things and the nodes aren't drawn. I can make it work if i join circle elements directly, but not if I interpose g elements with attached circles and text elements.
The code below is my best effort at a minimal example. This example works, but does not if I replace the .enter().append("circle") line with the .enter().append("g") lines.
Does anyone know why?
var Width = 200;
var Height = 200;
var Pix2Len = 10;
var color = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("width", Width)
.attr("height", Height);
var force = d3.layout.force()
.size([Width, Height])
var graph = {
"nodes":[
{"name":"Myriel","idx":0},
{"name":"Napoleon","idx":1},
{"name":"Mlle.Baptistine","idx":2},
{"name":"Mme.Magloire","idx":3}
],
"links":[
{"source":1,"target":0,"len":1,"idx":"1-0"},
{"source":2,"target":1,"len":4,"idx":"2-1"},
{"source":2,"target":0,"len":8,"idx":"2-0"},
{"source":3,"target":0,"len":10,"idx":"3-0"},
{"source":3,"target":1,"len":4,"idx":"3-1"},
{"source":3,"target":2,"len":6,"idx":"3-2"}
]
}
console.log("data loaded. nnode="+graph.nodes.length+" nlinks="+graph.links.length);
force
.nodes(graph.nodes)
.links(graph.links)
.size([Width, Height])
.linkDistance(function(link) {
// console.log("link: "+link.source.name+' '+link.target.name+' '+link.idx+' '+link.len)
return link.len * Pix2Len})
.on("tick", tick);
var link = svg.selectAll(".link")
.data(graph.links, function(d) {return d.idx; })
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(graph.nodes, function(d) {return d.idx; })
// THIS WORKS
.enter().append("circle").attr("r", 8).style("fill", function(d) { return color(0); });
// BUT THIS DOES NOT
// modeled after http://bl.ocks.org/mbostock/950642
//
//.enter().append("g")
//.attr("class", "node")
//.attr("cx", function(d) { return d.x; })
//.attr("cy", function(d) { return d.y; });
//
//node.append("circle")
//.attr("r", 10)
//.style("fill", function(d) { return color(0); });
//
//node.append("text")
//.attr("dx", 12)
//.attr("dy", ".35em")
//.text(function(d) { return d.name });
// 1. Begin with graph from JSON data
setTimeout(function() {
start();
}, 0);
// 2. Change graph topology
setTimeout(function() {
var new4 = {"name":"CountessdeLo","idx":4}
var new5 = {"name":"Geborand","idx":5}
graph.nodes.push(new4,new5);
var link40 = {"source":4,"target":0,"len":1,"idx":"4-0"};
var link43 = {"source":4,"target":3,"len":4,"idx":"4-3"};
var link50 = {"source":5,"target":0,"len":1,"idx":"5-0"};
var link52 = {"source":5,"target":2,"len":4,"idx":"5-2"};
graph.links.push(link40,link43,link50,link52);
start();
}, 3000);
//3. Change some link lengths
setTimeout(function() {
// force.links().forEach(function(link) {
graph.links.forEach(function(link) {
if (link.idx == '1-0')
{link.len=10; }
else if (link.idx == '3-0')
{link.len=2; }
else if (link.idx == '5-0')
{link.len=10; };
}); // eo-forEach
start();
}, 6000);
function start() {
link = link.data(force.links(), function(d) { return d.idx; });
link.enter().insert("line", ".node").attr("class", "link");
link.exit().remove();
node = node.data(force.nodes(), function(d) { return d.idx;});
node.enter().append("circle").attr("class", function(d) {
// tried with the <g> version above
// node.enter().append("g").attr("class", function(d) {
console.log('start:'+' '+d.name);
return d.idx; }).attr("r", 5).style("fill", function(d) { return color(1); });
node.exit().remove();
force.start();
}
function tick() {
node.attr("cx", function(d) {
// console.log('tick:'+' '+d.name);
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; });
}
//} // eo-ready()
In your code you are setting cx and cy attributes to the g element. g elements does not support any position attributes like x, y or cx, cy. To move the contents of a g element you will have to use the transform attribute.
Your code
var node = svg.selectAll(".node")
.data(graph.nodes, function(d) {return d.idx; })
.enter().append("g")
.attr("class", "node")
.attr("cx", function(d) { return d.x; }) //will not work
.attr("cy", function(d) { return d.y; }); //will not work
Solution
var node = svg.selectAll(".node")
.data(graph.nodes, function(d) {return d.idx; })
.enter().append("g")
.attr("class", "node");
node.append("circle")
.attr("r", 10)
.style("fill", function(d) { return color(0); });
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name });
Use translate function as below to move group elements.
function tick() {
//Moving <g> elements using transform attribute
node.attr("transform", function(d) { return "translate(" + d.x + "," + 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; });
}
JSFiddle
I am using D3 api for a graph where a couple of nodes are forming from a parent node i want to color the nodes of the whole graph in a manner that each parent node has a fixed color and the child nodes has different color i.e a root node always have red color and the left child is blue and the right one is green and if only one child is there it is green .I am using this api ,,
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.27.1"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?1.27.1"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?1.27.1"></script>
<style type="text/css">
line.link {
stroke: #ccc;
}
circle.node {
fill: #000;
stroke: #fff;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script type="text/javascript">
var w = 960,
h = 500,
r = d3.scale.sqrt().domain([0, 20000]).range([0, 20]);
var force = d3.layout.force()
.gravity(.01)
.charge(-120)
.linkDistance(60)
.size([w, h]);
var svg = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h);
d3.xml("flare.xml", "application/xml", function(xml) {
var nodes = self.nodes = d3.select(xml).selectAll("*")[0],
links = self.links = nodes.slice(1).map(function(d) {
return {source: d, target: d.parentNode};
});
force
.nodes(nodes)
.links(links)
.start();
var link = svg.selectAll("line.link")
.data(links)
.enter().append("svg:line")
.attr("class", "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; });
var node = svg.selectAll("circle.node")
.data(nodes)
.enter().append("svg:circle")
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return r(d.textContent) || 5; })
.call(force.drag);
force.on("tick", function() {
nodes[0].x = w / 2;
nodes[0].y = h / 2;
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>
can anyone help me
SVG has its own CSS properties, one of which being fill. It sets the fill color of an SVG element.
Here is how you use it with D3:
var node = svg.selectAll("circle.node")
.data(nodes)
.enter().append("svg:circle")
.style("fill", function (d) { return '#1f77b4'; })
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return r(d.textContent) || 5; })
.call(force.drag);
Here the color value (#1f77b4) will be the same for all nodes. If you want to color your nodes with a specific algorithm, D3 ships with predefined categorical color scales.
Edit: Here is a related SO question. One of its answers has a good custom ordinal color scale example.
I am trying to implement fisheye distortion on a force directed graph using d3.js. For some reason, the distortion works on the links but not the nodes. This might be caused by the nodes being g elements, but I am not sure. Any help would be appreciated!
var svg, node, link;
function make_graph() {
// Clear out the div first
$("#display_graph svg:first").remove();
// Sets size
var width = 960,
height = 500;
// Maps groups to colors
var color = d3.scale.category20();
// Select the div and append a svg
svg = d3.select("#display_graph").append("svg")
.attr("width", width)
.attr("height", height);
// Pull json from graph
var json_from_db = $('.graph_json').data('json');
var json_nodes = json_from_db.nodes
var json_links = json_from_db.links
// Sets up the force directed graph
var charge_val = $('#charge').val();
var link_distance_val = $('#link_distance').val();
var force = d3.layout.force()
.charge(charge_val)
.linkDistance(link_distance_val)
.size([width, height])
.nodes(json_nodes)
.links(json_links)
.start();
link = svg.selectAll(".link")
.data(json_links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var font_size_px = $('#font_size').val();
node = svg.selectAll(".node")
.data(json_nodes)
.enter().append("g")
.attr("class", "node")
.style("font-size", font_size_px)
.call(force.drag);
var radius = $('#radius').val();
node.append("circle")
.attr("r", radius)
.style("fill", function(d) { return color(d.group); });
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
$('#fisheye_btn').click( function() {
var glyph_color = $(this).css('color');
// original color is white
orig_color = 'rgb(255, 255, 255)';
if (glyph_color == orig_color) {
$(this).css('color', 'orange');
} else {
$(this).css('color', orig_color);
};
});
};
function make_fisheye() {
var fisheye = d3.fisheye.circular()
.radius(200)
.distortion(2);
svg.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
node.each(function(d) { d.fisheye = fisheye(d); })
.attr("cx", function(d) { return d.fisheye.x; })
.attr("cy", function(d) { return d.fisheye.y; })
.attr("r", function(d) { return d.fisheye.z * 4.5; });
link.attr("x1", function(d) { return d.source.fisheye.x; })
.attr("y1", function(d) { return d.source.fisheye.y; })
.attr("x2", function(d) { return d.target.fisheye.x; })
.attr("y2", function(d) { return d.target.fisheye.y; });
});
}
Eventually, I want the user to be able to turn the distortion on or off by clicking a button. Thanks for your help!
I updated the code. I can turn the distortion on or off with a button click now.
// Make force directed graph on button click
$(document).ready( function() {
$("#refresh_btn").click( function() {
make_graph();
});
});
// Turn on and off fisheye distortion
$(document).ready( function() {
$('#fisheye_btn').click( function() {
var glyph_color = $(this).css('color');
// original color is white
orig_color = 'rgb(255, 255, 255)';
if (glyph_color == orig_color) {
$(this).css('color', 'orange');
make_graph();
make_fisheye();
} else {
$(this).css('color', orig_color);
make_graph();
};
});
});
var svg, node, link;
function make_graph() {
// Clear out the div first
$("#display_graph svg:first").remove();
// Sets size
var width = 960,
height = 500;
// Maps groups to colors
var color = d3.scale.category20();
// Select the div and append a svg
svg = d3.select("#display_graph").append("svg")
.attr("width", width)
.attr("height", height);
// Pull json from graph
var json_from_db = $('.graph_json').data('json');
var json_nodes = json_from_db.nodes
var json_links = json_from_db.links
// Sets up the force directed graph
var charge_val = $('#charge').val();
var link_distance_val = $('#link_distance').val();
var force = d3.layout.force()
.charge(charge_val)
.linkDistance(link_distance_val)
.size([width, height])
.nodes(json_nodes)
.links(json_links)
.start();
link = svg.selectAll(".link")
.data(json_links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var font_size_px = $('#font_size').val();
node = svg.selectAll(".node")
.data(json_nodes)
.enter().append("g")
.attr("class", "node")
.style("font-size", font_size_px)
.call(force.drag);
var radius = $('#radius').val();
node.append("circle")
.attr("r", radius)
.style("fill", function(d) { return color(d.group); });
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.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("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
};
function make_fisheye() {
var fisheye = d3.fisheye.circular()
.radius(200)
.distortion(2);
svg.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
node.each(function(d) { d.fisheye = fisheye(d); })
.attr("cx", function(d) { return d.fisheye.x; })
.attr("cy", function(d) { return d.fisheye.y; })
.attr("r", function(d) { return d.fisheye.z * 4.5; });
link.attr("x1", function(d) { return d.source.fisheye.x; })
.attr("y1", function(d) { return d.source.fisheye.y; })
.attr("x2", function(d) { return d.target.fisheye.x; })
.attr("y2", function(d) { return d.target.fisheye.y; });
});
}
Unfortunately, I am still having issues with just the links being distorted. The nodes are actually g elements, which I am becoming more and more convinced might be the problem. Is there a way to select elements inside the g element using d3.js? I am surprised that the fisheye distortion is not just passed on to the elements inside the g element. Maybe my guess is wrong?
New Code
Now both the nodes and links are distorted on a button click! Here is the code:
// Global variables
var svg, node, link;
var fisheye_on = false;
// Make force directed graph on button click
$(document).ready( function() {
$("#refresh_btn").click( function() {
// Make sure fisheye is turned off
$('#fisheye_btn').css('color', 'white');
fisheye_on = false;
make_graph();
});
});
// Turn on and off fisheye distortion
$(document).ready( function() {
$('#fisheye_btn').click( function() {
var glyph_color = $(this).css('color');
// original color is white
orig_color = 'rgb(255, 255, 255)';
if (glyph_color == orig_color) {
$(this).css('color', 'orange');
fisheye_on = true;
make_graph();
} else {
$(this).css('color', orig_color);
fisheye_on = false;
make_graph();
};
});
});
function make_graph() {
// Clear out the div first
$("#display_graph svg:first").remove();
// Sets size
var width = 960,
height = 500;
// Maps groups to colors
var color = d3.scale.category20();
// Select the div and append a svg
svg = d3.select("#display_graph").append("svg")
.attr("width", width)
.attr("height", height);
// Pull json from graph
var json_from_db = $('.graph_json').data('json');
var json_nodes = json_from_db.nodes
var json_links = json_from_db.links
// Sets up the force directed graph
var charge_val = $('#charge').val();
var link_distance_val = $('#link_distance').val();
var force = d3.layout.force()
.charge(charge_val)
.linkDistance(link_distance_val)
.size([width, height])
.nodes(json_nodes)
.links(json_links)
.start();
link = svg.selectAll(".link")
.data(json_links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var font_size_px = $('#font_size').val();
node = svg.selectAll(".node")
.data(json_nodes)
.enter().append("g")
.attr("class", "node")
.style("font-size", font_size_px)
.call(force.drag);
var radius = $('#radius').val();
node.append("circle")
.attr("r", radius)
.style("fill", function(d) { return color(d.group); });
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
if (fisheye_on == false) {
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 + ")"; });
});
} else {
var fisheye = d3.fisheye.circular()
.radius(200)
.distortion(2);
svg.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
node.each(function(d) { d.fisheye = fisheye(d); });
node.selectAll("circle")
.attr("cx", function(d) { return d.fisheye.x - d.x; })
.attr("cy", function(d) { return d.fisheye.y - d.y; })
.attr("r", function(d) { return d.fisheye.z * 4.5; });
node.selectAll("text")
.attr("dx", function(d) { return d.fisheye.x - d.x; })
.attr("dy", function(d) { return d.fisheye.y - d.y; });
link.attr("x1", function(d) { return d.source.fisheye.x; })
.attr("y1", function(d) { return d.source.fisheye.y; })
.attr("x2", function(d) { return d.target.fisheye.x; })
.attr("y2", function(d) { return d.target.fisheye.y; });
});
};
};
Unfortunately, I have come across another issue. There seems to be an initial lag when the distortion is applied. The nodes get distorted first and then the links get distorted. Not sure what is causing this. Any hints?
Public JSFiddle
I managed to replicated the issue in a public JSFiddle:
http://jsfiddle.net/cspears2002/vVL99/
In order to make the graph, just click "Make Graph". Clicking "Fisheye" will turn the fisheye distortion on and off. The issue is that the nodes are distorted first and then the links follow. I want nodes and links to be distorted at the same time. Any help would be appreciated!
I still not understanding why the code bellow does not display its labels / text...
I have defined the css and set the attribute like the title when the move is over the node:
Json:
{
"nodes":[
{"name":"t1","group":1},
{"name":"t2","group":1},
{"name":"t3","group":1},
{"name":"t4","group":1},
{"name":"hate","group":2},
{"name":"good","group":2},
{"name":"aiport","group":3},
{"name":"flight","group":3}
],
"links":[
{"source":0,"target":4,"value":4},
{"source":0,"target":5,"value":4},
{"source":1,"target":4,"value":4},
{"source":2,"target":5,"value":4},
{"source":3,"target":5,"value":4},
{"source":4,"target":6,"value":4},
{"source":5,"target":6,"value":4},
{"source":5,"target":7,"value":4}
]
}
Code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node {
fill: #555;
stroke: #999;
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 = 1024,
height = 768;
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("data.json", function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll("line.link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll("circle.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; });
node.append("text")
.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>
You are adding the text element inside the circle element - try running your code and have a look at the svg with the DOM inspector. I'm not sure text is allowed there. Instead add the text elements separately - like another rendering of the nodes:
var texts = svg.selectAll("text.label")
.data(graph.nodes)
.enter().append("text")
.attr("class", "label")
.attr("fill", "black")
.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; });
texts.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
Another option would be to add both circle and text elements inside a g container element as shown below:
var container = svg.selectAll("g.node").data(graph.nodes).enter().append("g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
}).call(force.drag);
container.append("circle")
.attr("r", 5)
.style("fill", color);
container.append("text")
.style("text-anchor", "middle")
.text(function (d) {
return d.name;
});
Here you can play with a working jsfiddle:
http://jsfiddle.net/vfu78/16/