Plusieurs graphiques de mise en page avec d3 dans svg / div séparés

J'ai un problème avec la création de graphiques de mise en page de force multiples en utilisant d3 et la lecture de données à partir d'un fichier json. J'utilise une boucle for pour itérer sur les graphes, crée une division séparée contenant un svg pour chacune. Le problème est que la mise en page de la force n'est appliquée qu'au dernier créé, de sorte que les autres montrent simplement un point dans le coin supérieur gauche. Je pourrais le résoudre en partie en mettant une boucle for à la fin de chaque itération, mais je perds encore les capacités d'interaction des figures séparées.

Trouvez le code ci-dessous, merci d'avance.

Salut, Michael

var color = d3.scale.category20(); var force = new Array(); var div = new Array(); var svg = new Array(); var graph = new Array(); var link; var node; var width = 360; var height = 360; var brush = new Array(); var shiftKey; var count = 0; //loop through the different subsystems in the json-file for(name_subsystem in graphs) { //add a div for each subsystem div[count] = document.createElement("div"); div[count].style.width = "360px"; div[count].style.height = "360px"; div[count].style.cssFloat="left"; div[count].id = name_subsystem; document.body.appendChild(div[count]); //force is called. all attributes with default values are noted. see API reference on github. force[count] = d3.layout.force() .size([width, height]) .linkDistance(20) .linkStrength(1) .friction(0.9) .charge(-30) .theta(0.8) .gravity(0.1); div[count].appendChild(document.createTextNode(name_subsystem)); //create the svg rectangle in which other elements can be visualised svg[count] = d3.select("#"+name_subsystem) .on("keydown.brush", keydown) .on("keyup.brush", keyup) .append("svg") .attr("width", width) .attr("height", height) .attr("id",name_subsystem); brush[count] = svg[count].append("g") .datum(function() { return {selected: false, previouslySelected: false}; }) .attr("class", "brush"); //force is started force[count] .nodes(graphs[name_subsystem].nodes) .links(graphs[name_subsystem].links) .start(); //link elements are called, joined with the data, and links are created for each link object in links link = svg[count].selectAll(".link") .data(graphs[name_subsystem].links) .enter().append("line") .attr("class", "link") .style("stroke-width", function(d) { return Math.sqrt(d.thickness); }) .style("stroke", function(d){ if (d.linktype === 'reactant'){ return "black"; } else { return "red"; } }); //node elements are called, joined with the data, and circles are created for each node object in nodes node = svg[count].selectAll(".node") .data(graphs[name_subsystem].nodes) .enter().append("circle") .attr("class", "node") //radius .attr("r", 5) //fill .attr("fill", function(d) { if (d.type === 'metabolite') { return "blue"; } else { return "red"; } }) .on("mousedown", function(d) { if (!d.selected) { // Don't deselect on shift-drag. if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; }); else d3.select(this).classed("selected", d.selected = true); } }) .on("mouseup", function(d) { if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false); }) .call(force[count].drag() .on("dragstart",function dragstart(d){ d.fixed=true; d3.select(this).classed("fixed",true); }) ); //gives titles to nodes. i do not know why this is separated from the first node calling. node.append("title") .text(function(d) { return d.name; }); //enable brushing of the network brush[count].call(d3.svg.brush() .x(d3.scale.identity().domain([0, width])) .y(d3.scale.identity().domain([0, height])) .on("brushstart", function(d) { node.each(function(d) { d.previouslySelected = shiftKey && d.selected; }); }) .on("brush", function() { var extent = d3.event.target.extent(); node.classed("selected", function(d) { return d.selected = d.previouslySelected ^ (extent[0][0] <= dx && dx < extent[1][0] && extent[0][1] <= dy && dy < extent[1][1]); }); }) .on("brushend", function() { d3.event.target.clear(); d3.select(this).call(d3.event.target); }) ); //applies force per step or 'tick'. force[count].on("tick", function() { 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; }); node.attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }); }); //with this it works partly //for (var i = 0; i < 5000; ++i)force[count].tick(); count++; }; function keydown() { if (!d3.event.metaKey) switch (d3.event.keyCode) { case 38: nudge( 0, -1); break; // UP case 40: nudge( 0, +1); break; // DOWN case 37: nudge(-1, 0); break; // LEFT case 39: nudge(+1, 0); break; // RIGHT } shiftKey = d3.event.shiftKey || d3.event.metaKey; } function keyup() { shiftKey = d3.event.shiftKey || d3.event.metaKey; } 

Edit: a mis à jour le code après les commentaires, toujours le même problème.

Je travaille uniquement sur la mise en page de la force, avec de nombreux graphiques en même temps.

1 Vous n'avez pas besoin d'avoir une variable de compte pour chaque graphique.

2 Ne créez pas cette variable (force, svg, graphique) comme tableau. Il n'y a pas besoin de cela. Déclarez-les au-dessus comme (var svg;) et plus loin. Lorsque vous appelez la fonction, elle crée automatiquement sa copie différente et DOM les conserve séparément. Donc, chaque variable que vous utilisez en graphique vous permet de déclarer en haut de la fonction.

3 Vous tracez tous les graphiques au même moment, de sorte que le nouveau est appelé, le précédent s'arrête de faire sur svg, c'est pourquoi seul le dernier graphique a été construit avec succès. Alors dessinez-les après de petits intervalles de temps.

 <html> <script> function draw_graphs(graphs){ var color = d3.scale.category20(); var force; var div; var svg; var graph; var link; var node; var width = 360; var height = 360; var brush = new Array(); var shiftKey; //loop through the different subsystems in the json-file for(name_subsystem in graphs) { //add a div for each subsystem div = document.createElement("div"); div.style.width = "360px"; div.style.height = "360px"; div.style.cssFloat="left"; div.id = name_subsystem; document.body.appendChild(div); //force is called. all attributes with default values are noted. see API reference on github. force = d3.layout.force() .size([width, height]) .linkDistance(20) .linkStrength(1) .friction(0.9) .charge(-30) .theta(0.8) .gravity(0.1); div.appendChild(document.createTextNode(name_subsystem)); //create the svg rectangle in which other elements can be visualised svg = d3.select("#"+name_subsystem) .on("keydown.brush", keydown) .on("keyup.brush", keyup) .append("svg") .attr("width", width) .attr("height", height) .attr("id",name_subsystem); brush = svg.append("g") .datum(function() { return {selected: false, previouslySelected: false}; }) .attr("class", "brush"); //force is started force .nodes(graphs[name_subsystem].nodes) .links(graphs[name_subsystem].links) .start(); //link elements are called, joined with the data, and links are created for each link object in links link = svg.selectAll(".link") .data(graphs[name_subsystem].links) .enter().append("line") .attr("class", "link") .style("stroke-width", function(d) { return Math.sqrt(d.thickness); }) .style("stroke", function(d){ if (d.linktype === 'reactant'){ return "black"; } else { return "red"; } }); //node elements are called, joined with the data, and circles are created for each node object in nodes node = svg.selectAll(".node") .data(graphs[name_subsystem].nodes) .enter().append("circle") .attr("class", "node") //radius .attr("r", 5) //fill .attr("fill", function(d) { if (d.type === 'metabolite') { return "blue"; } else { return "red"; } }) .on("mousedown", function(d) { if (!d.selected) { // Don't deselect on shift-drag. if (!shiftKey) node.classed("selected", function(p) { return p.selected = d === p; }); else d3.select(this).classed("selected", d.selected = true); } }) .on("mouseup", function(d) { if (d.selected && shiftKey) d3.select(this).classed("selected", d.selected = false); }) .call(force.drag() .on("dragstart",function dragstart(d){ d.fixed=true; d3.select(this).classed("fixed",true); }) ); //gives titles to nodes. i do not know why this is separated from the first node calling. node.append("title") .text(function(d) { return d.name; }); //enable brushing of the network brush.call(d3.svg.brush() .x(d3.scale.identity().domain([0, width])) .y(d3.scale.identity().domain([0, height])) .on("brushstart", function(d) { node.each(function(d) { d.previouslySelected = shiftKey && d.selected; }); }) .on("brush", function() { var extent = d3.event.target.extent(); node.classed("selected", function(d) { return d.selected = d.previouslySelected ^ (extent[0][0] <= dx && dx < extent[1][0] && extent[0][1] <= dy && dy < extent[1][1]); }); }) .on("brushend", function() { d3.event.target.clear(); d3.select(this).call(d3.event.target); }) ); //applies force per step or 'tick'. force.on("tick", function() { 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; }); node.attr("cx", function(d) { return dx; }) .attr("cy", function(d) { return dy; }); }); //with this it works partly //for (var i = 0; i < 5000; ++i)force[count].tick(); }; function keydown() { if (!d3.event.metaKey) switch (d3.event.keyCode) { case 38: nudge( 0, -1); break; // UP case 40: nudge( 0, +1); break; // DOWN case 37: nudge(-1, 0); break; // LEFT case 39: nudge(+1, 0); break; // RIGHT } shiftKey = d3.event.shiftKey || d3.event.metaKey; } function keyup() { shiftKey = d3.event.shiftKey || d3.event.metaKey; } } </script> <script> $(document).ready(function() { draw_graphs("pass here the json file"); // this will drawn 2nd graph after 1 second. var t = setTimeout(function(){ draw_graphs("pass here json file"); }, 1000) }); 

Ici, le code que j'ai finalement utilisé avec l'aide des commentaires ci-dessus, peut-être utile pour les autres:

 <script type="text/javascript" src="d3_splitted_var.json"></script> <script> function draw_graphs(name_subsystem){ var force; var div; var svg; var link; var node; var width = 360; var height = 360; var r=5; var brush = new Array(); var shiftKey; //add a div for each subsystem div = document.createElement("div"); div.style.width = "360px"; div.style.height = "360px"; div.style.cssFloat="left"; div.id = name_subsystem; document.body.appendChild(div); force = d3.layout.force() .size([width, height]) .linkDistance(20) .linkStrength(1) .friction(0.9) .charge(-50) .theta(0.8) .gravity(0.1); div.appendChild(document.createTextNode(name_subsystem)); //create the svg rectangle in which other elements can be visualised svg = d3.select("#"+name_subsystem) .append("svg") .attr("width", width) .attr("height", height) .attr("id",name_subsystem); //force is started force .nodes(graphs[name_subsystem].nodes) .links(graphs[name_subsystem].links) .start(); //link elements are called, joined with the data, and links are created for each link object in links link = svg.selectAll(".link") .data(graphs[name_subsystem].links) .enter().append("line") .attr("class", "link") .style("stroke-width", function(d) { return Math.sqrt(d.thickness); }) .style("stroke", function(d){ if (d.linktype === 'reactant'){ return "black"; } else { return "red"; } }); //node elements are called, joined with the data, and circles are created for each node object in nodes node = svg.selectAll(".node") .data(graphs[name_subsystem].nodes) .enter().append("circle") .attr("class", "node") //radius .attr("r", r) //fill .attr("fill", function(d) { if (d.type === 'metabolite') { return "blue"; } else { return "red"; } }) .call(force.drag() .on("dragstart",function dragstart(d){ d.fixed=true; d3.select(this).classed("fixed",true); }) ); //gives titles to nodes. i do not know why this is separated from the first node calling. node.append("title") .text(function(d) { return d.name; }); //applies force per step or 'tick'. force.on("tick", function() { node.attr("cx", function(d) { return dx = Math.max(r, Math.min(width - r, dx)); }) .attr("cy", function(d) { return dy = Math.max(r, Math.min(height - r, dy)); }); 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; }); }); }; for(name_subsystem in graphs) { draw_graphs(name_subsystem); } </script> 

Remarque: les graphiques sont le nom de la variable dans mon fichier json. Vous devez inclure la bibliothèque d3.