Pre-calculate and set the starting positions of nodes in D3.js

I am trying to pre-compute the positions of a stable power oriented graph using igraph and transfer them to my d3.js. This is due to the size of the data set that I will use, which means that I cannot rely on the client so that it does not freeze if the calculation of the total force is performed on the client side. I have positions in JSON format and use linear scales to make them useful in d3.js.

var positions = {"positions": [{"x":"-68.824367374", "y": "-6.10824525755"}, {"x":"-80.8080803911", "y": "-3.38997541264"}, {"x":"6.75334817585", "y": "-49.6040729697"}, {"x":"14.6608797291", "y": "-81.8897019921"}, .... var force = d3.layout.force() .charge(-100) .linkDistance(3) .size([width, height]) .nodes(data.nodes) .links(data.links) .start(); var posX = d3.scale.linear() .range([0,960]) .domain([-125,120]); var posY = d3.scale.linear() .range([0,500]) .domain([-125,125]); 

And so I tried to do it. I experimented with px and py, but the results are the same. As if this code never ran. If I close console.log as shown below, the value will not be printed. This is regardless of where I put this code, whether before or after the launch of the force.

 force.on("start", function() { node .data(positions.positions) .attr("cx", function(d) { console.log(posX(dx)); return posX(dx); }) .attr("cy", function(d) { return posY(dy); }) }); 

Why doesn't the on start event set the starting positions of my nodes? It seems that they were initialized randomly. Alternatively, what is a good way to pre-calculate the stable state of the directional force graph d3.js? I took a look at this at Phantomjs, but gave up.

Thanks for taking the time to read my question!

EDIT

The following is an example: https://jsfiddle.net/xp0zgqes/ If you run it several times and pay attention to the starting positions of the nodes, you can see that they are accidentally initialized.

+5
source share
1 answer

Actually, I don’t like answering my own question, but I think it will be really helpful. First, (this may be specific only in my case), if the original location data comes from a different data object than the node / link data, this saves you a link to two data sources if you just combine them (or you can be smarter than me and just create an original json object with positions x and y). In my case:

 for (var i = 0; i < data.nodes.length; i++){ data.nodes[i].x = posX(positions.positions[i].x); data.nodes[i].y = posY(positions.positions[i].y); data.nodes[i].newAttribute = value; } 

Thanks to JSON magic, it's easy to add new attributes to good data, for example. if you want your nodes to be fixed.

The problem seems to be related to calling force.start() . If called upon initializing such a force:

 var force = d3.layout.force() .charge(-100) .linkDistance(3) .size([width, height]) .nodes(data.nodes) .links(data.links) .start(); 

the layout will randomly initialize the default nodes. The way I discovered that this happens is to add the on tick event and start the action later.

 var force = d3.layout.force() .charge(-100) .linkDistance(3) .size([width, height]) .nodes(data.nodes) .links(data.links) .on("tick", tick); force.start(); //Note, not chained //Standard code that goes in the on.tick event function tick() { 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; }); } 

And VoilΓ ! Now we have a fully functional force-oriented chart with start positions set from json.

Working fiddle: https://jsfiddle.net/zbxbzmen/

+4
source

Source: https://habr.com/ru/post/1247388/


All Articles