Grouping SVG circles using d3.js - javascript

I have this data :
[{"node":"A","group":"1","type":"node"},
{"node":"B","group":"2","type":"node"},
{"node":"D","group":"1","type":"node"},
{"node":"C","group":"2","type":"node"},
{"type":"link","interest":"1","source":"A","target":"B"},
{"type":"link","interest":"2","source":"A","target":"C"},
{"type":"link","interest":"10","source":"B","target":"C"},
{"type":"link","interest":"3","source":"D","target":"B"}]
Here is my code:
var force = d3.layout.force()
.nodes(data)
.size([+width(), +height()])
.gravity(.08)
.charge(-10)
.on("tick", tick)
.on("end", function(){
chart.dispatchEndDrawing()
})
.start();
var g = selection
.attr("width", width)
.attr("height", height);
var link = g.selectAll("line")
.data(data.filter(function (d){ return d.type == "link"; }))
.enter().append("line")
.style("stroke-width", function(d) { return d.interest; })
.style("stroke", "grey")
.call(force.drag);
var node = g.selectAll("circle")
.data(data.filter(function (d){ return d.type == "node"; }))
.enter().append("circle")
.attr("r", 5)
.style("fill", "blue")
.call(force.drag);
function tick(e) {
node
.attr("cx", function(d) { return d.x = Math.max(radius(), Math.min(width() - radius(), d.x)); })
.attr("cy", function(d) { return d.y = Math.max(radius(), Math.min(height()-10 - radius(), d.y)); });
link
.attr("x1", function(d) { return d3.selectAll('circle').filter(function (k) { return d.source === k.node; }).attr('cx');})
.attr("y1", function(d) { return d3.selectAll('circle').filter(function (k) { return d.source === k.node; }).attr('cy'); })
.attr("x2", function(d) { return d3.selectAll('circle').filter(function (k) { return d.target === k.node; }).attr('cx'); })
.attr("y2", function(d) { return d3.selectAll('circle').filter(function (k) { return d.target === k.node; }).attr('cy'); });
chart.dispatchStartDrawing()
}
})
I want to display 4 circles (A, B, C, D) in separate groups:
Group 1 - (A,D) | Group 2 - (B,C).
The grouping should be automatic by using the attribute "group".
The simulation is done by creating a d3 force layout object

Related

stacked nodes in directed graph d3js

I've a problem with my directed graph in d3js,in a few words:
if number of nodes < 24 the graph is rendered correctly
if number of nodes >= 24 everything was stacked on top left of svg but if I inspect my html code I see all nodes, links and labels...
this is my code:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var simulation = d3.forceSimulation()
.force("x", d3.forceX().strength(8).x( function(d){ return yScale(d.type) }))
.force("y", d3.forceY().strength(10).y(height/2))
.force("center", d3.forceCenter().x(width / 2).y(height / 2)) // Attraction to the center of the svg area
.force("charge", d3.forceManyBody().strength(1)) // Nodes are attracted one each other of value is > 0
.force("collide", d3.forceCollide().strength(0.9).radius(50).iterations(10)) // Force that avoids circle overlapping
.force("link", d3.forceLink().distance(function(d){return d.link_distance}).strength(1))
var yScale = d3.scalePoint()
.domain([1, 2, 3])
.range([150, width-150])
.padding(0.6)
.round(false);
d3.json("{{=URL('professionista', 'get_current_plan_graph', extension=False)}}", function(error, graph) {
if (error) throw error;
var links = graph.links;
function getNeighbors(node) {
return links.reduce(function(neighbors, link) {
if (link.target.id === node.id) {
neighbors.push(link.source.id)
} else if (link.source.id === node.id) {
neighbors.push(link.target.id)
}
return neighbors
}, [node.id])
}
function isNeighborLink(node, link) {
return link.target.id === node.id || link.source.id === node.id
}
function getNodeColor(node, neighbors) {
if (Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1) {
if (node.type === 1)
return "#3C99FB"
else if (node.type === 2)
return "#9EC2E2"
else if (node.type === 3)
return "#577EA7"
else
return '#dee2e6'
}
return '#dee2e6'
}
function getLinkColor(node, link) {
return isNeighborLink(node, link) ? '#000444' : '#E5E5E5'
}
function getTextColor(node, neighbors) {
return Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1 ? 'green' : 'black'
}
function selectNode(selectedNode) {
var neighbors = getNeighbors(selectedNode)
nodeElement.attr('fill', function(node) {
return getNodeColor(node, neighbors)
})
labelElement.attr('fill', function(node) {
return getTextColor(node, neighbors)
})
linkElement.attr('stroke', function(link) {
return getLinkColor(selectedNode, link)
})
}
var linkElement = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", 1)
.attr("stroke", "rgba(50, 50, 50, 0.2)");
var nodeElement = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter()
.append("circle")
.attr("r", 30)
.attr("fill", getNodeColor)
.on('click', selectNode)
var labelElement = svg.append("g")
.attr("class", "labels")
.selectAll("text")
.data(graph.nodes)
.enter().append("text")
.text(function(d) {
return d.codice;
})
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
linkElement
.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; });
nodeElement
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
labelElement
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
}
});
what's wrong with this code?
This happen also if I disable all links and labels, the nodes goes to the corner, please help me!!!

How to wrap text in force layout d3 with long labels?

I am trying to wrap text which is parsed from a json response into my application.
I have two svg elements : rect and text. I want to find a way where I can wrap my svg text such that it fits in the rectangle.
( Picture Force layout with text labels and rectangles )
This is the link to the original project in which I have made modifications
Visualizing Reddit Discussions
setupGraph();
function setupGraph() {
$(".network").empty();
names = {};
nodecolor = {};
force = d3.layout.force()
.charge(-500)
.linkDistance(20)
.size([width, height]);
nodes = force.nodes(),
links = force.links();
force.on("tick", function() {
svg.selectAll("line.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;
});
svg.selectAll("rect.node")
.attr("x", function(d) {
return d.x - d.curWidth / 2;
})
.attr("y", function(d) {
return d.y - d.curHeight / 2;
});
svg.selectAll("text.node")
.attr("x", function(d) {
return d.x - d.curWidth / 2 + 8;
})
.attr("y", function(d) {
return d.y - d.curHeight / 2 + 20;
});
});
d3.select("svg").remove();
svg = d3.select("#chart").append("svg:svg")
.attr("width", width)
.attr("height", height)
.attr("class", "network");
}
function updateNetwork() {
var link = svg.selectAll("line.link")
.data(links, function(d) {
return d.source.id + "-" + d.target.id;
});
link.enter().insert("svg:line", "text.node", "rect.node")
.attr("class", "link")
.style("stroke-width", function(d) {
return 2;
})
.style("stroke", "gray")
.style("opacity", 0.1);
var node = svg.selectAll("rect.node")
.data(nodes, function(d) {
return d.id;
});
var node = svg.selectAll("text.node")
.data(nodes, function(d) {
return d.id;
});
var nodeEnter = node.enter().append("svg:rect")
.attr("class", "node")
.call(force.drag)
.attr("width", function(d) {
return d.curWidth;
})
.attr("height", function(d) {
return d.curHeight;
})
.style("opacity", 1.0)
.on("mouseover", displayTooltip)
.on("mousemove", moveTooltip)
.on("mouseout", removeTooltip)
.on("mouseover", function(d) {
d3.select(this).transition().attr("height", 100).attr("width", 100); //.style("fill", "red");
})
.call(force.drag)
var nodeEnterr = node.enter().append("svg:text")
.attr("class", "node")
.text(function(d) {
return d.name + ": " + d.body
})
.call(force.drag)
.style("opacity", 1.0)
.on("mouseover", displayTooltip)
.on("mousemove", moveTooltip)
.on("mouseout", removeTooltip)
.call(force.drag)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Connect SVG circles using Javascript

I am trying to connect SVG circles and rectangles by drawing lines between source circles and target rectangles. Here is the format of my json file:
[{"sourceNode":"1","type":"sourceNode"},
{"sourceNode":"3","type":"sourceNode"},
{"sourceNode":"8","type":"sourceNode"},
.....
{"targetNode":"1","type":"targetNode"},
{"targetNode":"7","type":"targetNode"},
{"targetNode":"1","type":"targetNode"},
.....
{"type":"link","source":"1","target":"2"},
{"type":"link","source":"3","target":"4"},
{"type":"link","source":"3","target":"5"}]
I am using a tick function to give attributes to circles and the line. The circles work just fine, but I don't get lines with no attributes when I inspect my SVG in html.
Here is the code :
var nodeSource = g.selectAll("circle")
.data(data.filter(function (d){ return d.type == "sourceNode"; }))
.enter().append("circle")
.attr("r", 5)
.style("fill", "blue")
.call(force.drag);
var nodeTarget = g.selectAll("rect")
.data(data.filter(function (d){ return d.type == "targetNode"; }))
.enter().append("rect")
.attr("width", 10)
.attr("height", 10)
.style("fill", "green")
.call(force.drag);
var link = g.selectAll("line")
.data(data.filter(function (d){ return d.type == "link"; }))
.enter().append("line")
.style("stroke-width", "2")
.style("stroke", "grey")
.call(force.drag);
function tick(e) {
nodeSource
.attr("cx", function(d) { return d.x = Math.max(radius(), Math.min(width() - radius(), d.x)); })
.attr("cy", function(d) { return d.y = Math.max(radius(), Math.min(height() - radius(), d.y)); });
nodeTarget
.attr("x", function(d) { return d.x = Math.max(radius(), Math.min(width() - radius(), d.x)); })
.attr("y", function(d) { return d.y = Math.max(radius(), Math.min(height() - radius(), 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; });
chart.draw()
}
When you're assigning x1, x2, etc. coordinates to your line, you're not actually giving them a value. In your json file, the data for links has:
"source": "1"
for example. Using d.source.anything won't return a value because "1" doesn't have any properties attached to it. If you want to get a reference to the node which has this number, you have to use d3 to find it:
line.attr('x1', function (d) {
return d3.selectAll('circle').filter(function (k) {
return d.source === k.sourceNode;
}).attr('cx');
})
Then, when you want to do the target nodes:
line.attr('x2', function(d) {
return d3.selectAll('rect').filter(function (k) {
return d.target === k.targetNode;
}).attr('x');
})

Static Force Directed Graph links not correct length [duplicate]

This question already has answers here:
d3.js linkStrength influence on linkDistance in a force graph
(2 answers)
Closed 2 years ago.
I am trying to create a static force directed graph. One that loads without any animation in. Here's what I'm trying to emulate: http://bl.ocks.org/mbostock/1667139
I have the following D3 graph:
var width = $("#theVizness").width(),
height = $("#theVizness").height();
var color = d3.scale.ordinal().range(["#ff0000", "#fff000", "#ff4900"]);
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("#theVizness").append("svg")
.attr("width", width)
.attr("height", height);
var loading = svg.append("text")
.attr("class", "loading")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Loading...");
d3.json("https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json", function (error, json) {
var nodes = json.nodes;
force.nodes(nodes)
.links(json.links)
.linkDistance(function (d) {
return d.value * 1.5;
})
.friction(0.4);
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
var files = svg.selectAll(".file")
.data(json.nodes)
.enter().append("circle")
.attr("class", "file")
.attr("r", 10)
.attr("fill", function (d) {
return color(d.colorGroup);
});
var totalNodes = files[0].length;
files.append("title")
.text(function (d) { return d.name; });
force.start();
for (var i = totalNodes * totalNodes; i > 0; --i) force.tick();
force.stop();
nodes[0].x = width / 2;
nodes[0].y = height / 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; });
files.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("class", function(d){
var classString = "file"
if (d.index === 0) classString += " rootFile";
return classString;
})
.attr("r", function(d){
var radius = 10;
if (d.index === 0) radius = radius * 2;
return radius;
});
loading.remove();
});
Here's my data: https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json
{
"nodes":[
{"name":"File1.exe","colorGroup":0},
{"name":"File2.exe","colorGroup":0},
{"name":"File3.exe","colorGroup":0},
{"name":"File4.exe","colorGroup":0},
{"name":"File5.exe","colorGroup":0},
{"name":"File6.exe","colorGroup":0},
{"name":"File7.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File9.exe","colorGroup":0}
],
"links":[
{"source":1,"target":0,"value":10},
{"source":2,"target":0,"value":35},
{"source":3,"target":0,"value":50},
{"source":4,"target":0,"value":50},
{"source":5,"target":0,"value":65},
{"source":6,"target":0,"value":65},
{"source":7,"target":0,"value":81},
{"source":8,"target":0,"value":98},
{"source":9,"target":0,"value":100}
]
}
Fiddle
From my understanding of the bl.ocks page, this graph is running the tick method a certain amount of times. But my issue is the lengths of my links between the nodes are not proportionate to what I have in my JSON file.
I've opted for the static graph because I did not want to have the graph animate in, like in the standard graph.
Why are my links to the nodes nor correctly proportioned to match my JSON file?
I do not understand your question.
Is this what you mean?
var width = $("#theVizness").width(),
height = $("#theVizness").height();
var color = d3.scale.ordinal().range(["#ff0000", "#fff000", "#ff4900"]);
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("#theVizness").append("svg")
.attr("width", width)
.attr("height", height);
var loading = svg.append("text")
.attr("class", "loading")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Loading...");
d3.json("https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json", function (error, json) {
var nodes = json.nodes;
force.nodes(nodes)
.links(json.links)
.linkDistance(function (d) {
return d.value * 1.5;
})
.friction(0.4);
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
var files = svg.selectAll(".file")
.data(json.nodes)
.enter().append("circle")
.attr("class", "file")
.attr("r", 10)
.attr("fill", function (d) {
return color(d.colorGroup);
});
var totalNodes = files[0].length;
files.append("title")
.text(function (d) { return d.name; });
force.start();
for (var i = totalNodes * totalNodes; i > 0; --i) force.tick();
nodes[0].x = width / 2;
nodes[0].y = height / 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; });
files.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("class", function(d){
var classString = "file"
if (d.index === 0) classString += " rootFile";
return classString;
})
.attr("r", function(d){
var radius = 10;
if (d.index === 0) radius = radius * 2;
return radius;
});
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; });
files.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
loading.remove();
});
JSFiddle

How to hide labels with d3?

I am making a timeline sort of project. As of right now, my code looks like the following:
var width = 960,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-300)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("input.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); })
.style("visibility", function(d) {
return d.value == 2 ? "hidden" : "visible";
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 20)
.style("fill", function(d) { return color(d.group); })
.call(force.drag)
.style("visibility", function(d) {
return d.group == 1 ? "hidden" : "visible";
})
.on("mouseover", function(d) {
if(d.group == 2) {
node.filter(function(d) { return d.group == 1; }).style("visibility", "visible");
link.filter(function(d) { return d.value == 2; }).style("visibility", "visible");
texts.filter(function(d) { return d.value == 2; }).style("visibility", "visible");
}
}).on("mouseout", function(d) {
if(d.group == 2) {
node.filter(function(d) { return d.group == 1; }).style("visibility", "hidden");
link.filter(function(d) { return d.value == 2; }).style("visibility", "hidden");
}
});
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 + ")";
});
});
});
As of right now, the text, or labelsconstantly show, even when the nodes and links don't. I'm wondering what I need to do? I don't know if I can assign a value or group to the labels

Categories