2012-06-15 14 views
18

D3 ha una varietà di layout per grafi orientati che sono alberi severe, come ad esempio il seguente:Come per il layout di una gerarchia non-albero con D3

A 
|\ 
B C 
/\ 
D E 

Ho bisogno di disegnare una gerarchia di nodi che non è un albero, ma è un grafo aciclico diretto. Questo è un problema per un layout albero, perché molti dei rami convergono:

A 
|\ 
B C 
\| 
    D 

Qualcuno sa di un layout D3 per le gerarchie generali? O in alternativa, qualche trucco intelligente per il treelayout esistente? Ho notato che GraphVis gestisce bene questa situazione, ma D3 produce un grafico che meglio si adatta alle esigenze qui.

+0

Si potrebbe voler dare un'occhiata al layout del grafo diretto della forza. –

risposta

1

Parlando in generale di alberi e la gerarchia dei dati, è solo bisogno di avere "D" nella lista dei bambini sia per la B e C.

Creazione lista dei nodi, assicuratevi di avere un ID univoco restituito in modo che "D" non appare due volte.

vis.selectAll("g.node").data(nodes, function(d) { return d.id; }); 

Quindi, quando chiamate

var links = tree.links(nodes) 

si dovrebbe ottenere D appare come il "bersaglio" due volte (con B e C come "sorgente" rispettivamente) che si traduce in due linee al singolo nodo "D".

+0

Sono stato in grado di assemblare l'albero nel modo che descrivi, ma TreeLayout non sembra in grado di gestirlo. C'è una possibilità che l'ho rovinato in qualche modo; hai avuto fortuna ad avere un layout per correre in questo modo? Nel frattempo, ho gestito un workaround abbastanza adatto utilizzando GraphViz sul lato server. GraphViz farà un bel layout gerarchico e uscirà in formato DOT con posizioni (x, y) per ogni nodo. Da lì, è relativamente facile raggruppare tali informazioni nella pagina e disegnarle usando D3. Grazie per il vostro aiuto, apprezzo lo sforzo! –

+0

Ho giocato con esso ed è stato piuttosto pazzesco. A seconda del genitore, a volte fallirebbe in d3: "vom is undefined: 6444" e altre volte sarebbe rendering, ma mettere il bambino in una brutta posizione. Quindi una risposta breve è, hai ragione. Il layout dell'albero nel caso è il problema. – Glenn

10

È possibile creare il proprio codice senza dover fare affidamento su un layout D3 per poterlo fare.

Ho fornito un example in a jsFiddle. L'esempio è piuttosto semplicistico e avrebbe bisogno di essere lavorato un po 'per accogliere esempi più complessi.

L'esempio può essere rielaborato per elaborare anche i dati gerarchici con uno sforzo relativamente ridotto.

Ecco il codice che ho usato nel jsFiddle:

// Sample data set 
var json = { 
    nodes: [{ 
     name: 'A'}, 
    { 
     name: 'B'}, 
    { 
     name: 'C'}, 
    { 
     name: 'D'}], 
    links: [{ 
     source: 'A', 
     target: 'B'}, 
    { 
     source: 'A', 
     target: 'C'}, 
    { 
     source: 'B', 
     target: 'D'}, 
    { 
     source: 'C', 
     target: 'D'} 
                        ] 

}; 

var vis = d3.select('#vis').attr('transform', 'translate(20, 20)'); 

// Build initial link elements - Build first so they are under the nodes 
var links = vis.selectAll('line.link').data(json.links); 
links.enter().append('line').attr('class', 'link').attr('stroke', '#000'); 

// Build initial node elements 
var nodes = vis.selectAll('g.node').data(json.nodes); 
nodes.enter().append('g').attr('class', 'node').append('circle').attr('r', 10).append('title').text(function(d) { 
    return d.name; 
}); 

// Store nodes in a hash by name 
var nodesByName = {}; 
nodes.each(function(d) { 
    nodesByName[d.name] = d; 
}); 

// Convert link references to objects 
links.each(function(link) { 
    link.source = nodesByName[link.source]; 
    link.target = nodesByName[link.target]; 
    if (!link.source.links) { 
     link.source.links = []; 
    } 
    link.source.links.push(link.target); 
    if (!link.target.links) { 
     link.target.links = []; 
    } 
    link.target.links.push(link.source); 
}); 

// Compute positions based on distance from root 
var setPosition = function(node, i, depth) { 
    if (!depth) { 
     depth = 0; 
    } 
    if (!node.x) { 
     node.x = (i + 1) * 40; 
     node.y = (depth + 1) * 40; 
     if (depth <= 1) { 
      node.links.each(function(d, i2) { 
       setPosition(d, i2, depth + 1); 
      }); 
     } 

    } 

}; 
nodes.each(setPosition); 

// Update inserted elements with computed positions 
nodes.attr('transform', function(d) { 
    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; 
}); 
+3

Grazie per l'esempio, Phil! Ho finito per fare qualcosa di molto simile. Risulta che l'algoritmo di layout che volevo veramente fosse implementato in GraphViz. Ha collegamenti Python, ma non funzionano.Invece ho fatto quanto segue: 1) Modulo grafico in linguaggio DOT (http://www.graphviz.org/content/dot-language) 2) Passare il grafo al comando punto di graphviz tramite riga di comando, che fa il layout e mette (x, y) coordinate in DOT 3) Riformattare DOT in oggetti javascript, incorporare nella pagina 4) Utilizzare D3 per posizionare i nodi in base alle coordinate (x, y) in DOT. Questo funziona davvero bene con grafici molto grandi. –

7

Come questo esempio: "Force Directed Trees" illustra c'è un trucco che spesso funziona. Nell'esempio, il comportamento della direzione della forza viene regolato su ciascun segno di spunta in modo tale che i nodi si spostino leggermente verso l'alto o verso il basso a seconda della direzione dei collegamenti. Come mostrato, questo farà un buon lavoro per gli alberi, ma ho trovato che funziona anche bene tollerabile per i grafici aciclici. Nessuna promessa, ma potrebbe essere d'aiuto.

Problemi correlati