d3 javascript draw line over circle - javascript

I am new to d3 javascript library. I am trying to draw line over a circle using d3. I am able to create circle but somehow line does not appear on circle. See sample code attached. Any help is highly appreciated.
diag_circles.data(circle_data)
.enter()
.append("circle")
.attr("cx", function (d) {
console.log("d.x", d.x);
return d.x
})
.attr("cy", function (d) {
return d.y
})
.attr("r", function (d) {
return d.r
})
.append('line')
.attr("x1", function(d){return d.x- d.r})
.attr("y1", function(d){return d.y})
.attr("x2", function (d) { return d.x+ d.r})
.attr("y2", function(d){return d.y})
.attr("stroke-width", 20)
.attr("stroke", "black");
https://jsfiddle.net/c58859xy/

In a nutshell: you cannot append a line element to a circle element.
When creating your SVG, you have to know which elements allow appended children and what children they can have.
Solution: You'll have to append the lines to the SVG:
var lines = svg.selectAll('line')
.data(circle_data)
.enter()
.append("line")
.attr("x1", function(d){return d.x- d.r})
.attr("y1", function(d){return d.y})
.attr("x2", function (d) { return d.x+ d.r})
.attr("y2", function(d){return d.y})
.attr("stroke-width", 20)
.attr("stroke", "black");
Here is the updated fiddle: https://jsfiddle.net/c58859xy/1/

Related

d3.js forceSimulation() with zoom and drag (I can't add a text label with nodes' info) [duplicate]

This is my code look like, you can also have full code on JsFiddle .
I want to have labels on every node, but I can't.
By the way, labels can be embedded in the circle in the console.
var nodes = svg.selectAll("circle")
.data(dataset.nodes)
.enter()
.append("circle")
.attr("r", 10)
.style("fill", function(d, i){
return colors(i);
})
.call(force.drag);
var label = nodes.append("svg:text")
.text(function (d) { return d.name; })
.style("text-anchor", "middle")
.style("fill", "#555")
.style("font-family", "Arial")
.style("font-size", 12);
force.on("tick", function(){
edges.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; });
nodes.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
label.attr("x", function(d){ return d.x; })
.attr("y", function (d) {return d.y - 10; });
});
Right now, you are appending the text elements to the circle elements, and that simply won't work.
When you write...
var label = nodes.append("svg:text")
You're appending the texts to the nodesselection. However, you have to keep in mind what nodes is:
var nodes = svg.selectAll("circle")
.data(dataset.nodes)
.enter()
.append("circle")
Thus, you are appending the texts to circles, and that doesn't work. They show up when you inspect the page (as <circle><text></text></circle>), but nothing will actually show up in the SVG.
Solution: just change to:
var label = svg.selectAll(null)
.data(dataset.nodes)
.enter()
.append("text")
.text(function (d) { return d.name; })
.style("text-anchor", "middle")
.style("fill", "#555")
.style("font-family", "Arial")
.style("font-size", 12);
Here is the fiddle: https://jsfiddle.net/gerardofurtado/7pvhxfzg/1/

Add text label to d3 node in Force layout

This is my code look like, you can also have full code on JsFiddle .
I want to have labels on every node, but I can't.
By the way, labels can be embedded in the circle in the console.
var nodes = svg.selectAll("circle")
.data(dataset.nodes)
.enter()
.append("circle")
.attr("r", 10)
.style("fill", function(d, i){
return colors(i);
})
.call(force.drag);
var label = nodes.append("svg:text")
.text(function (d) { return d.name; })
.style("text-anchor", "middle")
.style("fill", "#555")
.style("font-family", "Arial")
.style("font-size", 12);
force.on("tick", function(){
edges.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; });
nodes.attr("cx", function(d){ return d.x; })
.attr("cy", function(d){ return d.y; });
label.attr("x", function(d){ return d.x; })
.attr("y", function (d) {return d.y - 10; });
});
Right now, you are appending the text elements to the circle elements, and that simply won't work.
When you write...
var label = nodes.append("svg:text")
You're appending the texts to the nodesselection. However, you have to keep in mind what nodes is:
var nodes = svg.selectAll("circle")
.data(dataset.nodes)
.enter()
.append("circle")
Thus, you are appending the texts to circles, and that doesn't work. They show up when you inspect the page (as <circle><text></text></circle>), but nothing will actually show up in the SVG.
Solution: just change to:
var label = svg.selectAll(null)
.data(dataset.nodes)
.enter()
.append("text")
.text(function (d) { return d.name; })
.style("text-anchor", "middle")
.style("fill", "#555")
.style("font-family", "Arial")
.style("font-size", 12);
Here is the fiddle: https://jsfiddle.net/gerardofurtado/7pvhxfzg/1/

D3.JS, How can I render multiple objects in the same data binding?

I have a data model which is an array of object with start time and end time.
Now I want to render gantt chart type cart with it.
It is easy to bind data and render a single line with it:
chart.data(myDataList).enter().append("line")
.attr("x1", function(d){return d.x})
.attr("y1", lineHeight)
.attr("x2", function(d){return d.y})
.attr("y2", lineHeight)
The data could be like
myDataList = [ [start time, end time],
[start time 2, end time 2],
[start time 3, end time 3]]
Now I need to render something like this with each data item:
O-------------------O
That is addition to the line, there will be circle in both end of the line.
Circle cx data will be coming from d.x and d.y.
However, I am not quite sure how to bind the same data element in three elements.
Any help?
First you make the line:
chart.data(myDataList).enter().append("line")
.attr("x1", function(d){return d.x})
.attr("y1", lineHeight)
.attr("x2", function(d){return d.y})
.attr("y2", lineHeight)
now make circles
//make inner circle
chart.selectAll(".in").data(dataset).enter()
.append("circle")
.attr("class", "in")
.attr("cx", function(d) {
return xScale(d[0])
})
.attr("cy", function(d) {
return xScale(d[0])
})
.attr("r", 2);
//make outer circle
chart.selectAll(".out").data(dataset).enter()
.append("circle")
.attr("class", "out")
.attr("cx", function(d) {
return xScale(d[1])
})
.attr("cy", function(d) {
return xScale(d[0])
})
.attr("r", 2);
Working code here
First,cache the line code in a variable.
var line=chart.data(myDataList).enter().append("line")
.attr("x1", function(d){return d.x})
.attr("y1", lineHeight)
.attr("x2", function(d){return d.y})
.attr("y2", lineHeight)
Then,now using the variable,we can add circles using the same data element as
line.append("circle")
.attr("cx", function(d){return d.x})
.attr("cy", lineHeight)
.attr("r", 4);//First circle
now,the second circle ...
line.append("circle")
.attr("cx", function(d){return d.y})
.attr("cy", lineHeight)
.attr("r", 4);//Second circle

Wrong positioning of D3 nodes

I have a simple force layout that calculates nodes and links when certain buttons are clicked. The first time the nodes are calculated and displayed everything is correctly positioned. However, when nodes are recalculated after another click, the position of the circles I have appended to the nodes are way off yet the text I added remains in the right place. Here's my JS:
//Compute Nodes and Links
var data = this.computePreviewNodes($(e.currentTarget).data("id"), $(e.currentTarget).data("type"));
var canvas = d3.select(".body").append("svg")
.attr("width", width)
.attr("height", screen.height/2)
.append("g");
canvas.append("text")
.text(compObj.name)
.attr("text-anchor", "middle")
.attr("font-size", "2em")
.attr("x", width/2)
.attr("y", 40);
var force = d3.layout.force()
.nodes(data.nodes)
.links(data.links)
.gravity(.05)
.distance(100)
.charge(-10)
.size([width, screen.height/2]);
var links = canvas.selectAll(".links")
.data(data.links)
.enter().append("line")
.attr("class", "links")
.attr("fill", "none")
.attr("stroke", "blue");
var nodes = canvas.selectAll(".nodes")
.data(data.nodes)
.enter()
.append("g")
.attr("class", "nodes")
.call(force.drag);
nodes.append("circle")
.attr("cx", function(d) {return d.x;})
.attr("cy", function(d) {return d.y;})
.attr("r", 10)
.attr("fill", "green");
nodes.append("text")
.text(function(d) {return d.name})
.attr("text-anchor", "right")
.attr("font-size", "1.8em")
.attr("y", 5);
force.on("tick", function(e) {
nodes
.attr("transform", function(d, i){
return "translate(" + d.x + ", " + d.y + ")";
});
links
.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;})
})
force.start();
My computePreviewNodes() function simply comes up with what nodes need to be displayed based on which button is clicked. My thoughts are that maybe I'm not updating my node positions correctly after the second rendering of my nodes. Any ideas?
Here's my canvas at the first click:
And here it is when I click/calculate my nodes once again:

D3: Attach text to circle such that it has same priority as circle object

I am able to add text to my sketch, but I would like it if I could make my text attached directly to the circle. This means that if a circle gets over-written by another circle, the text will as well. On a higher level not, I am finding the d3 model hard for constructing objects in a way that makes them composable with different shapes, etc. The code seems very procedural to mean so any tips would be greatly appeciated :)
JSFiddle link
var link = "https://api.github.com/orgs/csci-4830-002-2014/repos"
d3.json(link, function(error, data) {
var w = 10000;
var h = 1000;
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.selectAll("line")
.data(data)
.enter()
.append("line")
.attr("x1", 5)
.attr("y1", 5)
.attr("x2", function (d,i){
return 30*d.forks_count;
})
.attr("y2", function (d,i){
return 30*d.open_issues_count;
})
.attr("stroke-width", 2)
.attr("stroke", "black");
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 40)
.attr("cx", function(d){ return 30*d.forks_count; })
.attr("cy", function(d){ return 30*d.open_issues_count; })
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("fill", "white")
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("dx", function(d,i){ return 30*d.forks_count; })
.attr("dy", function(d,i){ return 30*d.open_issues_count; })
.text(function(d){
if (d.name.indexOf("challenge") != -1)
return "C";
else
return "H";
});
});
With the way your code written right now, all the lines will be added first, then all the circles, and finally the texts. SVG will always put elements added later on top. So to achieve what you want, you will need to group them up. To do this, you will need to add a g element for each element of your data
var element = svg.selectAll(".element")
.data(data)
.enter()
.append("g")
.attr("class","element");
Now you can add the line, circle, and text to it
element.append("line")
.attr("x1", 5)
.attr("y1", 5)
.attr("x2", function (d, i) {
return 30 * d.forks_count;
})
.attr("y2", function (d, i) {
return 30 * d.open_issues_count;
})
.attr("stroke-width", 2)
.attr("stroke", "black");
element.append("circle")
.attr("r", 30)
.attr("cx", function (d) {
return 30 * d.forks_count;
})
.attr("cy", function (d) {
return 30 * d.open_issues_count;
})
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("fill", "white")
element
.append("text")
.attr("dx", function (d, i) {
return 30 * d.forks_count;
})
.attr("dy", function (d, i) {
return 30 * d.open_issues_count+6;
})
.style("text-anchor", "middle")
.text(function (d) {
if (d.name.indexOf("challenge") != -1) return "C";
else return "H";
});
You can check the updated JSFiddle at http://jsfiddle.net/9tp1yun7/2/

Categories