2012-07-09 14 views
34

Sono nuovo su D3 e sto lavorando su un grafico a forza diretta dove i dati JSON sono dinamici. Sono in grado di cambiare il grafico della forza dopo aver ricevuto nuovi dati, ma ciò accade con un effetto di molla. Il codice che crea il mio grafico della forza è:Aggiornamento di collegamenti su un grafico forzato diretto da dati json dinamici

<div class="graph"></div> 
<script> 
var w = 660, 
    h = 700, 
    r = 10; 
var vis = d3.select(".graph") 
    .append("svg:svg") 
    .attr("width", w) 
    .attr("height", h) 
    .attr("pointer-events", "all") 
    .append('svg:g') 
    .call(d3.behavior.zoom().on("zoom", redraw)) 
    .append('svg:g'); 
vis.append('svg:rect') 
    .attr('width', w) 
    .attr('height', h) 
    .attr('fill', 'rgba(1,1,1,0)'); 
function redraw() { 
    console.log("here", d3.event.translate, d3.event.scale); 
    vis.attr("transform", "translate(" + d3.event.translate + ")" + 
          " scale(" + d3.event.scale + ")"); 
}; 

var force = d3.layout.force() 
    .gravity(.05) 
    .charge(-200) 
    .linkDistance(260) 
    .size([w, h]); 

var svg = d3.select(".text") 
    .append("svg") 
    .attr("width", w) 
    .attr("height", h); 

d3.json(graph, function(json) { 

    var nodeList = json.nodes; 
    var link = vis.selectAll("line") 
     .data(json.links) 
     .enter() 
     .append("line") 
     .attr("stroke-opacity", function(d) { 
      if(d.label == 'is a') { 
       return '0.8'; 
      } else { 
       return '0.2'; 
      }; 
     }) 
     .attr("stroke-width", function(d) { 
      if(d.value !== null) { 
       return d.value; 
      } else { 
       return 2; 
      }; 
     }) 
     .style("stroke", function(d) { 
      if(d.color !== null) { 
       return d.color; 
      }; 
     }) 
     .on("mouseover", function() { 
      d3.select(this) 
       .style("stroke", "#999999") 
       .attr("stroke-opacity", "1.0"); 
     }) 
     .on("mouseout", function() { 
      d3.select(this) 
       .style("stroke", function(d) { 
        if(d.color !== null) { 
         return d.color; 
        }; 
       }) 
       .attr("stroke-opacity", function(d) { 
        if(d.label == 'is a') { 
         return '0.8'; 
        } else { 
         return '0.2'; 
        }; 
       }) 
      }); 

    link.append("title") 
     .text(function(d) { return d.label });   

    var node = vis.selectAll("g.node") 
     .data(json.nodes) 
     .enter() 
     .append("svg:g") 
     .attr("class","node") 
     .call(force.drag); 

    node.append("svg:circle") 
     .attr("r", function(d) { 
      if (d.size > 0) { 
       return 10+(d.size*2); 
      } else { 
       return 10; 
      } 
     }) 
     .attr("id", function(d) { return "Node;"+d.id; }) 
     .style("fill", function(d) { 
      if(d.style == 'filled') { 
       return d.color; 
      }; 
     }) 
     .style("stroke", function(d) { 
      if(d.style !== 'filled') { 
       return d.color; 
      }; 
     }) 
     .style("stroke-width", "2") 
     .on("mouseover", function() { 
      d3.select(this).style("fill", "#999"); 
      fade(.1); 
     }) 
     .on("mouseout", function(d) { 
      if (d.style == 'filled') { 
       d3.select(this).style("fill",d.color);fade(1); 
      } else { 
       d3.select(this).style("stroke",d.color); 
       d3.select(this).style("fill","black"); 
      } 
      fade(1); 
     }); 

    node.append("title") 
     .text(function(d) { return d.Location; });   

    force.nodes(json.nodes) 
     .links(json.links) 
     .on("tick", tick) 
     .alpha(1) 
     .start(); 

    function tick() { 
    node.attr("cx", function(d) { return d.x; }) 
     .attr("cy", function(d) { return d.y; }) 
     .attr("transform", function(d) { 
      return "translate(" + d.x + "," + d.y + ")"; 
    }); 

    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; }); 
    } 

}); 
</script> 

io sono in grado di creare un nuovo grafico in caso di nuove stringa JSON ricevuto ricordando ancora una volta l'intera funzione. Questo crea un nuovo grafico al posto del vecchio. Non riesco ad aggiornare il vecchio grafico con il nuovo set di valori man mano che i valori vengono ricevuti; i nodi nel mio grafico non cambiano, cambia solo la relazione tra loro.

Ho fatto inciampare su un esempio (http://bl.ocks.org/1095795) in cui un nuovo nodo viene eliminato e ricreato, ma l'implementazione è leggermente diversa.

Qualsiasi suggerimento o aiuto sarà molto apprezzato.

risposta

55

Bene, ho trovato la soluzione che cercava, pubblicandola qui per chiunque avesse bisogno di aiuto su questo argomento. L'idea è di creare un oggetto del grafico e giocare con i nodi e le matrici dei collegamenti. Il codice JS va come:

var graph; 
function myGraph(el) { 

// Add and remove elements on the graph object 
this.addNode = function (id) { 
    nodes.push({"id":id}); 
    update(); 
}; 

this.removeNode = function (id) { 
    var i = 0; 
    var n = findNode(id); 
    while (i < links.length) { 
     if ((links[i]['source'] == n)||(links[i]['target'] == n)) 
     { 
      links.splice(i,1); 
     } 
     else i++; 
    } 
    nodes.splice(findNodeIndex(id),1); 
    update(); 
}; 

this.removeLink = function (source,target){ 
    for(var i=0;i<links.length;i++) 
    { 
     if(links[i].source.id == source && links[i].target.id == target) 
     { 
      links.splice(i,1); 
      break; 
     } 
    } 
    update(); 
}; 

this.removeallLinks = function(){ 
    links.splice(0,links.length); 
    update(); 
}; 

this.removeAllNodes = function(){ 
    nodes.splice(0,links.length); 
    update(); 
}; 

this.addLink = function (source, target, value) { 
    links.push({"source":findNode(source),"target":findNode(target),"value":value}); 
    update(); 
}; 

var findNode = function(id) { 
    for (var i in nodes) { 
     if (nodes[i]["id"] === id) return nodes[i];}; 
}; 

var findNodeIndex = function(id) { 
    for (var i=0;i<nodes.length;i++) { 
     if (nodes[i].id==id){ 
      return i; 
     } 
     }; 
}; 

// set up the D3 visualisation in the specified element 
var w = 500, 
    h = 500; 
var vis = d3.select("#svgdiv") 
    .append("svg:svg") 
    .attr("width", w) 
    .attr("height", h) 
    .attr("id","svg") 
    .attr("pointer-events", "all") 
    .attr("viewBox","0 0 "+w+" "+h) 
    .attr("perserveAspectRatio","xMinYMid") 
    .append('svg:g'); 

var force = d3.layout.force(); 

var nodes = force.nodes(), 
    links = force.links(); 

var update = function() { 
     var link = vis.selectAll("line") 
     .data(links, function(d) { 
      return d.source.id + "-" + d.target.id; 
      }); 

    link.enter().append("line") 
     .attr("id",function(d){return d.source.id + "-" + d.target.id;}) 
     .attr("class","link"); 
    link.append("title") 
    .text(function(d){ 
     return d.value; 
    }); 
    link.exit().remove(); 

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

    var nodeEnter = node.enter().append("g") 
     .attr("class", "node") 
     .call(force.drag); 

    nodeEnter.append("svg:circle") 
    .attr("r", 16) 
    .attr("id",function(d) { return "Node;"+d.id;}) 
    .attr("class","nodeStrokeClass"); 

    nodeEnter.append("svg:text") 
    .attr("class","textClass") 
    .text(function(d){return d.id;}) ; 

    node.exit().remove(); 
    force.on("tick", function() { 

     node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y   + ")"; }); 

     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; }); 
    }); 

    // Restart the force layout. 
    force 
    .gravity(.05) 
    .distance(50) 
    .linkDistance(50) 
    .size([w, h]) 
    .start(); 
}; 


// Make it all go 
update(); 
} 

function drawGraph() 
{ 
graph = new myGraph("#svgdiv"); 
graph.addNode('A'); 
graph.addNode('B'); 
graph.addNode('C'); 
graph.addLink('A','B','10'); 
graph.addLink('A','C','8'); 
graph.addLink('B','C','15'); 
} 
+1

@Rahul In sostanza si sta ridisegnare l'intero grafico? – Pramod

+2

Non sono in grado di capire questa risposta. qualcuno può aiutarmi a capirlo, in modo che io possa usarlo. –

+0

solo un paio di osservazioni, l'elemento che dovrebbe contenere lo svg è un parametro del costruttore di Graph, ma in questo caso viene utilizzato un valore hard coded. inoltre, la denominazione della funzione non è coerente in CamelCased, come removeallLinks. – glasspill

0

Oltre a chiamare drawGraph() nella funzione pronti, è anche possibile incorporare il codice pubblicato all'interno di una linea <script></script> blocco.

Ecco come la maggior parte delle esercitazioni sul sito d3 lo gestiscono.

12

Ho preso Rahuls un ottimo esempio, apportato alcune modifiche e pubblicato un bl.ock completo di animazione nel tempo se qualcuno è interessato a un esempio pienamente funzionante. Aggiungere/rimuovere collegamenti/nodi in realtà dovrebbe essere più facile di questo, ma comunque molto interessante.

http://bl.ocks.org/ericcoopey/6c602d7cb14b25c179a4

enter image description here

+3

Per quanto riguarda il codice del tuo blocco. Puoi evitare di usare Jquery per riordinare il DOM cambiando ** link.enter(). Append ("line") ** a ** link.enter(). Insert ("line", "g") ** – timebandit

Problemi correlati