D3 force layout create strange line and black rectangle when dragging - javascript

I have a force graph with simple nodes and link. Each node is a svg file 500x500.
I implemented zoom and dragging.
When clicked on a node, that node
become fixed while other is released
The problem is there are weird popping line and black shape if I drag a node and then panning around.
I do not know how to put it in words so I record this video. I used Firefox:
https://drive.google.com/file/d/0B2W405T4XZ7ZZDlLRkc0RE01NG8/view?usp=sharing
Edit: I added the json
{
"nodes":[
{"name":"Mohamed","type":"CEO"},
{"name":"Google.Inc","type":"Company"},
{"name":"Apple","type":"Company"},
{"name":"Leonardo Decaprio","type":"CEO"},
{"name":"Prisoner A","type":"Prisoner"},
{"name":"National Bank of Hakuna","type":"Bank"},
{"name":"Shanghai Major Bank","type":"Bank"},
{"name":"Boeing 747","type":"Boat"},
{"name":"Company Ltd.","type":"Company"},
{"name":"Shanghai Major Bank","type":"Bank"},
{"name":"Boeing 747","type":"Boat"}
],
"links":[
{"source":0,"target":1,"value":1},
{"source":1,"target":3,"value":1},
{"source":2,"target":0,"value":8},
{"source":3,"target":0,"value":10},
{"source":3,"target":2,"value":6},
{"source":3,"target":4,"value":1},
{"source":5,"target":0,"value":1},
{"source":6,"target":0,"value":1},
{"source":7,"target":0,"value":1},
{"source":8,"target":0,"value":2},
{"source":3,"target":1,"value":1}
]}
Edit: I added the source code as per requested.
var force = d3.layout.force()
.charge(-1000)
.linkDistance(80)
.gravity(.05)
.size([width, height]);
var drag = force.drag()
.origin(function(d) { return d; })
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var zoom = d3.behavior.zoom()
.scaleExtent([0.1, 10])
.on("zoom", zoomed);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom);
var container = svg.append("g");
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
}
function dragged(d) {
d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
d3.json("pst.json", function(error, graph) {
if (error) throw error;
root=graph.nodes[0];
root.x = width / 2;
root.y = height / 2;
root.fixed = true;
var link = container.append("g").selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = container.append("g").selectAll(".node")
.data(graph.nodes)
.enter()
.append("image")
.attr("xlink:href",function(d){return d.type+ ".svg" ;})
.attr("class", "node")
.attr("x", function(d) { return d.x+12; })
.attr("y", function(d) { return d.y+12; })
.attr("width", "24px")
.attr("height", "24px")
.call(force.drag)
.on("click",click);
force
.nodes(graph.nodes)
.links(graph.links)
.start();
node.append("title")
.text(function(d) { return d.name; });
function click(node) {
root.fixed = false;
root=node;
root.fixed=true;
}
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("x", function(d) { return d.x-12; })
.attr("y", function(d) { return d.y-12; });
});
});

Related

D3.js: How to highlight connected edges and nodes in a Force-Directed Graph?

I'm trying to build a Force-Directed Graph with Link Highlighting.
Initially the graph will look like the following:
But after hovering on a particular node I want it to become like the following (only connected nodes and edges will be highlighted):
I've tried the following code. But the highlighting part is not working.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("miserables.json", function (error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter()
.append("line")
.attr("stroke-width", function (d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(graph.nodes)
.enter()
.append("g")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
var circles = node.append("circle")
.attr("r", 10)
.attr("fill", function (d) { return color(d.group); });
var lables = node.append("text")
.text(function (d) {
return d.id;
})
.attr("x", 0)
.attr("dy", ".35em")
.attr("text-anchor", "middle");
node.append("title")
.text(function (d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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 dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
linkedByIndex[d.target.index + "," + d.source.index] = 1;
});
function neighboring(a, b) {
return a.index == b.index || linkedByIndex[a.index + "," + b.index];
}
function mouseover(d) {
d3.selectAll("link").transition().duration(500)
.style("opacity", function (o) {
return o.source === d || o.target === d ? 1 : 0;
});
d3.selectAll("node").transition().duration(500)
.style("opacity", function (o) {
return neighboring(d, o) ? 1 : 0;
});
}
function mouseout() {
d3.selectAll("link").transition().duration(500)
.style("opacity", 1);
d3.selectAll("node").transition().duration(500)
.style("opacity", 1);
}
The full code can be found at https://plnkr.co/edit/BA9OfjVQjOwyHDkh
Could you please help me find the issue here and make it work?

Bound nodes of a force directed graph on a certain div using d3.js

I have created a force directed graph using d3.js. When new nodes are inserted to the graph, the graph starts moving on the white area as shown in this image. I would like to pinpoint the nodes (incoming and "old) dynamically in order to avoid problems of "cutting" some nodes of the viewBox, as shown in the image attached.
Any help?
My code so far is:
var w = 550;//1200,
h = 400;//750;
var zoom = d3.behavior.zoom()
.scaleExtent([0.1, 10])
.on("zoom", zoomed);
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var testnode=[];
var color = d3.scale.category20();
var scale = 1200;
var vis = d3.select("#force")
.append("svg")
.attr("width", w) //w
.attr("height", h) //h
.attr("id", "svg")
.attr("group", "svg")
.attr("pointer-events", "all")
.attr("viewBox", "0 0 " + w + " " + h)
.call(zoom)
.attr("perserveAspectRatio", "xMinYMid meet")
.append('svg:g');
var force = d3.layout.force();
var nodes = force.nodes(),
links = force.links();
var update = function () {
var packetsReceivedArray = []
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 1e-6);
var link = vis.selectAll("line")
.data(links, function (d) {
return d.source.id + "-" + d.target.id;
});
var scale = d3.scale.linear()
.domain(d3.extent(links, function(d, i) {
return d.value;
}))
.range([1, 5]);
var scaleNode = d3.scale.linear()
.domain(d3.extent(nodes, function(d, i) {
return d.packetsReceived;
}))
.range([4, 16]);
link.enter().append("line")
.attr("id", function (d) {
return d.source.id + "-" + d.target.id;
})
.style("stroke-width", function(d) {
return scale(d.value) + "px";
})
.attr("class", "link");
link.append("title")
.text(function (d) {
return d.value;
});
link.exit().remove();
var node = vis.selectAll("g.node")
.data(nodes, function (d) {
return d.id;
});
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.call(force.drag);
nodeEnter.append("svg:circle")
.attr("x", 10)
.attr("y", ".31em")
.attr("class", "nodeStrokeClass")
.attr("data-legend",function(d) {
if(d.group == 0){
return 'Basic Node'
}
return 'Other nodes'
});
nodeEnter.append("svg:text")
.attr("class", "textClass")
.attr("x", 14)
.attr("y", ".31em")
legend = vis.append("g")
.attr("class","legend")
.attr("transform","translate(900,30)")
.style("font-size","14px")
.call(d3.legend)
node.exit().remove();
force.on("tick", function () {
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;
});
});
force
.gravity(.1)
.charge(-300)
.friction(0.15)
.linkDistance(60)
.size([w - 150, h - 150])
.start();
};

How to display source nodes of a clicked target?

How can I display the source nodes names of a clicked target node in a forced directed graph using D3?
The snippet below is from some example code for directed graphs by Mike Bostock. How can I go about modifying this base code so when a node is clicked on the graph, the target values of that node is displayed on the screen (in this case I would like to display the name attributes)?
For example the 0-th element in "nodes" is:
"nodes":[
{"name":"Myriel","group":1},
...
]
And in "links" the targets are define like so:
"links":[
{"source":1,"target":0,"value":1}, // Napoleon
{"source":2,"target":0,"value":8}, // Mlle.Baptistine
{"source":3,"target":0,"value":10}, // Mme.Magloire
{"source":3,"target":2,"value":6},
{"source":4,"target":0,"value":1}, // CountessdeLo
{"source":5,"target":0,"value":1}, // Geborand
{"source":6,"target":0,"value":1}, // Champtercier
{"source":7,"target":0,"value":1}, // Cravatte
{"source":8,"target":0,"value":2}, // Count
{"source":9,"target":0,"value":1}, // OldMan
...
{"source":11,"target":0,"value":5}, // Valjean
...
]
Then clicking on the node Myriel would display:
Napoleon,Mlle.Baptistine,Mme.Magloire,CountessdeLo,Geborand,Champtercier,Cravatte,Count,OldMan,Valjean
Myriel is located here in the graph:
Below is the JavaScript code:
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
d3.json("https://gist.githubusercontent.com/mbostock/4062045/raw/9653f99dbf6050b0f28ceafbba659ac5e1e66fbd/miserables.json", function(error, graph) {
if (error) throw error;
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)
.on("click",function(d){
var targets = graph.links.filter(function(i){
return i.target.name == d.name
});
tip.show( targets.map(function(i){ return i.source.name;}) );
});
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; });
});
});
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
d3.json("https://gist.githubusercontent.com/mbostock/4062045/raw/9653f99dbf6050b0f28ceafbba659ac5e1e66fbd/miserables.json", function(error, graph) {
if (error) throw error;
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)
.on("click",function(d){
var targets = graph.links.filter(function(i){
return i.target.name == d.name
});
tip.show( targets.map(function(i){ return i.source.name;}) );
});
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; });
});
});
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.0/d3.min.js"></script>
Add a click event on each node, then inside we can get all the targets by filtering the graph.links array such that we only have the elements who's target.name is the same as the clicked nodes name d.name. Once we have that you can use .map() to return the array with .source.name to give the name of those items inside targets:
.on("click",function(d) {
var targets = graph.links.filter(function(i){
return i.target.name==d.name;
});
tip.show( targets.map(function(i){ return i.source.name; }) );
});
var tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d; });
var width = 960,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.call(tip);
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
d3.json("https://gist.githubusercontent.com/mbostock/4062045/raw/9653f99dbf6050b0f28ceafbba659ac5e1e66fbd/miserables.json", function(error, graph) {
if (error) throw error;
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)
.on("click",function(d){
var targets = graph.links.filter(function(i){
return i.target.name == d.name
});
tip.show( targets.map(function(i){ return i.source.name;}) );
});
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; });
});
});
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-tip/0.6.7/d3-tip.min.js"></script>
Now for easy display of those values the d3-tip library can be used. This would be initialized for the graph like so:
var tip = d3.tip().attr('class', 'd3-tip').html(function(d) { return d; });
...
var svg = ..
svg.call(tip);
And finally the tip.show(...) function in the first code snippet will display those items on the graph.

D3.js network graph using force-directed layout and rectangles for nodes

I'm trying to modify Mike's Force-Directed Graph example to use rectangles instead of circles as nodes. Also, I want text inside the rectangle.
I have rectangles showing up with text correctly, however they're not attached to the links, and they do not move.
Here's a codepen link: http://codepen.io/anon/pen/gpgWaz
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);
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("g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.call(force.drag);
node.append("rect")
.attr("class", "node")
.attr("width", 100)
.attr("height", 35)
.style("fill", function(d) {
return color(d.group);
})
.style("stroke", "black")
.style("stroke-width", "1px");
node.append("text")
.text(function(d) {
return d.name;
})
.style("font-size", "12px")
.attr("dy", "1em");
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("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
Update
Thanks to Lars's comment, and his codepen it works now.
Updates I made to my code:
Add transform illustrated by Lars
Changed links to connect at the center of rectangles
Added rounded corners to rectangles
Gave the text a slight margin indentation
Changed to use window width/height
Here my new codepen: http://codepen.io/anon/pen/bdgREd
var width = window.innerWidth,
height = window.innerHeight,
nodeWidth = 100,
nodeHeight = 35;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-1500)
.linkDistance(100)
.friction(0.5)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
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("g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
.call(force.drag);
node.append("rect")
.attr("class", "node")
.attr("width", nodeWidth)
.attr("height", nodeHeight)
.attr("rx", 5)
.attr("ry", 5)
.style("fill", function(d) {
return color(d.group);
})
.style("stroke", "black")
.style("stroke-width", "1px");
node.append("text")
.attr("x", 5)
.attr("y", 2)
.text(function(d) {
return d.name;
})
.style("font-size", "12px")
.attr("dy", "1em");
node.append("title")
.text(function(d) {
return d.name;
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x + (nodeWidth / 2);
})
.attr("y1", function(d) {
return d.source.y + (nodeHeight / 2);
})
.attr("x2", function(d) {
return d.target.x + (nodeWidth / 2);
})
.attr("y2", function(d) {
return d.target.y + (nodeHeight / 2);
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
You're setting the x and y attributes on the g elements to change their positions -- this won't do anything. You need to set the transform attribute instead, like you're doing when adding the g elements. So your tick handler function would contain
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
instead of
node.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
Complete demo here.

d3.js force layout increase linkDistance

How to increase linkDistance without affecting the node alignment,
example: http://mbostock.github.com/d3/talk/20110921/force.html
when I try to increase the circle radius and linkDistance
the it collapse
<script type="text/javascript">
var w = 1280,
h = 800,
z = d3.scale.category20c();
var force = d3.layout.force()
.size([w, h]);
var svg = d3.select("#chart").append("svg:svg")
.attr("width", w)
.attr("height", h);
svg.append("svg:rect")
.attr("width", w)
.attr("height", h);
d3.json("flare.json", function(root) {
var nodes = flatten(root),
links = d3.layout.tree().links(nodes);
force
.nodes(nodes)
.links(links)
.start();
var link = svg.selectAll("line")
.data(links)
.enter().insert("svg:line")
.style("stroke", "#999")
.style("stroke-width", "1px");
var node = svg.selectAll("circle.node")
.data(nodes)
.enter().append("svg:circle")
.attr("r", 4.5)
.style("fill", function(d) { return z(d.parent && d.parent.name); })
.style("stroke", "#000")
.call(force.drag);
force.on("tick", function(e) {
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 flatten(root) {
var nodes = [];
function recurse(node, depth) {
if (node.children) {
node.children.forEach(function(child) {
child.parent = node;
recurse(child, depth + 1);
});
}
node.depth = depth;
nodes.push(node);
}
recurse(root, 1);
return nodes;
}
</script>
Play around with the .charge parameter. It defines how much the nodes repel each other.

Categories

Resources