I am trying implement labels on links in d3 v4 force layout. Although I am following code from a b.lock, I face difficulty to achieve the labelling. I am probably missing some basics at svg.selectAll and svg.append .
svg = d3.select("#svgdiv").append('svg').attr('width',width).attr('height',height).style("border","1px solid black"),
width = +svg.attr("width"),
height = +svg.attr("height");
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "black")
.style("pointer-events", "all")
.call(d3.zoom()
.scaleExtent([1 / 2, 4])
.on("zoom", zoomed));
g=svg.append("g");
function zoomed() {
g.attr("transform", d3.event.transform);
}
link = g.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.edge)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); })
.attr("id",function(d,i) {return 'edge'+i});
node = g.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.node)
.enter().append("circle")
.attr("r", radius)
.attr("fill", function(d) { return color(d.group); })
.on("click", mouseClick(.2))
.on("dblclick", mouseDblClick)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
Till here, the code is ok. But when I add the following code for labelling, it stops working.
edgepaths =svg.selectAll(".edgepath")
.data(graph.edge)
.enter()
.append('path')
.attr({'d': function(d) {return 'M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y},
'class':'edgepath',
'fill-opacity':0,
'stroke-opacity':0,
'fill':'green',
'stroke':'yellow',
'id':function(d,i) {return 'edgepath'+i}})
.style("pointer-events", "none");
edgelabels =svg.selectAll(".edgelabel")
.data(graph.edge)
.enter()
.append('text')
.style("pointer-events", "none")
.attr({'class':'edgelabel',
'id':function(d,i){return 'edgelabel'+i},
'dx':80,
'dy':0,
'font-size':10,
'fill':'white'});
edgelabels.append('textPath')
.attr('xlink:href',function(d,i) {return '#edgepath'+i})
.style("pointer-events", "none")
.text(function(d,i){return 'label '+i});
function ticked() {
node
.attr("cx", function(d) { return d.x;})
.attr("cy", function(d) { return 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; });
edgepaths.attr('d', function(d) {
var path='M '+d.source.x+' '+d.source.y+' L '+ d.target.x +' '+d.target.y;
//console.log(d)
return path});
edgelabels.attr('transform',function(d,i){
if (d.target.x<d.source.x){
bbox = this.getBBox();
rx = bbox.x+bbox.width/2;
ry = bbox.y+bbox.height/2;
return 'rotate(180 '+rx+' '+ry+')';
}
else {
return 'rotate(0)';
}
});
}
please help.
thank you.
JSON data.
{
"edge":[
{"source":"source1","target":"target1","value":"1"},
{"source":"source2","target":"target2","value":"0"},
.
.
],
"node":[
{"group":0,"id":"source1"},
{"group":3,"id":"source2"},
.
.
]
}
Related
I've been using the network graph example here:
https://bl.ocks.org/mbostock/4062045
I am trying to add text elements onto the nodes but can't seem to get it to appear next to the nodes.
I tried to add the following lines in the function below but athat this does it to display the text on the top left of the screen - while i'd expect it to appear on the top left.
Not sure if anyone could help?
var textElements = svg.append("g")
.selectAll('text')
.data(graph.nodes)
.enter().append('text').attr('font-size', 15)
.attr('dx', 15)
.attr('dy', 4)
.text(function(d) { return d.id; });
Original:
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("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
Found this guy that solved this:
myText.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
https://plnkr.co/edit/UwQfPscQiOg87IEMYtET?p=preview
This question already has an answer here:
Add text label to d3 node in Force layout
(1 answer)
Closed 5 years ago.
I am working on a simple visualisation with d3 to draw a force directed graph. I developed from the code at https://bl.ocks.org/mbostock/ad70335eeef6d167bc36fd3c04378048 and I have also added a marker. However, I am struggling to draw a text under each node. The code is as follows:
var nodes_url = "https://api.myjson.com/bins/1dedy1";
var edges_url = "https://api.myjson.com/bins/74lzt";
var marker = d3.select("svg").append('defs')
.append('marker')
.attr("id", "Triangle")
.attr("refX", 12)
.attr("refY", 6)
.attr("markerUnits", 'userSpaceOnUse')
.attr("markerWidth", 12)
.attr("markerHeight", 18)
.attr("orient", 'auto')
.append('path')
.attr("d", 'M 0 0 12 6 0 12 3 6');
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));
queue()
.defer(d3.json, nodes_url)
.defer(d3.json, edges_url)
.await(function(error, file1, file2) {createForceLayout(file1, file2);
});
function createForceLayout (nodes, links) {
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("line")
.style("stroke", "black")
.style("opacity", .5)
.style("stroke-width", "2px");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter()
.append("g")
.attr("class", "node")
.append("circle")
.attr("r", 5)
.style("fill", "lightgray")
.style("stroke", "black")
.style("stroke-width", "1px")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) { return d.id; });
node.append("text")
.style("text-anchor", "middle")
.attr("y", 15)
.text(function(d) {return d.id});
d3.selectAll("line").attr("marker-end", "url(#Triangle)");
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return 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;
}
The output returns the text, however, it is not displayed. Any thoughts on what can I be doing wrong?
Thanks a lot!
Right now, node is an "enter" selection for the circles, and you cannot append text to circles.
Solution: break the node selection, and change the ticked function accordingly:
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("gregorMendel")
.data(nodes)
.enter()
.append("g")
.attr("class", "node");
var circles = node.append("circle")
.attr("r", 5)
.style("fill", "lightgray")
.style("stroke", "black")
.style("stroke-width", "1px")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var titles = node.append("title")
.text(function(d) {
return d.id;
});
var text = node.append("text")
.style("text-anchor", "middle")
.attr("y", 15)
.text(function(d) {
return d.id
});
Here is the demo:
var nodes_url = "https://api.myjson.com/bins/1dedy1";
var edges_url = "https://api.myjson.com/bins/74lzt";
var marker = d3.select("svg").append('defs')
.append('marker')
.attr("id", "Triangle")
.attr("refX", 12)
.attr("refY", 6)
.attr("markerUnits", 'userSpaceOnUse')
.attr("markerWidth", 12)
.attr("markerHeight", 18)
.attr("orient", 'auto')
.append('path')
.attr("d", 'M 0 0 12 6 0 12 3 6');
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.queue()
.defer(d3.json, nodes_url)
.defer(d3.json, edges_url)
.await(function(error, file1, file2) {
createForceLayout(file1, file2);
});
function createForceLayout(nodes, links) {
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("line")
.style("stroke", "black")
.style("opacity", .5)
.style("stroke-width", "2px");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("gregorMendel")
.data(nodes)
.enter()
.append("g")
.attr("class", "node");
var circles = node.append("circle")
.attr("r", 5)
.style("fill", "lightgray")
.style("stroke", "black")
.style("stroke-width", "1px")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var titles = node.append("title")
.text(function(d) {
return d.id;
});
var text = node.append("text")
.style("text-anchor", "middle")
.attr("y", 15)
.text(function(d) {
return d.id
});
d3.selectAll("line").attr("marker-end", "url(#Triangle)");
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(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;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="300" height="200"></svg>
I created sankey layout in d3. tooltips added on links and nodes as shown in given Jfiddle http://jsfiddle.net/n7f2dkt0/39/
Issue is Node names disappears when user mouse hover on the node name for sankey layout on edge browser.how to resolve this issue?
screenshot for error
var link = svg.append("g").selectAll(".link")
.data(energy.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
link.on("mouseover", mouseoverLink)
link.on("mouseout", mouseoutLink);
var node = svg.append("g").selectAll(".node")
.data(energy.nodes)
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.call(drag);
node.append("rect")
.attr("height", function(d) { return d.dy; })
.attr("width", sankey.nodeWidth())
.style("fill", function(d) { return d.color = color(d.name.replace(/ .*/, "")); })
.style("stroke", function(d) { return d3.rgb(d.color).darker(2); })
node.append("text")
.attr("x", -6)
.attr("y", function(d) { return d.dy / 2; })
.attr("dy", ".35em")
.attr("text-anchor", "end")
.attr("transform", null)
.text(function(d) { return d.name; })
.filter(function(d) { return d.x < width / 2; })
.attr("x", 6 + sankey.nodeWidth())
.attr("text-anchor", "start");
node.on("mouseover", mouseover);
node.on("mouseout", mouseout);
Fiddle: http://jsfiddle.net/jzaun/SCb7T/
Code:
var width = 500,
height = 500;
var dotSize = 50;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-10020)
.linkDistance(dotSize)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var graph = {
"nodes":[
{"name":"Name","group":1},
{"name":"Name","group":2},
{"name":"Name","group":3},
{"name":"Name","group":4}
],
"links":[
{"source":1,"target":0,"value":1},
{"source":2,"target":0,"value":1},
{"source":3,"target":0,"value":1}
]
};
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", dotSize)
.style("fill", function(d) { return color(d.group); });
node.append("image")
.attr("xlink:href", "http://lorempixel.com/64/64/cats")
.attr("x", -32)
.attr("y", -32)
.attr("width", 64)
.attr("height", 64);
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; });
});
You can't append image to circle elements -- the SVG spec doesn't allow this. Instead, append the images directly and set transform on them in the tick function instead of cx/cy.
Complete example here.
Here is a JSFiddle of what I've done so far
The graph is not showing he nodes on load...I am not able to figure out what has gone wrong with the code...
var zoom = null; //the d3.js zoom object
var zoomWidgetObj = null; //the zoom widget draghandeler object
var zoomWidgetObjDoZoom = true;
var oldzoom = 0;
var w = 1060,
h = 800,
radius = d3.scale.log().domain([0, 312000]).range(["10", "50"]);
var color = d3.scale.category20();
var vis = d3.select("body").append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("idx", -1)
.attr("idsel", -1);
//d3.json(data, function(json) {
var force = self.force = d3.layout.force()
.nodes(data.nodes)
.links(data.links)
.distance(100)
.linkDistance(1)
.linkStrength(0.1)
.charge(-1000)
.size([w,h])
.start();
var link = vis.selectAll("line.link")
.data(data.links)
.enter().append("line")
.attr("class", "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; })
.style("stroke-width", function(d) { return Math.sqrt(d.value); })
.on("mouseover", function(d) {
var selection = d3.select(this);
var initialWidth = Number( selection.style("stroke-width") );
selection.transition().style("stroke-width", initialWidth + Number(1) )
.style("stroke-opacity", 1.0).duration(5)
.style("stroke","green")
})
.on("mouseout", function(d) {
var selection = d3.select(this);
selection.transition().style("stroke-width", getLinkStroke( selection.data()[0]))
.style("stroke-opacity", conf.link_def_opacity)
.style("stroke", "black")});
var node = vis.selectAll("g.node")
.data(data.nodes)
.enter().append("svg:g")
.attr("class", "node")
.attr("r", 4.5)
.call(force.drag)
.on("mousedown", function(d) {
d.fixed = true;
d3.select(this).classed("sticky", true)})
.on("mouseover", mouseover)
.on("mouseout", mouseout);
node.append("circle")
.attr("class", function(d){ return "node type"+d.type})
.attr("r", function(d) { return radius(d.value) || 10 })
.call(force.drag)
.style("stroke", "gray")
.attr('stroke', '#fff')
.attr('stroke-width', '2.5px');
node.append("svg:image")
.attr("class", "circle")
.attr("xlink:href", function(d){ return d.img_href})
.attr("x", "-16px")
.attr("y", "-16px")
.attr("width", "32px")
.attr("height", "32px");
node.append("svg:title")
.text(function(d) { return d.name; });
node.append("svg:text")
.attr("class", "nodetext")
.attr("dx", 16)
.attr("dy", ".35em")
.text(function(d) { return d.name });
node.select("circle").style("fill", function(d) { return d.name=="Salvation Army"?"white":"blue"; });
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 + ")"; });
});
function mouseover() {
d3.select(this).select("circle").transition()
.duration(75)
.attr("r", 16)
.style("fill", "red");
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(75)
.attr("r", 8);
}
One of your circles has a radius of NaN - this is most likely why they aren't rendering. Put a breakpoint where you're setting the radius and isolate the node / the cause of the NaN.