Coloring Connected Components D3 V3 - javascript

I am working in D3 version 3, and I have a simple working program that can read a JSON file and convert it into an animated, linked graph.
I was wondering if there was a way for me to color separate connected components differently, for example, have the first component colored blue and another colored red, in a way that could be applied to a larger JSON file. I am pretty new to javascript, but wondering if I could use the group id to determine a node's color. I organized my example JSON file as follows-
{
"nodes":[
{"name":"node1","group":1},
{"name":"node2","group":1},
{"name":"node3","group":1},
{"name":"node4","group":3}
],
"links":[
{"source":2,"target":1,"weight":3},
{"source":0,"target":2,"weight":3}
]
}
Note that each node is part of a group (connected component).
My index.html is as follows.
<!DOCTYPE html>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
svg {
background-color:red;
width: 100%;
}
.link {
stroke: #fff;
}
.node text {
stroke:#fff;
fill: aliceblue;
cursor: pointer;
font-family: fantasy;
padding: 10%;
}
.node circle{
stroke:#fff;
stroke-width:3px;
fill:#fff;
padding: 20px;
}
</style>
<body>
<div class="nodeContainer">
<script>
var width = 960,
height = 500
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(.05)
.distance(100)
.charge(-100)
.size([width, height]);
d3.json("graphFile.json", function(json) {
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.weight); });
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r","20");
node.append("text")
.attr("dx", 23)
.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 + ")"; });
});
});
</script>
</div>

Here are a few approaches to color the nodes based on the group:
Using a pre-defined d3 colorScale (d3 oridnal color schemas)
var colorScale = d3.scale.category10().domain(json.nodes.map(function(d) { return d.group; }));
User defined ordinal color scale:
var colorScale = d3.scale.ordinal().domain([1, 2, 3]).range(["blue", "green", "red"]);
If the extent of the groups is huge, I'd recommend a linear scale using a gradient of colors. Similar to this: http://bl.ocks.org/jfreyre/b1882159636cc9e1283a
Using one of the above and applying to the nodes:
node.append("circle")
.attr("r","20")
.style('fill', function(d) {
return colorScale(d.group);
});
Here's a snippet:
var json = {
"nodes":[
{"name":"node1","group":1},
{"name":"node2","group":1},
{"name":"node3","group":1},
{"name":"node4","group":3}
],
"links":[
{"source":2,"target":1,"weight":3},
{"source":0,"target":2,"weight":3}
]
};
var width = 960,
height = 500
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(.05)
.distance(100)
.charge(-100)
.size([width, height]);
var colorScale = d3.scale.category10().domain(json.nodes.map(function(d) { return d.group; }));
//var colorScale = d3.scale.ordinal().domain([1, 2, 3]).range(["blue", "green", "red"]);
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return Math.sqrt(d.weight); });
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r","20")
.style('fill', function(d) {
return colorScale(d.group);
});
node.append("text")
.attr("dx", 23)
.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 + ")"; });
});
svg {
background-color:red;
width: 100%;
}
.link {
stroke: #fff;
}
.node text {
stroke:#fff;
fill: aliceblue;
cursor: pointer;
font-family: fantasy;
padding: 10%;
}
.node circle{
stroke:#fff;
stroke-width:3px;
fill:#fff;
padding: 20px;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<div class="nodeContainer">
</div>
Hope this helps.

Related

Network Graph D3. Different Lengths

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.nodeDetail {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
.text {
font: 12px sans-serif;
pointer-events: none; }
.node {
stroke:#fff;
stroke-width:3px;
fill:#008876;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script>
var graph = {
"nodes":[
{"name":"RFID Scanner","group":1},
{"name":"Chemical 1 Tag ID: 10001 ","group":1},
{"name":"Chemical 2 Tag ID: 10002","group":1},
{"name":"Chemical 3 Tag ID: 10003","group":1},
{"name":"Chemical 4 Tag ID: 10004","group":1}
],
"links":[
{"source":0,"target":1,"value":1 },
{"source":0,"target":2,"value":1},
{"source":0,"target":3,"value":1},
{"source":0,"target":4,"value":1},
{"source":0,"target":0,"value":1}
]
};
var width = 1000,
height = 1000;
var force = d3.layout.force()
.charge(-300)
.linkDistance(100)
.linkStrength(15)
.size([width,height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var drawGraph = function(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 gnodes = svg.selectAll('g.gnode')
.data(graph.nodes)
.enter()
.append('g')
.classed('gnode', true);
var node = gnodes.append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.name === 'RFID Scanner'? 40 : 20; })
.call(force.drag)
.filter(function(d) { return d.name !== 'RFID Scanner'})
.on("mouseover", function(d)
{
d3.select(labels[0][d.index]).style("visibility","visible")
})
.on("mouseout", function(d)
{
d3.select(labels[0][d.index]).style("visibility","hidden")
})
var labels = gnodes.append("text")
.text(function(d) { return d.name; })
.style("visibility", function(d) { return d.name === 'RFID Scanner'? 'visible' : 'hidden'; });
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; });
gnodes.attr("transform", function(d) {
return 'translate(' + [d.x, d.y] + ')';
});
});
};
drawGraph(graph);
</script>
</body>
</html>
Im trying to make different lengths in the network graph. I went through the API and I think it has something to do w .linkDistance(100). I am not too sure how to go about doing this. but I want to give each node a specific length. I tried going through the links data that I hard coded but it seems pretty limited. Thank you in advance, im curious to see how you do this.
It appears that you are using d3.js version 3.
The documentation about linkDistance states:
if distance is a function, then the function is evaluated for each link (in order), being passed the link and its index, with the this context as the force layout; the function's return value is then used to set each link's distance.
So if each link valueattribute contains the desired distance, instead of:
.linkDistance(100)
You may specify:
.linkDistance(function(d) {return d.value})

D3.js network graph

Hi I have this nice network graph going. The node that says "scanner" I am trying to make that radius bigger than the other nodes and I want the scanner to be printed on the node while the other nodes have the hover feature. I am struggling since the nodes have to be linked together in order for it to work cohesively. Thank you in advance, looking forward to how you tackle this issue.
var graph = {
"nodes":[
{"name":" Scanner","group":1},
{"name":"Chemical 1 ","group":1},
{"name":"Chemical 2","group":1},
{"name":"Chemical 3","group":1},
{"name":"Chemical 4","group":1}
],
"links":[
{"source":0,"target":1,"value":1},
{"source":0,"target":2,"value":1},
{"source":0,"target":3,"value":1},
{"source":0,"target":4,"value":1},
{"source":0,"target":0,"value":1}
]
};
var width = 1000,
height = 1000;
var force = d3.layout.force()
.charge(-300)
.linkDistance(300)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var drawGraph = function(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 gnodes = svg.selectAll('g.gnode')
.data(graph.nodes)
.enter()
.append('g')
.classed('gnode', true);
var node = gnodes.append("circle")
.attr("class", "node")
.attr("r", 25)
.on("mouseover", function(d)
{
d3.select(labels[0][d.index]).style("visibility","visible")
})
.on("mouseout", function(d)
{
d3.select(labels[0][d.index]).style("visibility","hidden")
})
.call(force.drag);
var labels = gnodes.append("text")
.text(function(d) { return d.name; })
.style("visibility", "hidden");
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; });
gnodes.attr("transform", function(d) {
return 'translate(' + [d.x, d.y] + ')';
});
});
};
drawGraph(graph);
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.nodeDetail {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
.text {
font: 12px sans-serif;
pointer-events: none; }
.node {
stroke:#fff;
stroke-width:3px;
fill:#2E8B57;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
Larger radius for "scanner" circle
d3.js selection.attr accepts a function as the second parameter, allowing to set different values according to the data.
In this function, you should check whether d.name is "Scanner", and return a radius value accordingly.
.attr("r", function(d) { return d.name === 'Scanner'? 40 : 25; })
Always display the label for Scanner
Similarly, selection.style setting visibility for labels should be initialized in a function checking data value:
.style("visibility", function(d) { return d.name === 'Scanner'? 'visible' : 'hidden'; });
In order to have the mouseover and mouseout event listeners only applying to the other nodes, use selection.filter:
.filter(function(d) { return d.name !== 'Scanner'})
.on("mouseover", function(d)
// ...
Demo
The snippet below illustrates the solution.
Remark: The leading space character scanner node found in the question has been removed in this demo : "Scanner" instead of " Scanner"
var graph = {
"nodes":[
{"name":"Scanner","group":1},
{"name":"Chemical 1 ","group":1},
{"name":"Chemical 2","group":1},
{"name":"Chemical 3","group":1},
{"name":"Chemical 4","group":1}
],
"links":[
{"source":0,"target":1,"value":1},
{"source":0,"target":2,"value":1},
{"source":0,"target":3,"value":1},
{"source":0,"target":4,"value":1},
{"source":0,"target":0,"value":1}
]
};
var width = 1000,
height = 1000;
var force = d3.layout.force()
.charge(-300)
.linkDistance(300)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var drawGraph = function(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 gnodes = svg.selectAll('g.gnode')
.data(graph.nodes)
.enter()
.append('g')
.classed('gnode', true);
var node = gnodes.append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.name === 'Scanner'? 40 : 25; })
.call(force.drag)
.filter(function(d) { return d.name !== 'Scanner'})
.on("mouseover", function(d)
{
d3.select(labels[0][d.index]).style("visibility","visible")
})
.on("mouseout", function(d)
{
d3.select(labels[0][d.index]).style("visibility","hidden")
})
var labels = gnodes.append("text")
.text(function(d) { return d.name; })
.style("visibility", function(d) { return d.name === 'Scanner'? 'visible' : 'hidden'; });
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; });
gnodes.attr("transform", function(d) {
return 'translate(' + [d.x, d.y] + ')';
});
});
};
drawGraph(graph);
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.nodeDetail {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
.text {
font: 12px sans-serif;
pointer-events: none; }
.node {
stroke:#fff;
stroke-width:3px;
fill:#2E8B57;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>

Unable to display text label with D3 force layout [duplicate]

<!DOCTYPE html>
<html>
<meta charset="utf-8">
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<body>
</body>
<script src="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.json("data.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", function(d) {return d.r;})
.style("fill", function(d) { return color(d.group); })
node.append("title")
.text(function(d) { return d.name; });
node.append("text")
.text("A");
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>
</html>
The code above is using D3js to draw a Force-directed graph drawing from some data, and I just want to place some text on the circle so I use node.append("text") you can see it above.
But however when add it it does not work, there is still not text on the circle so I wonder how could it be????
SVG does not allow a text element inside an circle element. You should put the circle and the text element inside a common g. Try something like this (not tested):
var node = svg.selectAll(".node")
.data(graph.nodes).enter().append('g').classed('node', true);
node.append("circle")
.attr("r", function(d) {return d.r;})
.style("fill", function(d) { return color(d.group); })
.append("title")
.text(function(d) { return d.name; });
node.append("text")
.text("A");
And then instead of setting cx and cy on nodes, set the transform property on the g.node:
force.on("tick", function() {
// ...
node.attr("transform", function(d) { return 'translate(' + [d.x, d.y] + ')'; })
});

D3.js force-directed graph from a JSON data file - not showing any nodes

I am a newbie in D3JS and I was trying to do a similar example to this one : http://bl.ocks.org/mbostock/4062045
I have generated this JSON file output.json and I try to get it running but nothing shows up!
{"nodes":[{"name":"Hiroyasu Nakata","group":1},{"name":"Kazuo Satoh","group":1},{"name":"Tyuzi Ohyama","group":1},{"name":"Yasufumi Fujiwara","group":1},{"name":"Youichi Nonogaki","group":1},{"name":"Yoshikazu Takeda","group":1},{"name":"N. Tamari","group":2},{"name":"S. H. Wemple","group":3},{"name":"Kun-Jing Lee","group":4},{"name":"Z. C. Huang","group":4},{"name":"J. C. Chen","group":4},{"name":"L. Cai","group":5},{"name":"S. Han","group":5},{"name":"G. May","group":5},{"name":"S. Kamra","group":5},{"name":"T. Krygowski","group":5},{"name":"A. Rohatgi","group":5},{"name":"Stephen Miller","group":7},{"name":"Paul H. Holloway","group":7}],"links":[{"source":1,"target":2},{"source":1,"target":3},{"source":1,"target":4},{"source":1,"target":5},{"source":1,"target":6},{"source":2,"target":3},{"source":2,"target":4},{"source":2,"target":5},{"source":2,"target":6},{"source":3,"target":4},{"source":3,"target":5},{"source":3,"target":6},{"source":4,"target":5},{"source":4,"target":6},{"source":5,"target":6},{"source":9,"target":10},{"source":9,"target":11},{"source":10,"target":11},{"source":12,"target":13},{"source":12,"target":14},{"source":12,"target":15},{"source":12,"target":16},{"source":12,"target":17},{"source":13,"target":14},{"source":13,"target":15},{"source":13,"target":16},{"source":13,"target":17},{"source":14,"target":15},{"source":14,"target":16},{"source":14,"target":17},{"source":15,"target":16},{"source":15,"target":17},{"source":16,"target":17},{"source":18,"target":19}]}
This is the HTML file :
<!DOCTYPE html>
<meta charset="utf-8">
<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.json("output.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>
What did I do wrong? And how can I fix it?
Thanks!

How to display a text when mouseover a node in D3 force layout

I am trying to display the text when I move the mouse over a node in Force-Directed Graph in D3.js. My problem is that when I move the mouse over any node, all the texts of these nodes are displayed. Here is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.nodeDetail {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
node .text {
font: 12px sans-serif;
pointer-events: none;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.js"></script>
<script src="d3/d3.v3.min.js charset=UTF-8"></script>
<script>
var graph = {
"nodes":[
{"name":"Myriel","group":1},
{"name":"Napoleon","group":1},
{"name":"Mlle.Baptistine","group":1},
{"name":"Mme.Magloire","group":1},
{"name":"CountessdeLo","group":1}
],
"links":[
{"source":1,"target":0,"value":1},
{"source":2,"target":0,"value":8},
{"source":3,"target":0,"value":10},
{"source":3,"target":2,"value":6},
{"source":4,"target":0,"value":1}
]
};
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 drawGraph = function(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 gnodes = svg.selectAll('g.gnode')//('g.gnode')
.data(graph.nodes)
.enter()
.append('g')
.classed('gnode', true);
var node = gnodes.append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); })
.on("mouseover", function(d)
{
d3.select(this).transition()
.duration(750)
.attr("r", 15);
return labels.style("visibility", "visible");
})
.on("mouseout", function()
{
d3.select(this).transition()
.duration(750)
.attr("r", 5);
return labels.style("visibility", "hidden");//})
})
.call(force.drag);
var labels = gnodes.append("text")
.text(function(d) { return d.name; })
.style("visibility", "hidden");
/*gnodes.append("text")
.text(function(d) { return d.name; })
.style("visibility", "hidden");*/
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; });
gnodes.attr("transform", function(d) {
return 'translate(' + [d.x, d.y] + ')';
});
});
};
drawGraph(graph);
</script>
</body>
</html>
I tried to do the following codes but it did not work neither:
.on("mouseover", function(d)
{
d3.select(this).transition()
.duration(750)
.attr("r", 15);
d3.select(this).append(text)
.style("visibility", "visible");
})
How can I display the text that is related to a particular node when I move the mouse over that node? Could anyone please help me solve this problem. Thank you in advance.
This should help:
.on("mouseover", function(d)
{
d3.select(labels[0][d.index]).style("visibility","visible")
})
.on("mouseout", function(d)
{
d3.select(labels[0][d.index]).style("visibility","hidden")
})
Learning d3 myself here - also thanks very much to juvian
I find a more transparent way of coding this would be to say
.on("mouseover", function(d) {
d3.select(this).select("text").style("visibility", "visible")
})
which selects the object you hover over (a group), selects the text inside it and changes the style to visible. This also avoids the need to make a labels variable which gets a bit messy with the array indexing.

Categories

Resources