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
Post a Comment