2012-06-15 7 views
18

D3 tiene una variedad de diseños para grafos dirigidos que son árboles estrictas, tales como las siguientes:Como disponer de una jerarquía no árbol con D3

A 
|\ 
B C 
/\ 
D E 

I necesidad de elaborar una jerarquía de nodos que no es un árbol, pero es un gráfico acíclico dirigido. Esto es un problema para un diseño de árbol, porque varias de las ramas convergen:

A 
|\ 
B C 
\| 
    D 

¿alguien sabe de un diseño D3 para jerarquías generales? O, como alternativa, ¿algún truco inteligente para la distribución de árboles existente? Me di cuenta de que GraphVis maneja bien esta situación, pero D3 produce un gráfico que se adapta mejor a los requisitos aquí.

+0

Es posible que desee echar un vistazo a la disposición del gráfico dirigido por la fuerza. –

Respuesta

1

Hablando en general, de los árboles y la jerarquía de datos, usted sólo tiene que tener "D" en la lista de los niños, tanto para B y C.

Creación de su lista de nodos, asegúrese de que tiene un identificador único devuelto de manera que "D" no aparece dos veces.

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

Luego, cuando se llama a

var links = tree.links(nodes) 

que debería obtener D que aparece como el "objetivo" dos veces (con B y C como la "fuente", respectivamente) que se traduce en dos líneas al nodo único "RE".

+0

Pude obtener el árbol ensamblado de la manera que describes, pero TreeLayout parece no ser capaz de manejarlo. Existe la posibilidad de que lo arruiné de alguna manera; ¿Has tenido suerte obteniendo un diseño para correr de esta manera? Mientras tanto, he logrado una solución bastante adecuada utilizando GraphViz en el lado del servidor. GraphViz hará un buen diseño jerárquico y saldrá en formato DOT con posiciones (x, y) para cada nodo. A partir de ahí, es relativamente fácil empaquetar esa información en la página y dibujarla usando D3. Gracias por su ayuda, agradezco el esfuerzo! –

+0

Jugué con eso y fue bastante loco. Dependiendo del padre, algunas veces fallaría dentro de d3: "vom no está definido: 6444" y otras veces lo renderizaría, pero pondría al niño en un lugar feo. Así que la respuesta corta es, tienes razón. El diseño del árbol en el caso es el problema. – Glenn

10

Puede crear su propio código sin tener que depender de un diseño D3 para hacerlo.

He proporcionado un example in a jsFiddle. El ejemplo es bastante simplista y necesitaría ser trabajado un poco para dar cabida a ejemplos más complejos.

El ejemplo podría volver a trabajarse para procesar datos jerárquicos también con relativamente poco esfuerzo.

Este es el código que he utilizado en el 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

¡Gracias por el ejemplo, Phil! Terminé haciendo algo muy similar. Resulta que el algoritmo de diseño que realmente quería se implementó en GraphViz. Tiene enlaces de python, pero no funcionan.En su lugar, hice lo siguiente: 1) Formulario de gráfico en el lenguaje DOT (http://www.graphviz.org/content/dot-language) 2) Pase el gráfico al comando de gráfico graphviz a través de la línea de comandos, que hace el diseño y pone (x, y) se coordina con el DOT 3) Reformatee el DOT en objetos javascript incrustados en la página 4) Utilice D3 para colocar los nodos según las coordenadas (x, y) en DOT. Esto funciona muy bien con gráficos muy grandes. –

7

Como este ejemplo: "Force Directed Trees" ilustra que hay un truco que trabaja a menudo. En el ejemplo, el comportamiento de la dirección de fuerza se ajusta en cada tic de modo que los nodos se desvíen ligeramente hacia arriba o hacia abajo dependiendo de la dirección de los enlaces. Como se muestra, esto hará un buen trabajo para los árboles, pero he encontrado que también funciona bien para gráficos acíclicos. Sin promesas, pero puede ayudar.

Cuestiones relacionadas