Reputation: 175
I'm trying to create a force-directed layout d3 viz, and while the nodes are rendering fine I'm struggling with the link connections, which don't seem to register at all.
I've done some digging around but I'm struggling to resolve this, can anyone help?
Codepen - https://codepen.io/quirkules/pen/dqXRwj
var links_data = [{"source":"JRASERVER","target":"BAM","count":4},{"source":"JRASERVER","target":"BON","count":2},{"source":"JRASERVER","target":"BSERV","count":4},{"source":"JRASERVER","target":"CLOUD","count":3},{"source":"JRASERVER","target":"CONFCLOUD","count":1},{"source":"JRASERVER","target":"CONFSERVER","count":31},{"source":"JRASERVER","target":"CWD","count":13},{"source":"JRASERVER","target":"FE","count":7},{"source":"JRASERVER","target":"HCPUB","count":1},{"source":"JRASERVER","target":"ID","count":1},{"source":"JRASERVER","target":"JPOSERVER","count":2},{"source":"JRASERVER","target":"JRACLOUD","count":41},{"source":"JRASERVER","target":"JSDCLOUD","count":2},{"source":"JRASERVER","target":"JSDSERVER","count":23},{"source":"JRASERVER","target":"JSWCLOUD","count":3},{"source":"JRASERVER","target":"JSWSERVER","count":40},{"source":"JRASERVER","target":"TRANS","count":2}];
var nodes_data = [{"name":"JRASERVER","total":4}, {"name":"BAM","total":4},{"name":"BON","total":2},{"name":"BSERV","total":4},{"name":"CLOUD","total":3},{"name":"CONFCLOUD","total":1},{"name":"CONFSERVER","total":31},{"name":"CWD","total":13},{"name":"FE","total":7},{"name":"HCPUB","total":1},{"name":"ID","total":1},{"name":"JPOSERVER","total":2},{"name":"JRACLOUD","total":41},{"name":"JSDCLOUD","total":2},{"name":"JSDSERVER","total":23},{"name":"JSWCLOUD","total":3},{"name":"JSWSERVER","total":40},{"name":"TRANS","total":2}];
//create somewhere to put the force directed graph
var height = 650,
width = 950;
var svg = d3.select("body").append("svg")
.attr('width',width)
.attr('height',height);
var radius = 15;
//set up the simulation and add forces
var simulation = d3.forceSimulation()
.nodes(nodes_data);
var link_force = d3.forceLink()
.id(function(d) { return d.name; })
;
var charge_force = d3.forceManyBody()
.strength(-100);
var center_force = d3.forceCenter(width / 2, height / 2);
simulation
.force("charge_force", charge_force)
.force("center_force", center_force)
.force("link",link_force)
;
//add tick instructions:
simulation.on("tick", tickActions );
//add encompassing group for the zoom
var g = svg.append("g")
.attr("class", "everything");
//draw circles for the nodes
var node = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes_data)
.enter()
.append("circle")
.attr("r", radius)
.attr("fill", "#FFFFFF")
.on("mouseover", mouseOver(.1))
.on("mouseout", mouseOut);
// add the curved links to our graphic
var link = g.selectAll(".link")
.data(links_data)
.enter()
.append("path")
.attr("class", "link")
.style('stroke', "#FF00FF")
.attr('stroke-width', 2);
//add drag capabilities
var drag_handler = d3.drag()
.on("start", drag_start)
.on("drag", drag_drag)
.on("end", drag_end);
drag_handler(node);
//add zoom capabilities
var zoom_handler = d3.zoom()
.on("zoom", zoom_actions);
zoom_handler(svg);
/** Functions **/
//Drag functions
//d is the node
function drag_start(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
//make sure you can't drag the circle outside the box
function drag_drag(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function drag_end(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
//Zoom functions
function zoom_actions(){
g.attr("transform", d3.event.transform)
}
function tickActions() {
//update circle positions each tick of the simulation
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
//update link positions
link.attr("d", positionLink);
}
// links are drawn as curved paths between nodes,
// through the intermediate nodes
function positionLink(d) {
var offset = 30;
var midpoint_x = (d.source.x + d.target.x) / 2;
var midpoint_y = (d.source.y + d.target.y) / 2;
var dx = (d.target.x - d.source.x);
var dy = (d.target.y - d.source.y);
var normalise = Math.sqrt((dx * dx) + (dy * dy));
var offSetX = midpoint_x + offset * (dy / normalise);
var offSetY = midpoint_y - offset * (dx / normalise);
return "M" + d.source.x + "," + d.source.y +
"S" + offSetX + "," + offSetY +
" " + d.target.x + "," + d.target.y;
}
// build a dictionary of nodes that are linked
var linkedByIndex = {};
links_data.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
// check the dictionary to see if nodes are linked
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
// fade nodes on hover
function mouseOver(opacity) {
return function(d) {
// check all other nodes to see if they're connected
// to this one. if so, keep the opacity at 1, otherwise
// fade
node.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
return thisOpacity;
});
node.style("fill-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
return thisOpacity;
});
// also style link accordingly
link.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
link.style("stroke", function(o) {
return o.source === d || o.target === d ? "#FF0000" : "#ddd";
});
};
}
function mouseOut() {
node.style("stroke-opacity", 1);
node.style("fill-opacity", 1);
link.style("stroke-opacity", 1);
link.style("stroke", "#00FF00");
}
Upvotes: 0
Views: 1072
Reputation: 28633
You have to supply the link records to the link force.
var link_force = d3.forceLink(links_data)
.id(function(d) { return d.name; })
;
Have a look at your CSS
.link {
fill: #FFFFFF00;
}
Better is
.link {
fill: none;
}
Upvotes: 1