Saving and reloading a force layout using d3.js -


i trying find correct method able save out force diagram node layout positions once settled, later, reload layout , start again same settled state.

i trying cloning dom elements containing diagram, removing , reloading it.

this can do, in part indicated below:-

_clone = $('#chart').clone(true,true); $('#chart').remove(); 

selects containing div, clones , removes it, later

var _target = $('#p1content'); _target.append(_clone); 

selects div used hold chart , reloads it. reloaded diagram fixed.

i don't know how reconnect force allow manipulation carry on. possible? want preserve settled position of nodes.

another possibility, reload node positions , start force low alpha?

<!doctype html> <html lang="en"> <head>     <meta charset="utf-8">     <title>d3: force layout</title>     <script src="./jquery-2.0.3.min.js" type="text/javascript"></script>     <script type="text/javascript" src="../d3.v3.js"></script>     <style type="text/css">         /* no style rules here yet */     </style> </head> <body>      <div data-role="content" id="p1content">         <div id="chart"></div>     </div>     <script type="text/javascript">          //width , height         var w = 800;         var h = 600;          //original data         var dataset = {             nodes: [                 { name: "adam" },                 { name: "bob" },                 { name: "carrie" },                 { name: "donovan" },                 { name: "edward" },                 { name: "felicity" },                 { name: "george" },                 { name: "hannah" },                 { name: "iris" },                 { name: "jerry" }             ],             edges: [                 { source: 0, target: 1 },                 { source: 0, target: 2 },                 { source: 0, target: 3 },                 { source: 0, target: 4 },                 { source: 1, target: 5 },                 { source: 2, target: 5 },                 { source: 2, target: 5 },                 { source: 3, target: 4 },                 { source: 5, target: 8 },                 { source: 5, target: 9 },                 { source: 6, target: 7 },                 { source: 7, target: 8 },                 { source: 8, target: 9 }             ]         };          //initialize default force layout, using nodes , edges in dataset         var force = d3.layout.force()                              .nodes(dataset.nodes)                              .links(dataset.edges)                              .size([w, h])                              .linkdistance([100])                              .charge([-100])                              .start();          var colors = d3.scale.category10();          //create svg element         var svg = d3.select("#chart")                     .append("svg")                     .attr("width", w)                     .attr("height", h);          //create edges lines         var edges = svg.selectall("line")             .data(dataset.edges)             .enter()             .append("line")             .style("stroke", "#ccc")             .style("stroke-width", 1);          //create nodes circles         var nodes = svg.selectall("circle")             .data(dataset.nodes)             .enter()             .append("circle")             .attr("r", 10)             .style("fill", function(d, i) {                 return colors(i);             })             .call(force.drag);          //every time simulation "ticks", called         force.on("tick", function() {              edges.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; });              nodes.attr("cx", function(d) { return d.x; })                  .attr("cy", function(d) { return d.y; });          });  // after 5 secs clone , remove dom elements         settimeout(function() {                         _clone = $('#chart').clone(true,true);                         $('#chart').remove();         }, 5000); //after 10 secs reload dom         settimeout(function() {                         var _target = $('#p1content');                         _target.append(_clone);  // needs go here recouple force?                                }, 10000);      </script> </body> </html> 

added put // needs go here recouple force?
seems work picking existing elements restored , recouples force left off passing force nodes etc timeout function

force = d3.layout.force()     .nodes(dataset.nodes)     .links(dataset.edges)     .size([w, h])     .linkdistance([100])     .charge([-100])     .start();  colors = d3.scale.category10();  //create svg element svg = d3.select("#chart");  //create edges lines edges = svg.selectall("line")     .data(dataset.edges);  //create nodes circles nodes = svg.selectall("circle")     .data(dataset.nodes)     .call(force.drag);  //every time simulation "ticks", called force.on("tick", function() {      edges.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; });     nodes.attr("cx", function(d) { return d.x; })         .attr("cy", function(d) { return d.y; });  }); 

edit: full solution now!

furthermore, approach work in wide variety of scenarios -- both stop , restart layout on single page, , save , re-load layout on different page.

first, save original json graph @ end of layout process, can listen using:

force.on('tick', function(){     ... }).on('end', function(){     // run when layout has finished! }); 

saving valuable because x,y coordinates (and other things) have been added each node , edge d3, during layout (but keep changing, until comes halt). being json, graph easy serialize, stick in localstorage, pull out , parse again:

localstorage.setitem(json.stringify(graph)); ... localstorage.getitem(json.parse('graph')); 

once you've pulled out of storage though, don't want json object, want turn saved object svg, , ideally, using apparatus available d3.layout.force simplicity. , in fact, can -- with few small changes.

if stick saved graph right in, i.e. run

force   .nodes(graph.nodes)   .links(graph.links)   .start(); 

with saved graph, you'll 2 weird behaviors.

weird behavior 1, , solution

based on good documentation, including x , y coordinates in starting graph overrides random initialization of layout process -- initialization. you'll nodes should be, they'll float off uniformly distributed circle, layout ticks. keep happening, use:

  for(n in graph.nodes){     graph.nodes[n].fixed = 1   } 

before running force.start().

weird behavior 2, , solution nodes , edges both want them be, edges -- shrink?

something similar has happened, unfortunately, can't use same solution. edge lengths saved in json object, , used in initialization of layout, layout imposes default length (20) on them all, unless you, first, save edge lengths in json graph --

.on('end', function() {      links = svg.selectall(".link")[0]     for(i in graph.links){       graph.links[i].length = links[i].getattribute('length')     }     localstorage.setitem('graph', json.stringify(graph));  }); 

and then, before force.start() --

force.linkdistance(function (d) { return d.length }) 

(documentation can found here) , finally, graph it's supposed to.

in summary, if make sure json graph 1) has x,y coordinates on nodes, 2) has nodes set fixed=1, , 3) force has linkdistance set before .start(), can run exactly same layout process if initializing scratch, , you'll saved graph.


Comments

Popular posts from this blog

ios - UICollectionView Self Sizing Cells with Auto Layout -

node.js - ldapjs - write after end error -

DOM Manipulation in Wordpress (and elsewhere) using php -