3

I have a force directed graph with the ability to double click on a node and only that node and its neighbors will be shown, the rest given a hidden class -> visiblity:hidden

But this only works for one node. I have the ability to select multiple nodes and give them a selectedNode class.

Now what I wish to happen is to use this neighboring algorithm on all nodes with the class selectedNode. This is so all selected nodes and the edges connecting them are still shown and the unselected nodes and edges will be hidden.

Here is how I am showing/hiding the edges. Again, this only works for one node d3.select(this).node().__data__;. I have tried d = d3.selectAll("selectedNode").data(); but no luck :(

var linkedByIndex = {};//Create an array logging what is connected to what

for (i = 0; i < graph.data.nodes.length; i++) //-populate the array
{
    linkedByIndex[i + "," + i] = 1;
};


graph.data.edges.forEach(function (d) //-checks what nodes are related in array
{
    linkedByIndex[d.source.index + "," + d.target.index] = 1;
});


//-------------------------check if nodes are linked
function neighboring(a, b) //This function looks up whether a pair are neighbours
{
    return linkedByIndex[a.index + "," + b.index];
}

d = d3.select(this).node().__data__;


links.classed("hidden", function (o) {
            return d.index==o.source.index | d.index==o.target.index ? false : true;
        });

Added code

var links = inner.selectAll(".link").append("g")
    .data(force.links())
    .enter().append("line")
    .attr("id", "links")
    .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("marker-end",  "url(#arrow)") //adds arrows, main code on SVG append towards bottom
    .style("stroke-width", lineWidth)   
    ;
rekoDolph
  • 813
  • 1
  • 9
  • 31
  • could you provide a jsfiddle with a baerbone implementation? It is unclear what `this` refers to in that scope. – Renaud Dec 18 '14 at 13:26
  • See http://stackoverflow.com/questions/8739072/highlight-selected-node-its-links-and-its-children-in-a-d3-force-directed-grap – Lars Kotthoff Dec 18 '14 at 13:29
  • 1
    here is the fiddle of the basics as my code is nearly 1000 lines long. http://jsfiddle.net/reko91/skhhq62n/1/. and Lars I have looked at that but that only applies to one node/line. I want to do it to multiple nodes if possible ? – rekoDolph Dec 18 '14 at 13:36
  • See http://stackoverflow.com/questions/23386277/find-targets-target-i-e-friend-of-friend-in-force-directed-graph – Lars Kotthoff Dec 18 '14 at 13:50
  • @LarsKotthoff surely theres an easy way of applying this : links.classed("hidden", function (o) { return d.index==o.source.index | d.index==o.target.index ? false : true; }); to all the selected nodes rather than the one node ? – rekoDolph Dec 18 '14 at 14:00
  • I don't understand your question. Applying this to nodes that have been selected already is just calling the function on the selection, isn't it? – Lars Kotthoff Dec 18 '14 at 14:05
  • yeah so in my code it says : d = d3.select(this).node().__data__; ..... so this is for node that is being clicked on. I have tried passing the selected nodes data to it like : var d = d3.selectAll("selectedNode").data(); .. but this doesnt seem to work ? i dont think i understand it fully or im doing something wrong ? All i want to do is show the connections of the selected nodes, if any. so basically check if the edge has both a source and a target in the data and if it has both then show them, if not, hide them – rekoDolph Dec 18 '14 at 14:10
  • @reko your fiddle doesn't work – Renaud Dec 18 '14 at 14:17
  • @Reno lol i know it doesnt work ive just got the code i need for someone to help me with my question – rekoDolph Dec 18 '14 at 14:19
  • @reko you're missing the point, some of your variables are undeclared and you haven't even bothered including `d3` as a dependency. Please update your example so as to make it at least workable. – Renaud Dec 18 '14 at 14:24
  • can you not do it without looking at the rest of the code ? it means me uploading my json file and i cant do that atm. sorry :( – rekoDolph Dec 18 '14 at 14:33

1 Answers1

0

I think there a two problems here and since you're short on details I'm going to make assumptions.

1) You have a method to check if two nodes are neighbours. This is a good start but it's not enough, what you also need is a method to tell you if any given node is a selected node or one of it's neighbours. Let's assume you are maintaining a list of selected nodes in an array selected, here is what such a function would look like, in a naive implementation:

function shouldBeVisible(nodeIndex){
    // visible if either selected itself or neighbour of a selected node
    return selected.some(function(d){
        // assuming you don't care about the direction of your edges
        return linkedByIndex[nodeIndex+','+d]+linkedByIndex[d+','+nodeIndex];
    });
}

You can even overload this method to make it "edge friendly":

function shouldBeVisible(sourceIndex, targetIndex){
    if (targetIndex !== undefined)
        return shouldBeVisible(sourceIndex)
            && shouldBeVisible(targetIndex);
    else return selected.some(function(d){
        return linkedByIndex[sourceIndex+','+d]
             + linkedByIndex[d+','+sourceIndex];
    });
}

Note that if you're not maintaining selected nodes in a data structure you should be able to retrieve them without too much effort using d3.selectAll('.selectedNode'). You might want to implement a similar method for your edges.

2) You're only checking for neighbourhood against the first node in the selection instead of all nodes in the selection. Either way you shouldn't have to bother doing this assuming links accurately describes what it refers to.

Try something like this instead:

links.classed("hidden", function(d) {
    return !shouldBeVisible(d.source.index, d.target.index);
});
Renaud
  • 4,569
  • 7
  • 41
  • 72
  • wow. nice response :) cheers man. you say about d3.selectAll('.selectedNode'), i can do that for nodes but if i were to do that for the edges I would have to see if the edges source and target are classed as 'selectedNode' How would i go about that ? – rekoDolph Dec 18 '14 at 14:59
  • unless you class your edges as `.selectedEdge` or maintain a selected edges data structure I don't see an straightforward way to go about this. – Renaud Dec 18 '14 at 15:02
  • thats what I want to do, is give them a class but to do that you have to check its source and target nodes to see if they have a class of visible or hidden :/ :( – rekoDolph Dec 18 '14 at 15:07
  • I'm not sure I understand what you mean. What does your `links` variable refer to? Isn't that your links selection? If it is, then your answer is in 2) – Renaud Dec 18 '14 at 16:12
  • updated code @reno dont know if that helps :/ what i want to do is if the links source and target are visible show the link, if either of them are hidden dont show the link – rekoDolph Dec 18 '14 at 16:38