2013-05-31 14 views
14

Ho un diagramma di rete (grafico orientato alla forza), uno scatterplot e una tabella tutti interconnessi (vedere jsFiddle). Ho le interconnessioni che funzionano nel modo in cui le voglio per gli eventi mouseover. Vorrei modificare il mio codice in modo che quando topo il mouse su un nodo nel diagramma di rete, non solo sia evidenziato il nodo moused-over (e le sue connessioni nello scatterplot e nella tabella), ma anche i suoi nodi adiacenti immediati (come pure come le loro connessioni nello scatterplot e nel tavolo).applicare diversi eventi mouseover a nodi vicini (connessi)

Ho cercato le informazioni in Highlight selected node, its links, and its children in a D3 force directed graph per aiuto. Da qualche parte lungo la strada (non esattamente sicuro dove) ho trovato un esempio di una funzione che aiuta a definire i nodi connessi, isConnected().

function isConnected(a, b) { 
    return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index; 
    } 

mi piacerebbe inserire questa funzione nei miei eventi mouseover, magari con un if() dichiarazione, in modo che io possa fare tutti i "evidenziando" che voglio. Ma, sono nuovo a D3 e js e non sono sicuro di come impostarlo.

Di seguito è riportato lo snippet di codice (dallo jsFiddle) che desidero modificare. Apprezzerei qualsiasi suggerimento o suggerimento per altri esempi.

var node = svg.selectAll(".node") 
    .data(graph.nodes) 
    .enter().append("g") 
    .attr("class", function(d) { return "node " + d.name + " " + d.location; }) 
    .call(force.drag) 
    .on("mouseover", function(d) { 
     // I would like to insert an if statement to do all of these things to the connected nodes 
     // if(isConnected(d, o)) { 
      d3.select(this).select("circle").style("stroke-width", 6); 
      d3.select(this).select("circle").style("stroke", "orange"); 
      d3.select(this).select("text").style("font", "20px sans-serif"); 
      d3.selectAll("rect." + d.location).style("stroke-width", 6); 
      d3.selectAll("rect." + d.location).style("stroke", "orange"); 
      d3.selectAll("text." + d.location).style("font", "20px sans-serif"); 
      d3.selectAll("tr." + d.name).style("background-color", "orange"); 
      //} 
     }) 
    .on("mouseout", function(d) { 
     // if(isConnected(d, o)) { 
      d3.select(this).select("circle").style("stroke-width", 1.5); 
      d3.select(this).select("circle").style("stroke", "gray"); 
      d3.select(this).select("text").style("font", "12px sans-serif"); 
      d3.selectAll("rect." + d.location).style("stroke-width", 1.5); 
      d3.selectAll("rect." + d.location).style("stroke", "gray"); 
      d3.selectAll("text." + d.location).style("font", "12px sans-serif"); 
      d3.selectAll("tr." + d.name).style("background-color", "white"); 
      //} 
     }); 

risposta

5

In un altro scenario, inserisco i miei oggetti visivi in ​​una struttura di dati del grafico e lo sposto per aggiornare in modo efficiente gli elementi appropriati. Ma questo è d3, ma così faremo la stessa cosa ma invece di una struttura di dati del grafico che creeremo useremo le selezioni d3 (che possono essere come i grafici ma per questo saranno molto più simili agli array). Algoritmicamente questo approccio non sarà altrettanto efficiente, ma i nostri grafici sono piccoli.

Quindi, lavorando all'indietro, desidero una selezione che includa solo i nodi vicini del nodo selezionato . Lo farò selezionando tutti i cerchi e poi usando il metodo del filtro di selezione d3 per ridurlo solo a quei cerchi che sono vicini.

Naturalmente ho bisogno dell'elenco dei vicini, ma alcuni semplici metodi di array js ne riducono il funzionamento. Il relativo codice finale (nel passaggio del mouse) non è nemmeno così a lungo - ma ho aggiunto un po 'di commenti:

// Figure out the neighboring node id's with brute strength because the graph is small 
var nodeNeighbors = graph.links.filter(function(link) { 
    // Filter the list of links to only those links that have our target 
    // node as a source or target 
    return link.source.index === d.index || link.target.index === d.index;}) 
.map(function(link) { 
    // Map the list of links to a simple array of the neighboring indices - this is 
    // technically not required but makes the code below simpler because we can use   
    // indexOf instead of iterating and searching ourselves. 
    return link.source.index === d.index ? link.target.index : link.source.index; }); 

// Reset all circles - we will do this in mouseout also 
svg.selectAll('circle').style('stroke', 'gray'); 

// now we select the neighboring circles and apply whatever style we want. 
// Note that we could also filter a selection of links in this way if we want to 
// Highlight those as well 
svg.selectAll('circle').filter(function(node) { 
    // I filter the selection of all circles to only those that hold a node with an 
    // index in my listg of neighbors 
    return nodeNeighbors.indexOf(node.index) > -1; 
}) 
.style('stroke', 'orange'); 

si può provare anche il fiddle

credo che il concetto importante d3 rilevante è che quando si associano i dati a un elemento (in genere utilizzando i metodi data() o datum() sulle selezioni), allora i dati si attengono a quell'elemento e qualsiasi selezione futura lo utilizzerà sempre.

Per collegare altri aspetti è possibile estrarre tali attributi in modo simile e collegarli tramite d3.Ad esempio per i rettangoli di posizione che è possibile aggiungere al passaggio del mouse:

var nodeLocations = graph.links.filter(function(link) { 
     return link.source.index === d.index || link.target.index === d.index;}) 
    .map(function(link) { 
     return link.source.index === d.index ? link.target.location : link.source.location; }); 

d3.selectAll("rect").filter(function(node) { return nodeLocations.indexOf(node.location) > -1; }) .style("stroke", "cyan"); 
+0

Ciò è utile, ma si applica solo l'evidenziazione adiacente al diagramma di rete. Voglio anche evidenziare i bit corrispondenti nella tabella e nella mappa. Quindi, ad esempio, quando topo il mouse su GroupA, voglio che Jim, Sally e Tom abbiano (1) cerchi evidenziati nel diagramma di rete (il tuo codice li indirizza), (2) righe evidenziate nella tabella e (3) evidenziate rettangoli sulla mappa. Potete aiutarmi con le parti 2 e 3? –

+0

Sembra che i tuoi rettangoli e forse la tua tabella non associno i dati allo stesso modo di D3. Ma non vi è alcun motivo per cui non sia possibile utilizzare le selezioni d3 in ogni caso. Il trucco sarebbe utilizzare la matrice degli indici dei nodi adiacenti per ottenere effettivamente un elenco di nodi o di attributi corretti per evidenziare gli altri elementi. allora dovresti essere in grado di applicare la stessa logica. – Superboggly

+0

Puoi darmi un suggerimento su come farlo? Ho provato \t \t \t \t 'd3.selectAll ("rect." + D.location) .filter (funzione (nodo) { ritorno nodeNeighbors.indexOf (node.index)> -1; }) .Style (" ictus "," ciano ");' ma, ovviamente, questo non ha funzionato. –

0

Questa cosa ho costruito lo fa con la funzione Ego rete:

https://gist.github.com/emeeks/4588962

Aggiungere un .on ("mouseover", findEgo) per i nodi e il seguente dovrebbe funzionare, a patto dato che hai un qualche tipo di identificazione dell'attributo uid, che potresti generare quando carichi i nodi se non ne hai a portata di mano. È un po 'eccessivo, dal momento che consente reti di ego n-degree e crea una tabella aggregata per altre funzioni di analisi di rete, ma la funzionalità di base ti darà quello che vuoi e tu o altri utenti potresti trovare quell'aspetto utile:

function findEgo(d) { 
    var computedEgoArray = findEgoNetwork(d.id, 1, false,"individual"); 
    d3.selectAll("circle.node").style("fill", function(p) {return p.id == d.id ? "purple" : computedEgoArray.indexOf(p.id) > -1 ? "blue" : "pink"}) 
} 

function findEgoNetwork(searchNode, egoNetworkDegree, isDirected, searchType) { 
    var egoNetwork = {}; 
    for (x in nodes) { 
    if (nodes[x].id == searchNode || searchType == "aggregate") { 
    egoNetwork[nodes[x].id] = [nodes[x].id]; 
    var z = 0; 
    while (z < egoNetworkDegree) { 
    var thisEgoRing = egoNetwork[nodes[x].id].slice(0); 
    for (y in links) { 
    if (thisEgoRing.indexOf(links[y].source.id) > -1 && thisEgoRing.indexOf(links[y].target.id) == -1) { 
    egoNetwork[nodes[x].id].push(links[y].target.id) 
    } 
    else if (isDirected == false && thisEgoRing.indexOf(links[y].source.id) == -1 && thisEgoRing.indexOf(links[y].target.id) > -1) { 
    egoNetwork[nodes[x].id].push(links[y].source.id) 
    } 
} 
z++; 
} 
} 
} 
if (searchType == "aggregate") { 
//if it's checking the entire network, pass back the entire object of arrays 
return egoNetwork; 
} 
else { 
//Otherwise only give back the array that corresponds with the search node 
return egoNetwork[searchNode]; 
} 
} 
Problemi correlati