Error with force cluster layout d3 - javascript

I'm trying to do a graph like this example: http://bl.ocks.org/mbostock/1747543
The problem that I have is that when I execute my html, there are an error that says:
SyntaxError: expected expression, got '.'
My code is:
var width = 900,
height = 500;
var color = d3.scale.category20();
var force = d3.layout.force()
.gravity(.1)
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("body").select('#contenedor').select('#contenido').append("svg")
.attr("width", width)
.attr("height", height);
d3.json("fisica_noms.json", function(error, graph) {
if (error) throw error;
var clusters = []
var getColors = function(category){
var nods = graph.nodes;
var subs = [];
var m = {};
for (var ele in nods){
for(var categs in ele){
if (!m[categs] && categs == category){
m[categs] = true;
subs.push(categs);
};
};
};
return subs;
};
var sameCategory = function(category,subCategory){
var nods = graph.nodes;
var subs = [];
for(var i=0; i<nods.length;i++){
if(nods[i][category]===subCategory){
subs.push(nods[i])};
};
return subs
};
var searchClusters = function(){
var nods = graph.nodes;
var nCom = getColors('Comunitat').length;
for(var i=0; i<nCom;i++){
clusters.push(sameCategory('Comunitat',i));
clusters[i] = d3.max(clusters[i], function(d) {return d.degree;});
};
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("weight", function(d) { return Math.sqrt(d.value); });
force.linkStrength(function(link){return link.value}) ;
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function(d){return d.degree/60})
.style("fill", function(d) { return color(d.Comunitat); })
.call(force.drag)
.on('click',function(d){showInfo(d);});
node.append("title")
.text(function(d) { return d.id; });
force.on("tick",tick);
function tick (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; });
console.log(e);
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
.each(cluster(10 * e.alpha * e.alpha))
};
// Move d to be adjacent to the cluster node.
function cluster(alpha) {
return function(d) {
var cluster = clusters[d.Comunitat];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = d.degree/60 + cluster.degree/60;
if (l != r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
cluster.x += x;
cluster.y += y;
}
};
}
});
};
The console says that my error is here:
.each(cluster(10 * e.alpha * e.alpha))

Related

Contamination in a graph using d3js

I'm trying to simulate the diffusion of a virus in a network. I've modified a random graph generator to create a graph in which each node represents an individual. Now I would like to be able to click on a node (which infects him) and to see the propagation of the virus in the graph. For exemple, each neighbour of an infected node is infected with a probability p chosen before. For the moment, I can only click on a node and it turns red, but that's it. Any ideas ?
Here is the javascript code :
var width = 1300,
height = 1000;
n = 30
m = 100
charge = -2000
z = 0,7 // contamination parameter
function changevalue() {
n = document.getElementById("numberofnodes").value;
m = document.getElementById("numberoflinks").value;
charge = document.getElementById("chargenumber").value;
}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.on("dblclick", create);
create();
function create () {
svg.selectAll(".link, .node").remove();
randomGraph(n, m, charge);
}
function randomGraph (n, m, charge) {
var nodes = d3.range(n).map(Object),
list = randomChoose(unorderedPairs(d3.range(n)), m),
links = list.map(function (a) { return {source: a[0], target: a[1]} });
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links)
.charge(charge)
.on("tick", tick)
.start();
var svgLinks = svg.selectAll(".link").data(links)
.enter().append("line")
.attr("class", "link");
var svgNodes = svg.selectAll(".node").data(nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", 2)
.style("fill", "green")
.style("fill", function(d) {return d.color; })
.each(function() {
var sel = d3.select(this);
var state = false;
sel.on("click", function() {
state = !state;
if (state) {
sel.style("fill", "red");
} else {
sel.style("fill", function(d) { return d.color; });
}
});
});
svgNodes.transition().duration(2000)
.attr("r", function (d) { return 0.8 * d.weight })
svgLinks.transition().duration(20000)
.style("stroke-width", 1);
function tick () {
svgNodes
.attr("cx", function(d) { return d.x })
.attr("cy", function(d) { return d.y });
svgLinks
.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 });
}
}
function randomChoose (s, k) {
var a = [], i = -1, j;
while (++i < k) {
j = Math.floor(Math.random() * s.length);
a.push(s.splice(j, 1)[0]);
};
return a;
}
function unorderedPairs (s) {
var i = -1, a = [], j;
while (++i < s.length) {
j = i;
while (++j < s.length) a.push([s[i],s[j]])
};
return a;
}
A possible approach is to create a function that takes in an array 'infected' nodes and for each infected node:
examines the links to look for this node is linked
where a link is found, randomly add the linked node to the array of newly infected
nodes, and repeat the function. The random can be based on whatever probability you want - currently the example below uses 0.5.
repeat this function a limited number of times, else it will continue
forever
The example below assigns a colour to the infected nodes based on which iteration of the function that node is infected.
console.clear()
var width = 600,
height = 500;
n = 30
m = 100
charge = -2000
z = 0, 7 // contamination parameter
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var count = 0
var iterations = 5
var links, nodes
var colour = d3.scaleLinear()
.domain([1, 10])
.range(["red", "steelblue"])
.interpolate(d3.interpolateHcl);
create();
function create() {
svg.selectAll(".link, .node").remove();
randomGraph(n, m, charge);
}
function randomGraph(n, m, charge) {
var nodes = d3.range(n).map(function(d) {
let obj = {}
obj.r = 15
obj.state = false
return obj
});
list = randomChoose(unorderedPairs(d3.range(n)), m),
links = list.map(function(a) {
return {
source: a[0],
target: a[1]
}
});
var force = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links))
.force("collide", d3.forceCollide(30))
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", tick)
var svgLinks = svg.selectAll(".link")
.data(links)
.enter()
.append("line")
.attr("class", "link")
var svgNodes = svg.selectAll(".node")
.data(nodes)
.enter()
.append("circle")
.attr("id", d => "node-" + d.index)
.attr("class", "node")
.attr("r", 2)
.style("fill", "grey")
.on("click", startVirus)
svgNodes.transition().duration(2000)
.attr("r", function(d) {
return 0.8 * d.r
})
svgLinks.transition().duration(2000)
.style("stroke-width", 1);
function tick() {
svgNodes
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
});
svgLinks
.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
});
}
}
function startVirus(node) {
d3.selectAll('circle').style("fill", function(d) {
d.state = false
return "grey"
})
let id = node.index
count = 0;
console.clear()
infectCircles([id])
}
function infectCircles(carrierIDs) {
count = count + 1
let potentialInfections = []
carrierIDs.forEach(function(c) {
let circleID = "#node-" + c
d3.select(circleID)
.transition(2000)
.style("fill", function(d) {
if (d.state == true) {
return d.colour
} else {
d.state = true
d.colour = colour(count)
return d.colour
}
})
links.forEach(function(l) {
if (l.source.index == c) {
if (Math.random() < 0.5) {
console.log("infect! " + l.target.index)
potentialInfections.push(l.target.index)
}
}
if (l.target.index == c) {
if (Math.random() < 0.5) {
console.log("infect! " + l.source.index)
potentialInfections.push(l.source.index)
}
}
})
if (count <= iterations) {
infectCircles(potentialInfections)
}
})
}
function randomChoose(s, k) {
var a = [],
i = -1,
j;
while (++i < k) {
j = Math.floor(Math.random() * s.length);
a.push(s.splice(j, 1)[0]);
};
return a;
}
function unorderedPairs(s) {
var i = -1,
a = [],
j;
while (++i < s.length) {
j = i;
while (++j < s.length) a.push([s[i], s[j]])
};
return a;
}
circle {
stroke: white;
stroke-width: 2;
}
line {
fill: none;
stroke: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

Updating D3 bubble radius with collide simulation

I've been wrestling very hard with D3 to try to make a simple bubble-chart using force collide that live-updates the bubble size.
I can get the chart to show on the first data update with force collide. However subsequent data calls freeze the chart and the sizes are never updated:
https://jsfiddle.net/d2zcfjfa/1/
node = svg.selectAll('g.node')
.data(root.children)
.enter()
.append('g')
.attr('class', 'node')
.append('circle')
.attr('r', function(d) { return d.r * 1.4; })
.attr('fill', function(d) { return color(d.data.name); })
.call(d3.drag()
.on("start", dragStart)
.on("drag", dragged)
.on("end", dragEnd));
var circleUpdate = node.select('circle')
.attr('r', function(d)
{
return d.r;
});
simulation.nodes(root.children);
I can get updates to work but only without using the collide simulation as seen here:
https://jsfiddle.net/rgdox7g7/1/
node = svg.selectAll('g.node')
.data(root.children)
.enter()
.append('g')
.attr('id', function(d) { return d.id; })
.attr('class', 'node')
.attr('transform', function(d)
{
return "translate(" + d.x + "," + d.y + ")";
});
var nodeUpdate = svg.selectAll('g.node')
.transition()
.duration(2000)
.ease(d3.easeLinear);
var circleUpdate = nodeUpdate.select('circle')
.attr('r', function(d)
{
return d.r;
});
node.append("circle")
.attr("r", function(d) { return d.r; })
.style('fill', function(d) { return color(d.data.name); });
Everything I have tried to mix these two solutions together simply does not work. I have scoured the internet for other examples and nothing I can find is helping. Can someone please help me understand what to do? I never thought D3 would be such a frustration!
stackoverflow: the place where you have to answer your own questions.
here is my working solution:
https://jsfiddle.net/zc0fgh6y/
var subscription = null;
var width = 600;
var height = 300;
var maxSpeed = 1000000;
var pack = d3.pack().size([width, height]).padding(0);
var svg = d3.select('svg');
var node = svg.selectAll("g.node");
var root;
var nodes = [];
var first = true;
var scaleFactor = 1.4;
var color = d3.interpolateHcl("#0faac3", "#dd2323");
var forceCollide = d3.forceCollide()
.strength(.8)
.radius(function(d)
{
return d.r;
}).iterations(10);
var simulationStart = d3.forceSimulation()
.force("forceX", d3.forceX(width/2).strength(.04))
.force("forceY", d3.forceY(height/2).strength(.2))
.force('collide', forceCollide)
.on('tick', ticked);
var simulation = d3.forceSimulation()
.force("forceX", d3.forceX(width/2).strength(.0005))
.force("forceY", d3.forceY(height/2).strength(.0025))
.force('collide', forceCollide)
.on('tick', ticked);
function ticked()
{
if (node)
{
node.attr('transform', function(d)
{
return "translate(" + d.x + "," + d.y + ")";
}).select('circle').attr('r', function(d)
{
return d.r;
});
}
}
function rand(min, max)
{
return Math.random() * (max - min) + min;
};
setInterval(function()
{
var hosts = [];
for (var i = 0; i < 100; i++)
{
hosts.push({name: i, cpu: rand(10,100), speed: rand(0,maxSpeed)});
}
root = d3.hierarchy({children: hosts})
.sum(function(d)
{
return d.cpu ? d.cpu : 0;
});
var leaves = pack(root).leaves().map(function(item)
{
return {
id: 'node-'+item.data.name,
name: item.data.name,
r: item.r * scaleFactor,
x: width/2,
y: height/2,
cpu: item.data.cpu,
speed: item.data.speed
};
});
for (var i = 0; i < leaves.length; i++)
{
if (nodes[i] && nodes[i].id == leaves[i].id)
{
var oldR = nodes[i].newR;
nodes[i].oldR = oldR;
nodes[i].newR = leaves[i].r;
nodes[i].cpu = leaves[i].cpu;
nodes[i].speed = leaves[i].speed;
}
else
{
nodes[i] = leaves[i];
//nodes[i].r = 1;
nodes[i].oldR = 1;//nodes[i].r;
nodes[i].newR = leaves[i].r;
}
}
if (first)
{
first = false;
node = node.data(nodes, function(d) { return d.id; });
node = node.enter()
.append('g')
.attr('class', 'node');
node.append("circle")
.style("fill", 'transparent');
node.append("text")
.attr("dy", "0.3em")
.style('fill', 'transparent')
.style("text-anchor", "middle")
.text(function(d)
{
return d.name;//.substring(0, d.r / 4);
});
// transition in size
node.transition()
.ease(d3.easePolyInOut)
.duration(950)
.tween('radius', function(d)
{
var that = d3.select(this);
var i = d3.interpolate(1, d.newR);
return function(t)
{
d.r = i(t);
that.attr('r', function(d)
{
return d.r;
});
simulationStart.nodes(nodes).alpha(1);
}
});
// fade in text color
node.select('text')
.transition()
.ease(d3.easePolyInOut)
.duration(950)
.style('fill', 'white');
// fade in circle size
node.select('circle')
.transition()
.ease(d3.easePolyInOut)
.duration(950)
.style('fill', function(d)
{
return color(d.speed / maxSpeed);
});
}
else
{
// transition to new size
node.transition()
.ease(d3.easeLinear)
.duration(950)
.tween('radius', function(d)
{
var that = d3.select(this);
var i = d3.interpolate(d.oldR, d.newR);
return function(t)
{
d.r = i(t);
that.attr('r', function(d)
{
return d.r;
});
simulation.nodes(nodes).alpha(1);
}
});
// transition to new color
node.select('circle')
.transition()
.ease(d3.easeLinear)
.duration(950)
.style('fill', function(d)
{
return color(d.speed / maxSpeed);
});
}
}, 1000);

Dynamic Node Addition/Updation on D3 js Network Graph on button click

I'm trying to add Nodes and Links between them on a Button click without loading any .Json file.
Here is what i've done: Create Nodes/Links on document ready
$(document).ready(function(){
var w = $("#graph").innerWidth();
var h = $("#graph").innerHeight();
var default_node_color = "#ccc";
var default_link_color = "red";
var nominal_base_node_size = 8;
var nominal_text_size = 10;
var max_text_size = 24;
var nominal_stroke = 1.5;
var highlight_stroke_width = 4;
var max_stroke = 4.5;
var min_zoom = 0.1;
var max_zoom = 7;
var svg = d3.select("#graph").append("svg");
var zoom = d3.behavior.zoom().scaleExtent([min_zoom,max_zoom])
var g = svg.append("g");
svg.style("cursor","move");
var dnodes = [];
var dlinks = [];
function findNode(id) {
for (var i in dnodes) {
if (dnodes[i]["id"] === id) return dnodes[i];
};
};
function addNode(id,name) {
var newNode = findNode(id);
if(newNode == undefined)
{
dnodes.push({"id":id,"name":name});
//update(dnodes,dlinks);
}
};
function addLink(sourceId, targetId) {
var sourceNode = findNode(sourceId);
var targetNode = findNode(targetId);
if((sourceNode !== undefined) && (targetNode !== undefined)) {
dlinks.push({"source": sourceNode, "target": targetNode});
//update(dnodes,dlinks);
}
};
addNode("1","2XYZ");
addNode("3","3XYZ");
addNode("4","4XYZ");
addNode("5","5XYZ");
addLink("1","2");
addLink("1","3");
addLink("1","4");
addLink("1","5");
var force = d3.layout.force()
.linkDistance(160)
.charge(-300)
.friction(0.5)
.size([w,h]);
var nodes = force.nodes(dnodes);
var links = force.links(dlinks);
force.start();
//function update(dnodes, dlinks)
//{
function isConnected(sourceNodeid, destNodeid)
{
for(var i in dlinks)
{
if((dlinks[i].source.id == sourceNodeid && dlinks[i].target.id==destNodeid)||((dlinks[i].source.id == destNodeid && dlinks[i].target.id==sourceNodeid)))
{
return true;
}
}
if(sourceNodeid == destNodeid)
{
return true;
}
return false;
};
function dragstart(d, i) {
force.stop() // stops the force auto positioning before you start dragging
}
function dragmove(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick();
}
function dragend(d, i) {
d.fixed = true;
tick();
}
var node_drag = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", dragmove)
.on("dragend", dragend);
var link = g.selectAll(".link")
.data(dlinks)
.enter().append("line")
.attr("class", "link")
.style("stroke-width",nominal_stroke)
.style("stroke", default_link_color)
var node = g.selectAll(".node")
.data(dnodes)
.enter().append("g")
.attr("class", "node")
.call(node_drag);
var circle = node.append("rect")
.attr("x", "-15px")
.attr("y", "-15px")
.attr("rx", "4")
.attr("ry", "4")
.attr("width", "30px")
.attr("height", "30px")
.attr("id", function (d) {return d.id;})
.attr("fill", "#336699");
var text = g.selectAll(".text")
.data(dnodes)
.enter().append("text")
.attr("dy", ".35em")
.attr("y","22")
.style("font-size", nominal_text_size + "px")
text.text(function(d) { return d.name; })
.style("text-anchor", "middle");
zoom.on("zoom", function() {
var stroke = nominal_stroke;
if (nominal_stroke*zoom.scale()>max_stroke) stroke = max_stroke/zoom.scale();
link.style("stroke-width",stroke);
circle.style("stroke-width",stroke);
var text_size = nominal_text_size;
if (nominal_text_size*zoom.scale()>max_text_size) text_size = max_text_size/zoom.scale();
text.style("font-size",text_size + "px");
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
});
svg.call(zoom);
resize();
force.on("tick", tick);
function tick()
{
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
text.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; });
//node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function resize() {
var width = $("#graph").innerWidth();
var height = $("#graph").innerHeight();
svg.attr("width", width).attr("height", height);
force.size([force.size()[0]+(width-w)/zoom.scale(),force.size()[1]+(height-h)/zoom.scale()]).resume();
w = width;
h = height;
}
//}
});
text {
font-family: sans-serif;
pointer-events: none;
}
html,body { width:100%; height:100%; margin:none; padding:none; }
#graph { width:100%;height:100%; margin:auto; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.10/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<body>
<div id="graph"></div>
</body>
and Here is What i'm trying to do: By Update function
$(document).ready(function(){
var w = $("#graph").innerWidth();
var h = $("#graph").innerHeight();
var default_node_color = "#ccc";
var default_link_color = "red";
var nominal_base_node_size = 8;
var nominal_text_size = 10;
var max_text_size = 24;
var nominal_stroke = 1.5;
var highlight_stroke_width = 4;
var max_stroke = 4.5;
var min_zoom = 0.1;
var max_zoom = 7;
var svg = d3.select("#graph").append("svg");
var zoom = d3.behavior.zoom().scaleExtent([min_zoom,max_zoom])
var g = svg.append("g");
svg.style("cursor","move");
var dnodes = [];
var dlinks = [];
function findNode(id) {
for (var i in dnodes) {
if (dnodes[i]["id"] === id) return dnodes[i];
}
}
function addNode(id,name) {
var newNode = findNode(id);
if(newNode == undefined)
{
dnodes.push({"id":id,"name":name});
update(dnodes,dlinks);
}
}
function addLink(sourceId, targetId) {
var sourceNode = findNode(sourceId);
var targetNode = findNode(targetId);
if((sourceNode !== undefined) && (targetNode !== undefined)) {
dlinks.push({"source": sourceNode, "target": targetNode});
update(dnodes,dlinks);
}
}
$("#btnadd").click(function(){
addNode("1","2XYZ");
addNode("3","3XYZ");
addNode("4","4XYZ");
addNode("5","5XYZ");
addLink("1","2");
addLink("1","3");
addLink("1","4");
addLink("1","5");
});
var force = d3.layout.force()
.linkDistance(160)
.charge(-300)
.friction(0.5)
.size([w,h]);
var nodes = force.nodes(dnodes);
var links = force.links(dlinks);
force.start();
function update(dnodes, dlinks)
{
function isConnected(sourceNodeid, destNodeid)
{
for(var i in dlinks)
{
if((dlinks[i].source.id == sourceNodeid && dlinks[i].target.id==destNodeid)||((dlinks[i].source.id == destNodeid && dlinks[i].target.id==sourceNodeid)))
{
return true;
}
}
if(sourceNodeid == destNodeid)
{
return true;
}
return false;
}
function dragstart(d, i) {
force.stop();
}
function dragmove(d, i) {
d.px += d3.event.dx;
d.py += d3.event.dy;
d.x += d3.event.dx;
d.y += d3.event.dy;
tick();
}
function dragend(d, i) {
d.fixed = true;
tick();
}
var node_drag = d3.behavior.drag()
.on("dragstart", dragstart)
.on("drag", dragmove)
.on("dragend", dragend);
var link = g.selectAll(".link")
.data(dlinks)
.enter().append("line")
.attr("class", "link")
.style("stroke-width",nominal_stroke)
.style("stroke", default_link_color);
var node = g.selectAll(".node")
.data(dnodes)
.enter().append("g")
.attr("class", "node")
.call(node_drag);
var circle = node.append("rect")
.attr("x", "-15px")
.attr("y", "-15px")
.attr("rx", "4")
.attr("ry", "4")
.attr("width", "30px")
.attr("height", "30px")
.attr("id", function (d) {return d.id;})
.attr("fill", "#336699");
var text = g.selectAll(".text")
.data(dnodes)
.enter().append("text")
.attr("dy", ".35em")
.attr("y","22")
.style("font-size", nominal_text_size + "px")
text.text(function(d) { return d.name; })
.style("text-anchor", "middle");
zoom.on("zoom", function() {
var stroke = nominal_stroke;
if (nominal_stroke*zoom.scale()>max_stroke) stroke = max_stroke/zoom.scale();
link.style("stroke-width",stroke);
circle.style("stroke-width",stroke);
var text_size = nominal_text_size;
if (nominal_text_size*zoom.scale()>max_text_size) text_size = max_text_size/zoom.scale();
text.style("font-size",text_size + "px");
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
});
svg.call(zoom);
resize();
force.on("tick", tick);
function tick()
{
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
text.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; });
//node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
function resize() {
var width = $("#graph").innerWidth();
var height = $("#graph").innerHeight();
svg.attr("width", width).attr("height", height);
force.size([force.size()[0]+(width-w)/zoom.scale(),force.size()[1]+(height-h)/zoom.scale()]).resume();
w = width;
h = height;
}
}
});
text {
font-family: sans-serif;
pointer-events: none;
}
html,body { width:100%; height:100%; margin:none; padding:none; }
#graph { width:100%;height:100%; margin:auto; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.10/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<body>
<button id="btnadd">Add</button>
<div id="graph"></div>
</body>
1st link code is working fine , 2nd link code is not working properly.
in 2nd link code i'm updating graph by update() function to add nodes on a button click.
addNode() addLink() i want to call on a button click. something like:
$("#btnadd").click(function(){
addNode("1","2XYZ");
addNode("3","3XYZ");
addLink("1","2");
});
I'm not getting where i'm doing wrong.
Thanks!
You are calling the update after each node insertion.
if(newNode == undefined)
{
dnodes.push({"id":id,"name":name});
update(dnodes,dlinks);//don't do this
}
Instead make all your nodes and links in the button click like this(commented below).
$("#btnadd").click(function() {
addNode("1", "2XYZ");
addNode("3", "3XYZ");
addNode("4", "4XYZ");
addNode("5", "5XYZ");
addLink("1", "2");
addLink("1", "3");
addLink("1", "4");
addLink("1", "5");
update(dnodes, dlinks);//call your update function
force.start();//you forgot to start the force layout.
});
working code here

Problems to label nodes in a particular force layout D3

I have made an algorithm with D3 to vizualize some datas. The algorithm is doing what i'm expecting of him. He separates nodes in multiple focis with the Force layout, links and nodes appears and disappears dynamicaly.
The problems comes with the labeling of the nodes. D3 seems to do the work but multiples times for each nodes (when i look at it with firebug).
Here is my code :
var actions = [
{"action":"arrivee","id":"001","service":1},
{"action":"arrivee","id":"002","service":1},
{"action":"arrivee","id":"003","service":1},
{"action":"arrivee","id":"004","service":1},
{"action":"arrivee","id":"005","service":1},
{"action":"arrivee","id":"006","service":3},
{"action":"arrivee","id":"007","service":3},
{"action":"arrivee","id":"008","service":3},
{"action":"arrivee","id":"009","service":3},
{"action":"arrivee","id":"010","service":2},
{"action":"arrivee","id":"011","service":2},
{"action":"arrivee","id":"012","service":2},
{"action":"arrivee","id":"013","service":2},
{"action":"arrivee","id":"014","service":4},
{"action":"arrivee","id":"015","service":4},
{"action":"arrivee","id":"016","service":4},
{"action":"arrivee","id":"017","service":4},
{"action":"contact","id":"0","source":"001","target":"017"},
{"action":"contact","id":"1","source":"016","target":"012"},
{"action":"contact","id":"2","source":"004","target":"011"},
{"action":"contact","id":"3","source":"001","target":"010"},
{"action":"fincontact","id":"0"},
{"action":"depart","id":"017"},
{"action":"arrivee","id":"018","service":2},
{"action":"arrivee","id":"019","service":1},
{"action":"arrivee","id":"020","service":1},
{"action":"arrivee","id":"021","service":0},
{"action":"arrivee","id":"022","service":0},
{"action":"arrivee","id":"023","service":0},
{"action":"arrivee","id":"024","service":0},
{"action":"arrivee","id":"025","service":0},
{"action":"arrivee","id":"026","service":0},
{"action":"arrivee","id":"027","service":3},
{"action":"arrivee","id":"028","service":2},
{"action":"arrivee","id":"029","service":2},
{"action":"arrivee","id":"030","service":2},
{"action":"arrivee","id":"031","service":2},
{"action":"arrivee","id":"032","service":4},
{"action":"arrivee","id":"033","service":4},
{"action":"arrivee","id":"034","service":4},
{"action":"arrivee","id":"035","service":4},
{"action":"contact","id":"4","source":"013","target":"002"},
{"action":"contact","id":"5","source":"009","target":"008"},
{"action":"contact","id":"6","source":"005","target":"007"},
{"action":"contact","id":"7","source":"009","target":"014"},
{"action":"fincontact","id":"7"},
{"action":"fincontact","id":"6"},
{"action":"fincontact","id":"5"},
{"action":"fincontact","id":"4"},
{"action":"fincontact","id":"3"},
{"action":"fincontact","id":"2"},
{"action":"fincontact","id":"1"},
{"action":"depart","id":"016"},
{"action":"depart","id":"015"},
{"action":"depart","id":"014"},
{"action":"depart","id":"013"},
{"action":"depart","id":"012"},
{"action":"depart","id":"011"},
{"action":"depart","id":"010"},
{"action":"depart","id":"009"},
{"action":"depart","id":"008"},
{"action":"depart","id":"007"},
{"action":"depart","id":"006"},
{"action":"depart","id":"005"},
{"action":"depart","id":"004"},
{"action":"depart","id":"003"},
{"action":"depart","id":"002"},
{"action":"depart","id":"018"},
{"action":"depart","id":"019"},
{"action":"depart","id":"020"},
{"action":"depart","id":"021"},
{"action":"depart","id":"022"},
{"action":"depart","id":"023"},
{"action":"depart","id":"024"},
{"action":"depart","id":"025"},
{"action":"depart","id":"026"},
{"action":"depart","id":"027"},
{"action":"depart","id":"028"},
{"action":"depart","id":"029"},
{"action":"depart","id":"030"},
{"action":"depart","id":"031"},
{"action":"depart","id":"032"},
{"action":"depart","id":"033"},
{"action":"depart","id":"034"},
{"action":"depart","id":"035"},
{"action":"depart","id":"001"}]
var vv = window,
w = vv.innerWidth,
h = vv.innerHeight;
var rmax = 30;
foci = [{x: 500, y: 150}, {x: 200, y: 500}, {x: 700, y: 500}, {x: 400, y: 700}, {x: 600, y: 700}];
//canevas selection
var svg = d3.select("#animviz")
.append("svg")
.attr("width", w)
.attr("height", h);
var fill = d3.scale.category10();
//link and node class creation
svg.append("g").attr("class", "links");
svg.append("g").attr("class", "nodes");
//to know if the graphs are up to date
var uptodate = true;
var nIntervId;
//containers de noeuds et liens
var nodes = [], links = [];
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([w, h])
.friction(0.9)
.charge(-50)
.gravity(0.02)
.linkDistance(50)
.charge(-500)
.on("tick", tick);
var iter = 0;
var node = svg.select(".nodes").selectAll(".node");
var link = svg.select(".links").selectAll(".link");
//repeat an action every "interval"
var interval = 0.2;
nIntervId = setInterval(function() {
var action = readData();
addData(action);
if(!uptodate){
update();
}
}, interval*1000);
function addData(action) {
uptodate = false;
switch(action.action) {
case "arrivee":
nodes.push({id: action.id, service: action.service});
break;
case "depart":
for (var i = nodes.length - 1; i >= 0; i--) {
if(nodes[i].id == action.id){
nodes.splice(i, 1);
}
};
break;
case "contact":
var source;
var target;
for (var i = nodes.length - 1; i >= 0; i--) {
if(nodes[i].id == action.source){
source = nodes[i];
}
if(nodes[i].id == action.target){
target = nodes[i];
}
};
links.push({source:source, target:target, id:action.id});
break;
case "fincontact":
for (var i = links.length - 1; i >= 0; i--) {
if(links[i].id == action.id){
links.splice(i, 1);
}
};
break;
default:
uptodate = true;
}
}
function readData(){
var n = iter;
iter++;
var data = actions[n];
return data;
}
function update() {
force.start();
link = link.data(force.links(), function(d) { return d.source.id+"-"+d.target.id; });
link.enter()
.append("line")
.attr("class", "link")
.attr("stroke", "#ccc")
.attr("stroke-width", 2);
link.exit().remove();
var r = d3.scale.sqrt()
.domain(d3.extent(force.nodes(), function(d) {return d.weight; }))
.range([15, rmax]);
node = node.data(force.nodes(), function(d) { return d.id; });
node.enter()
.append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 20)
.style("stroke-width", "10")
.style("fill", "white")//function(d, i) { return fill(d.service ); })
.style("stroke", function(d, i) { return d3.rgb(i & 1 ? "red" : "green") })
node.append("text")
.attr("text-anchor","middle")
.text(function(d) {return d.id});
node.exit().remove();
}
function tick(e) {
var k = 0.5 * e.alpha;
nodes.forEach(function(o, i) {
o.y += (foci[o.service].y - o.y) * k;
o.x += (foci[o.service].x - o.x) * k;
});
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 = Math.max(rmax, Math.min(w - rmax, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(rmax, Math.min(h - rmax, d.y)); });
}
readData() read the dataframe, addData() is a big loop to have dynamical moves and update() is the D3 part where is my attempt to label my nodes.
I'm missing something obvious.
Thanks you.
After a few days of searches i'm able to post the working code as an answer.
var actions = [
{"action":"arrivee","id":"001","service":1},
{"action":"arrivee","id":"002","service":1},
{"action":"arrivee","id":"003","service":1},
{"action":"arrivee","id":"004","service":1},
{"action":"arrivee","id":"005","service":1},
{"action":"arrivee","id":"006","service":3},
{"action":"arrivee","id":"007","service":3},
{"action":"arrivee","id":"008","service":3},
{"action":"arrivee","id":"009","service":3},
{"action":"arrivee","id":"010","service":2},
{"action":"arrivee","id":"011","service":2},
{"action":"arrivee","id":"012","service":2},
{"action":"arrivee","id":"013","service":2},
{"action":"arrivee","id":"014","service":4},
{"action":"arrivee","id":"015","service":4},
{"action":"arrivee","id":"016","service":4},
{"action":"arrivee","id":"017","service":4},
{"action":"contact","id":"0","source":"001","target":"017"},
{"action":"contact","id":"1","source":"016","target":"012"},
{"action":"contact","id":"2","source":"004","target":"011"},
{"action":"contact","id":"3","source":"001","target":"010"},
{"action":"fincontact","id":"0"},
{"action":"depart","id":"017"},
{"action":"arrivee","id":"018","service":2},
{"action":"arrivee","id":"019","service":1},
{"action":"arrivee","id":"020","service":1},
{"action":"arrivee","id":"021","service":0},
{"action":"arrivee","id":"022","service":0},
{"action":"arrivee","id":"023","service":0},
{"action":"arrivee","id":"024","service":0},
{"action":"arrivee","id":"025","service":0},
{"action":"arrivee","id":"026","service":0},
{"action":"arrivee","id":"027","service":3},
{"action":"arrivee","id":"028","service":2},
{"action":"arrivee","id":"029","service":2},
{"action":"arrivee","id":"030","service":2},
{"action":"arrivee","id":"031","service":2},
{"action":"arrivee","id":"032","service":4},
{"action":"arrivee","id":"033","service":4},
{"action":"arrivee","id":"034","service":4},
{"action":"arrivee","id":"035","service":4},
{"action":"contact","id":"4","source":"013","target":"002"},
{"action":"contact","id":"5","source":"009","target":"008"},
{"action":"contact","id":"6","source":"005","target":"007"},
{"action":"contact","id":"7","source":"009","target":"014"},
{"action":"fincontact","id":"7"},
{"action":"fincontact","id":"6"},
{"action":"fincontact","id":"5"},
{"action":"fincontact","id":"4"},
{"action":"fincontact","id":"3"},
{"action":"fincontact","id":"2"},
{"action":"fincontact","id":"1"},
{"action":"depart","id":"016"},
{"action":"depart","id":"015"},
{"action":"depart","id":"014"},
{"action":"depart","id":"013"},
{"action":"depart","id":"012"},
{"action":"depart","id":"011"},
{"action":"depart","id":"010"},
{"action":"depart","id":"009"},
{"action":"depart","id":"008"},
{"action":"depart","id":"007"},
{"action":"depart","id":"006"},
{"action":"depart","id":"005"},
{"action":"depart","id":"004"},
{"action":"depart","id":"003"},
{"action":"depart","id":"002"},
{"action":"depart","id":"018"},
{"action":"depart","id":"019"},
{"action":"depart","id":"020"},
{"action":"depart","id":"021"},
{"action":"depart","id":"022"},
{"action":"depart","id":"023"},
{"action":"depart","id":"024"},
{"action":"depart","id":"025"},
{"action":"depart","id":"026"},
{"action":"depart","id":"027"},
{"action":"depart","id":"028"},
{"action":"depart","id":"029"},
{"action":"depart","id":"030"},
{"action":"depart","id":"031"},
{"action":"depart","id":"032"},
{"action":"depart","id":"033"},
{"action":"depart","id":"034"},
{"action":"depart","id":"035"},
{"action":"depart","id":"001"}]
var vv = window,
w = vv.innerWidth,
h = vv.innerHeight;
foci = [{x: 500, y: 150}, {x: 200, y: 500}, {x: 700, y: 500}, {x: 400, y: 700}, {x: 600, y: 700}];
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var links = [], nodes = [];
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.size([w, h])
.friction(0.9)
.charge(-50)
.gravity(0.02)
.linkDistance(50)
.charge(-500)
.on("tick", tick);
var link;
var node;
var iter = 0;
var interval = 0.2;
var uptodate = true;
var nIntervId;
nIntervId = setInterval(function() {
//alert("je passe");
var action = readData();
addData(action);
if(!uptodate){
update();
}
}, interval*1000);
function addData(action) {
uptodate = false;
switch(action.action) {
case "arrivee":
nodes.push({id: action.id, service: action.service});
break;
case "depart":
for (var i = nodes.length - 1; i >= 0; i--) {
if(nodes[i].id == action.id){
nodes.splice(i, 1);
}
};
break;
case "contact":
var source;
var target;
for (var i = nodes.length - 1; i >= 0; i--) {
if(nodes[i].id == action.source){
source = nodes[i];
}
if(nodes[i].id == action.target){
target = nodes[i];
}
};
links.push({source:source, target:target, id:action.id});
break;
case "fincontact":
for (var i = links.length - 1; i >= 0; i--) {
if(links[i].id == action.id){
links.splice(i, 1);
}
};
break;
default:
uptodate = true;
}
}
function readData(){
var n = iter;
iter++;
var data = actions[n];
return data;
}
function update(){
force.start();
link = svg.selectAll(".link")
.data(force.links());
link.enter().append("line")
.attr("class", "link");
link.exit().remove();
node = svg.selectAll(".node")
.data(force.nodes());
node.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", 20)
.style("stroke-width", "10")
.style("fill", "white")//function(d, i) { return fill(d.service ); })
.style("stroke", function(d, i) { return d3.rgb(i & 1 ? "red" : "green") });
node.append("text")
.attr("text-anchor","middle")
.text(function(d) { return d.id });
node.exit().remove();
}
function tick(e) {
var k = 0.5 * e.alpha;
nodes.forEach(function(o, i) {
o.y += (foci[o.service].y - o.y) * k;
o.x += (foci[o.service].x - o.x) * k;
});
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 + ")"; });
};
Thank you for helping.

D3.js Image at corner issue

Hello im trying to implement this D3 project http://bl.ocks.org/929623:
with images like this one http://bl.ocks.org/950642:
But I can't make the source images to resize and move along with the nodes. Heres the code:
var nodesCreated = 1;
var newDistance = 100;
var width = document.documentElement.clientWidth,
height = document.documentElement.clientHeight,
fill = d3.scale.category20(),
nodes = [],
links = [];
var vis = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height);
vis.append("rect")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.linkDistance(newDistance)
.nodes(nodes)
.links(links)
.gravity(.01)
.size([width, height]);
force.on("tick", function() {
vis.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; });
vis.selectAll(".node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
var tempX = window.innerWidth/2;
var tempY = window.innerHeight/2;
var point = tempX,tempY,
node = {imgsrc: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-prn1/48799_806120304_700633127_n.jpg"};
n = nodes.push(node);
vis.on("mousedown", function() {
var point = d3.mouse(this),
node = {imgsrc: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-ash4/211698_100002756979859_374256176_n.jpg"},
n = nodes.push(node);
nodesCreated++;
console.log(nodesCreated);
var tempCounter = 0;
newDistance == 10;
force.linkDistance(newDistance);
nodes.forEach(function(target) {
if (/*Math.sqrt(x * x + y * y) < 100 ||*/ tempCounter == 0) {
links.push({source: node, target: target});
tempCounter++;
}
});
restart();
});
function restart() {
force.start();
vis.selectAll("line.link")
.data(links)
.enter().insert("line", ".node")
.attr("class", "link");
var realNode = vis.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
realNode.append("image")
.attr("xlink:href", function(d) { return d.imgsrc; })
.attr("x", -8)
.attr("y", -8)
.attr("width", 160)
.attr("height", 160);
}
I have been looking for some help at google but I found no solution.
You should add X and Y co-ordinates to your nodes:
var tempX = window.innerWidth/2;
var tempY = window.innerHeight/2;
var point = [tempX,tempY],
node = {imgsrc: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-prn1/48799_806120304_700633127_n.jpg", x: tempX, y: tempY};
and
var point = d3.mouse(this),
node = {imgsrc: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-ash4/211698_100002756979859_374256176_n.jpg", x:point[0], y:point[1]},
n = nodes.push(node);
And then need to add a transform to the force.on("tick".... function:
vis.selectAll(".node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ") scale(0.30)"; });
This scales your images down to 30%, but you can configure this.
For completeness, here is all of the code:
var nodesCreated = 1;
var newDistance = 100;
var width = document.documentElement.clientWidth,
height = document.documentElement.clientHeight,
fill = d3.scale.category20(),
nodes = [],
links = [];
var vis = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height);
vis.append("rect")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.linkDistance(newDistance)
.nodes(nodes)
.links(links)
.gravity(.01)
.size([width, height]);
force.on("tick", function() {
vis.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; });
vis.selectAll(".node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
vis.selectAll(".node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ") scale(0.30)"; });
});
var tempX = window.innerWidth/2;
var tempY = window.innerHeight/2;
var point = [tempX,tempY],
node = {imgsrc: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-prn1/48799_806120304_700633127_n.jpg", x: tempX, y:tempY};
n = nodes.push(node);
vis.on("mousedown", function() {
var point = d3.mouse(this),
node = {imgsrc: "https://fbcdn-profile-a.akamaihd.net/hprofile-ak-ash4/211698_100002756979859_374256176_n.jpg", x:point[0], y:point[1]},
n = nodes.push(node);
nodesCreated++;
console.log(nodesCreated);
var tempCounter = 0;
newDistance == 10;
force.linkDistance(newDistance);
nodes.forEach(function(target) {
if (/*Math.sqrt(x * x + y * y) < 100 ||*/ tempCounter == 0) {
links.push({source: node, target: target});
tempCounter++;
}
});
restart();
});
function restart() {
force.start();
vis.selectAll("line.link")
.data(links)
.enter().insert("line", ".node")
.attr("class", "link");
var realNode = vis.selectAll(".node")
.data(nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
realNode.append("image")
.attr("xlink:href", function(d) { return d.imgsrc; })
.attr("x", -8)
.attr("y", -8)
.attr("width", 160)
.attr("height", 160);
}​

Categories