so first, I have data in the format the same as miserables.json below
https://bl.ocks.org/mbostock/4062045
I would like to be able to make the link connected to my node red when I click the node.
So the psuedo⁻code would be like
selectAll(.links)
if links.source=nodeID or links.target=nodeID
then links.color=red
But I have not be able to do it. My ultimate goal would be intergrate it with Arc diagram below
http://bl.ocks.org/sjengle/5431779
Your pseudocode is a good start. You can use filter to implement the if condition in a selection. Note that the .source and .target of links are edited by d3, they are no longer the id of the nodes, but the node themselves:
thisNode = nodeObject; // where nodeObject is the javascript object for the node, it's probably called "d" in your function.
d3.selectAll(".link")
.filter(function(d) {
return (d.source === thisNode) || (d.target === thisNode);
})
.style("stroke", "red")
Expanding on top of #laurent's answer, to "reset" color of links that potentially were painted red during a previous interaction:
thisNode = nodeObject; // where nodeObject is the javascript object for the node, it's probably called "d" in your function.
d3.selectAll(".link")
.style("stroke",function(d) {
return d.source === thisNode || d.target === thisNode ? "red" : "#888888";
})
Related
For the following Fiddle I'd like to hide the root node. Any help is appreciated, to view the code please view the fiddle.
I imagine I would do something like the following but I'm unsure of how/where to implement it:
if (d.depth > 0) {
...node is drawn
}
Image below:
Not drawing it is not "drawing it with zero opacity" or "hidden display". It's actually not appending the element.
Therefore, the simplest option is removing it from the data array. Just filter out the first node:
nodes = nodes.filter(function(d){
return d.depth != 0;
})
As 0 is falsy, this is the same of:
nodes = nodes.filter(function(d){
return d.depth;
})
And also filter out all links from it:
links = links.filter(function(d){
return d.depth != 1;
})
Here is your updated fiddle: https://jsfiddle.net/wa21csbc/
Also, since those elements are not painted anymore, you can move the dataviz to the left, thus occupying the empty SVG space. That space is there because we're filtering out the first node after d3.tree() calculated the positions.
I am trying to select a branch of my tree and assign each (parent and two levels of children) a unique color. In my example picture below, parent nodes on the right and leafnodes on the left.
My hierarchical data is three levels deep (name:"Specialty" >> name:"Location" >> LeafNode Containing "Provider", "Location", "Specialty"). I'm thinking I need to check each LeafNode object as the path is drawn -
d.source.children.children.children.Specialty == d.source.children.name
but can't put this together to get it to work. As I draw the paths how might I assign a unique color to each branch?
var link = canvas.selectAll(".link")
.data(links)
.enter().append("path")
.attr("d", diagonal)
.attr("stroke-width", 1)
.attr("fill", "none")
.attr("stroke", function(d){
return color(d.target.name == d.target.name ? d : "#111" ); //<< huh?
})
Working with the data structure generated from the tree layout and your code, you can achieve what you're after with some if else statements; you could probably use the ternary operator, but the code would end up being quite terse and not as clear.
Anyway. as I said above you first need to set up a colour scale, which I've done using
d3.scale.category10()
You then need to calculate the the unique categorical variables for the colour scales domain, this could be done for he Speciality variable. The d3.set can do this for you, but first you need to get an array of of the Speciality variables.
var categoryArray = [];
csv.forEach(function(d,i) {
categoryArray[i] = d.Specialty;
});
colourDomain = d3.set(categoryArray)
.values();
Pass the unique array of specialities to the colour scale
colours.domain(colourDomain);
Now, all you need to do is set up the anonymous function to set the colour for the paths strokes. This is quite strait forward, and I'll admit not particularity elegant. Your link variable would become:
var link = canvas.selectAll(".link")
.data(links)
.enter().append("path")
.attr("class", "link")
.attr("stroke", function(d,i) {
if (d.source.depth === 1) {
return colours(d.source.name);
} else if (d.source.depth === 2) {
return colours(d.source.parent.name);
} else if (d.source.depth === 3) {
return colours(d.source.Specialty);
} else {
return "white";
}
})
.attr("d", diagonal);
Don't forget to remove the stroke attribute for the link class from your css file either.
Oh, this won't give you a unique colour for each category; it repeats the 10 colours in the scale. This might be OK for your application, otherwise setting up a colour scale to give you unique values is easily done in d3.
I'm trying to draw a network topology using D3sj force directed layout. I did a basic topology with link and node. However, now i want to show the interface name for each link on each node as the following picture. Could you guys guide me how i can do that?
Thank you in advance!
P/S I attached my topology here!1
Ideally you should present the javascript that you have already written, and explained what it does do, and what is missing. Having said that, I recently finished work on a similar project to what you describe, so had the following results handy.
Does this jsfiddle do what you are attempting?
There are two key components. The first is in defining the text elements (here I append them to an SVG element):
var text = svg.selectAll('text')
.data(force.nodes())
.enter().append('text')
.text(function (d) { return d.name });
Here I'm assuming that the nodes[] array contains objects with a .name property that is to be displayed.
The second component is to translate the text elements to their appropriate positions, inside the tick handler:
function tick () {
text.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
// Other code for nodes and links
}
See the jsfiddle for a complete working example, including commented code that should allow you to add images at the nodes if you want to try to reproduce your sample image more closely.
I am using the D3 force layout and adding code to allow the user to select individual nodes (here, select is not related to D3 select() but indicates the highlighted state of the node from the user's perspective). Only one node should be selected at one time. So, when the user clicks on a node which is not selected, I want to deselect any selected nodes.
I am using an attribute, selected, on the node and struggling to set that using the D3 data(...) or datum(...) method. In fact, I couldn't get it to work like that. What I have now seems kind of kludgy so I am hoping there is a cleaner way.
function deselectAll() {
var sc = d3.selectAll("circle")
.filter(function(d) {return (d.selected == "y")});
var circles = sc.data();
sc.transition()
.duration(50)
.style("stroke-width", "1px")
.style("stroke", "#3182bd");
if(null != circles && circles.length > 0) {
for(i=0; i<circles.length; i++) {
circles[i].selected = "n";
}
}
}
The problem is that adding .data("n") to the chain doesn't set 'selected' to "n". Is it possible to do that as part of the d3 chain?
Many selection (or transition) functions in d3 pass the current datum from each node to your defined functions. You can modify the contents of this datum at any time.
Using your example, if you want to set the nodes that have d.selected == "y" from "y" to "n" you can use your filtered selection like this:
sc.each(function(d) {
d.selected = "n";
});
This function will be invoked once for each node in the selection (each having "y" for selected as per your filter) so you can simply change the property value on the datum.
I am working on a force directed graph in D3. I want to highlight the mouseover'd node, its links, and its child nodes by setting all of the other nodes and links to a lower opacity.
In this example, http://jsfiddle.net/xReHA/, I am able to fade out all of the links and nodes then fade in the connected links, but, so far, I haven't been able to elegantly fade in the connected nodes that are children of the currently mouseover'd node.
This is the key function from the code:
function fade(opacity) {
return function(d, i) {
//fade all elements
svg.selectAll("circle, line").style("opacity", opacity);
var associated_links = svg.selectAll("line").filter(function(d) {
return d.source.index == i || d.target.index == i;
}).each(function(dLink, iLink) {
//unfade links and nodes connected to the current node
d3.select(this).style("opacity", 1);
//THE FOLLOWING CAUSES: Uncaught TypeError: Cannot call method 'setProperty' of undefined
d3.select(dLink.source).style("opacity", 1);
d3.select(dLink.target).style("opacity", 1);
});
};
}
I am getting a Uncaught TypeError: Cannot call method 'setProperty' of undefined error when I try to set the opacity on an element I loaded from the source.target. I suspect this is not the right way to load that node as a d3 object, but I can't find another way to load it without iterating over all of the nodes again to find the ones that match the link's target or source. To keep the performance reasonable, I don't want to iterate over all the nodes more than necessary.
I took the example of fading the links from https://bl.ocks.org/mbostock/4062006:
However, that doesn't show how to alter the connected child nodes.
Any good suggestions on how to solve or improve this will be furiously upvoted :)
The error is because you are selecting the data objects (d.source and d.target) rather than the DOM elements associated with those data objects.
You've got the line highlighting working, but I would probably combine your code into a single iteration, like this:
link.style("opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
Highlighting the neighboring nodes is harder because what you need to know the neighbors for each node. This information isn't that easy to determine with your current data structures, since all you have as an array of nodes and an array of links. Forget the DOM for a second, and ask yourself how you would determine whether two nodes a and b are neighbors?
function neighboring(a, b) {
// ???
}
An expensive way to do that is to iterate over all of the links and see if there is a link that connects a and b:
function neighboring(a, b) {
return links.some(function(d) {
return (d.source === a && d.target === b)
|| (d.source === b && d.target === a);
});
}
(This assumes that links are undirected. If you only want to highlight forward-connected neighbors, then eliminate the second half of the OR.)
A more efficient way of computing this, if you have to do it frequently, is to have a map or a matrix which allows constant-time lookup to test whether a and b are neighbors. For example:
var linkedByIndex = {};
links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
Now you can say:
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
And thus, you can now iterate over the nodes and update their opacity correctly:
node.style("opacity", function(o) {
return neighboring(d, o) ? 1 : opacity;
});
(You may also want to special-case the mouseovered link itself, either by setting a self-link for every node in linkedByIndex, or by testing for d directly when computing the style, or by using a !important css :hover style.)
The last thing I would change in your code is to use fill-opacity and stroke-opacity rather than opacity, because these offer much better performance.