Can't add class to d3 svg image - d3.js

I am using a world flags sprite sheet from https://github.com/lafeber/world-flags-sprite to add flag images to my force-directed graph. When I follow the instructions it works just fine on an image element I created in html. I added the link to the head of my html file. When I tried it out on the img with the "hello" id, it worked fine. You can see it below.
<div class="f32">
<svg width="960" height="600"><img id="hello"/></svg>
</div>
d3.selectAll("#hello")
.attr("class", "flag us")
But the flag image is not appearing on the following
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("image")
.data(graph.nodes)
.enter().append("image")
.attr("id", "hello")
.attr("width", 200)
.attr("height", 200)
When I try setting the class to a single image element I created in d3, it still does not work:
svg.append("g").attr("transform", "translate(60, 60)")
.append("image")
.attr("id", "hello")

Sure, I ended up not using the sprite sheet, what I did was just use images that were available at http://flags.fmcdn.net. To get the right image to match each country I just input the country code that was available from my json api into the end of the url, just before .png.
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("image")
.data(graph.nodes)
.enter().append("image")
.attr("xlink:href", function(d){return "http://flags.fmcdn.net/data/flags/w580/"+d.code+".png"})
.attr("width", 30)

Related

Clippath doesn't work when dynamic id's are generated

I have implemented a bar chart with brush (D3.js V3). I have applied clip-path which is working fine for a hard-coded value (#clip).
But when I tried doing it with dynamic id its not working. when I click on a button which opens a model-box the height of bars is also clipped.
Image with model popup
Thanks in advance.
svg.append("defs")
.append("clipPath")
// .attr("id", container)
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var dev = '# '+ container ;
main.append("g")
.attr('clip-path', 'url(#clip)')// this is hard coded id
//.attr('clip-path', "url("+ dev +")")// this is dynamic id
.attr("class", "bars")

How to attach nodes and links to simulation force in d3js version 4?

I try to port to d4.v4.js some graph drawing built with version 3.
I quite don't understand how to attach svg nodes and links to the simulation with atlas force.
The simplified version using d3.v3 is shared on fiddle, note that all nodes are grouped in a <g> element, and each node, simplified as a circle in this example is inserted in a <g> element as well. Similarly, links are grouped in a <g> element. I need those to draw more complexe networks.
svg.append("g").attr("class", "links");
svg.append("g").attr("class", "nodes");
var force = d3.layout.force()
.gravity(.15)
.distance(100)
.charge(-1200)
.friction(0.8)
.size([width, height]);
// push initial sets of nodes and links
var nodes = force.nodes(), links = force.links();
Array.prototype.push.apply(nodes, data.nodes);
Array.prototype.push.apply(links, data.edges);
My reference for drawing a network with d3.v4 in shared on fiddle as well, it actually works, however each node is not in a <g> element as required (I guess) for my more complex graphs.
This last fiddle shows the d3.v4 version of the simplified graph, as one can see, nodes remain in the upper left corner, only zoom works.
var svg = d3.select("#chart").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink()
.id(function(d) { return d.id; })
.distance(function(d) { return (1-d.value*d.value*d.value)*20; }))
.force("charge", d3.forceManyBody().strength(-200))
.force("center", d3.forceCenter(width / 2, height / 2));
var pane = svg.append("g");
var link = pane.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) { return d.value*d.value*d.value*10; });
var node = pane.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.value*5 })
.style("fill", function(d) { return color(d.group); })
My question is how should I attach nodes (<g>) and links to "simulation"? What is wrong in my script?
Many thanks in advance for your help.
Let's take your last fiddle with the d3v4 implementation.
2 changes and it will work.
1) the ticked function to move the elements. Since nodes are svg groups now, you can't use cx and cy attributes (good for circles). You need to use transform translate like this:
node
.attr("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});
2) The node variable there contains only the update selection, since you called the enter selection nodeEnter. And since all elements are in the enter selection (just created), you don't move anything.
So you just need to merge the two selections like this:
nodeEnter.append("circle")
.attr("r", function(d) { return d.value*10 })
node.exit().remove();
node = nodeEnter.merge(node);
You can read more details in Mike Bostock documentation about this. Check also this and other similar bl.ocks.
To correctly target the links in ticked you should also merge enter and update link selections in the same way.
As a side note, you don't need to link source and target objects in the links as you do in getGraph function, since this is done by d3 simulation already. So you could just use your json object as data.
Here is a working fiddle.
Hope it is clearer how to upgrade d3 v3 force directed layout to d3 v4 force simulation :D

d3.js trying to call data method twice on same selector produces strange results

I'm new to D3 and am trying to build a table like structure out of rectangles. I would like the header to be a different color than the rest of the rectangles. I've written the following code:
table = svgContainer.selectAll('rect')
.data([managedObj])
.enter()
.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue")
.text(function(d) {
return d.name;
});
// create table body
table.selectAll('rect')
.data(managedObj.data)
.enter()
.append('rect')
.attr("y", function() {
shift += 20;
return shift;
})
.attr("width", 120)
.attr("height", 20)
.attr("fill", "red")
.text(function(d) {
return d.name;
});
This is producing the following results:
This is almost what I intended except it is nesting the second group of rectangles inside the first rectangle. This causes only the first blue rectangle to be visible. I'm assuming this has something to do with calling the data method twice. How can I fix this issue?
I think I understand the intended result, so I'll give it a go:
This line :
table.selectAll('rect')
is selecting the rectangle just created here:
table = svgContainer.selectAll('rect')....append('rect')....
You don't want to append rectangles to that rectangle (or any rectangle for that matter) because this won't work, but you do want to append them to the SVG itself.
So instead of table.selectAll you should be using svgContainer.selectAll, but there are two other issues:
if you use svgContainer.selectAll('rect') you will be selecting the rect you have already appended, when you actually want an empty selection. See the answer here.
you cannot place text in a rect (See answer here), instead you could append g elements and then append text and rect elements to those. And, for ease of positioning, you could translate the g elements so that positioning the rectangles and text is more straight forward.
So, your code could look like:
var data = ["test1","test2","test3","test4"];
var svgContainer = d3.select('body').append('svg').attr('width',900).attr('height',400);
var header = svgContainer.selectAll('g')
.data([data])
.enter()
.append('g')
.attr('transform','translate(0,0)');
header.append('rect')
.attr("width", 120)
.attr("height", 20)
.attr("fill", "blue");
header.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return "header";
});
// create table body
var boxes = svgContainer.selectAll('.box')
.data(data)
.enter()
.append('g')
.attr('class','box')
.attr('transform',function(d,i) { return 'translate(0,'+((i+1)*20)+')'; });
boxes.append('rect').attr("width", 120)
.attr("height", 20)
.attr("fill", "red");
boxes.append('text')
.attr('y',15)
.attr('x',5)
.text(function(d) {
return d;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

D3.js: circle color change on title mouseover doesn't work

I'm appending some text to D3.js circles and want the circles to change color mouseover, also on mouseover on the text.
Currently the circles do change color on mouseover, but when hovering over the text, the circle mouseover doesn't work anymore (logical: I'm hovering over the text). How do I get the circles to also change color when hovering over the text?
My code (gnode is a earlier defined circle):
var label = gnode.append("text")
.text(function(d) { return d.key ; })
.attr("font-size", function(d) {return 12 + d.value[0]/4})
.attr("text-anchor", "middle")
.call(force.drag)
.on("mouseover", function(d){
this.style.cursor='pointer';
d3.select( "#" + d.key.toLowerCase().replace(/ /g, '_'))
.attr("id", "none")
.classed("mouse_over",true)
.classed("mouse_out",false);
thanks
You can achieve this by simply putting all the elements belonging together inside a group. Then attach the mouse events to the group instead of the elements themselves.
First create svg element and append data:
var svg = d3.select("#main")
.append("svg")
.attr("id", "svgElement")
.attr("width", width)
.attr("height", height);
var svgg = svg.selectAll("g.myGroup")
.data(myData)
.enter()
.append("g");
Then add your elements via the each function:
svgg.each(function (d, i) {
selection = d3.select(this);
// ... append to this selection
});
Now attach mouse events to the group:
svgg.on("mouseover", function(d) {
d3.select(this) // Select and do something
});
You can find the working fiddle here:
http://jsfiddle.net/77XLD/1/
Note that the event fires when either moving over the line of the circle and also when hovering over the text.

D3.js: yAxis text bold and blur

I am trying to plot a customized yAxis using D3.js. My ideal result should be like this: JSFiddle.
But I haven't found the way to export the SVG image together with CSS styles. So I would like to move the styles to the SVG elements:
var yNode = svg.append("g")
.style("fill", "none")
.style("stroke", "#000")
.attr("transform", "translate(" + left + ",0)")
.attr("class", "axis")
.call(yAxis);
yNode.selectAll('text')
.style('font-size', '11px')
.style('font-family', 'Lucida Console');
The code is here: JSFiddle.
All the styles are the same. But somehow the yAxis label of the later one becomes blur and bold. How to fix it? Thanks.
Here it is : JsFiddle
You selected the whole axis dom element and applied the fill: none and stroke: black to it. The axis contains both: the path and the text, and both are going to be affected by the stroke. That's why your text is stroked.
So I commented out the fill & stroke here
var yNode = svg.append("g")
//.style("fill", "none")
//.style("stroke", "#000")
.attr("transform", "translate(" + left + ",0)")
//.attr("class", "axis")
.call(yAxis);
Selected the path and added the fill and stroke styles
yNode.select('path')
.style("fill", "none")
.style("stroke", "#000");

Resources