Realtime Data in a Force Directed Layout

793 views
Skip to first unread message

wlindner

unread,
Jan 28, 2012, 11:54:57 AM1/28/12
to d3-js
I want to visualize realtime data in a force directed layout, but the
graph keeps "jumping around" whenever I call force.start() after
adding the new nodes to the graph.

Here's what I'm doing:

1. Ajax call to get data
2. Replace the current force.nodes() with the new data
3. Rendering svg:circle elements in the graph, adding/removing as
necessary
4. Calling force.start()

This works fine the first time that I render the data, but I want to
continuously go through this process to update it as more data comes
in. There are many times when the data will be exactly the same,
which is fine, the nodes shouldn't change until the data returned by
the ajax call is different, but even if the nodes have not changed,
when I call force.start(), everything jumps around the page as if they
were being rendered for the first time. Is there any way to keep the
graph from jumping around if I'm calling force.start() and nothing new
is being added?

Is there a more seamless way to add or remove nodes in a force
directed layout?

Mike Bostock

unread,
Jan 28, 2012, 12:27:51 PM1/28/12
to d3...@googlegroups.com
The graph jumps because calling force.start() resets the cooling
parameter, alpha. If your graph is showing realtime data, then you
probably want to disable cooling so that it is continuously active. To
do that, you can say:

d3.timer(force.resume);

Mike

wlindner

unread,
Jan 28, 2012, 1:41:26 PM1/28/12
to d3-js
So, do I need to call d3.timer(force.resume); right after I create the
force or every time I update the data? Should I no longer call
force.start()? I'm a little unsure about what d3.timer(force.resume);
does. As best I can tell from the documentation, it replaces the
animation timer with a custom one? And force.resume is an animation
timer without resetting the alpha parameter? Just looking for a
little further clarification.

wlindner

unread,
Jan 28, 2012, 1:43:52 PM1/28/12
to d3-js
Also, whenever I call d3.timer(force.resume); instead of
force.start(); I get this error: "Uncaught TypeError: Cannot read
property 'undefined' of undefined" on line 538 of d3.layout.js

Mike Bostock

unread,
Jan 28, 2012, 1:49:27 PM1/28/12
to d3...@googlegroups.com
> Should I no longer call force.start()?

You still need to call force.start() whenever you change the
definition of nodes and links.

> So, do I need to call d3.timer(force.resume); right after I create the
> force or every time I update the data?

Calling force.resume() resets the cooling parameter alpha. It makes
sure that the force layout's internal timer is still running. Calling
d3.timer(force.resume) repeatedly calls force.resume(), about every 20
ms depending in your frame rate. You only need to create the timer
once, and your graph will perpetually stay "hot".

Mike

wlindner

unread,
Jan 28, 2012, 2:37:02 PM1/28/12
to d3-js
I believe the issue is actually because I am resetting all of the 'x'
and 'y' attributes of all of the nodes when the new data comes in.
I'm also resetting it to the center of the graph, so they all get put
on top of each other at the same time and immediately repel each other
and jump around the graph.

I think what I need to do instead is get the 'x' and 'y' attributes of
the node if it already exists in the graph and set those values when
the new data comes in.

Mike Bostock

unread,
Jan 28, 2012, 2:44:00 PM1/28/12
to d3...@googlegroups.com
> I believe the issue is actually because I am resetting all of the 'x'
> and 'y' attributes of all of the nodes when the new data comes in.

The force layout should initialize starting positions for you
automatically. Can you use that (and just delete the corresponding
part of your code)?

Mike

wlindner

unread,
Jan 28, 2012, 3:11:20 PM1/28/12
to d3-js
Ok, so now what I'm doing is not adding 'x' and 'y' attributes if the
node is being added for the first time. That way d3 should be
assigning the 'x' and 'y' starting position attributes itself.

Then, I'm checking if the 'x' and 'y' attributes have already been
assigned when I load more data and I'm using that value that already
exists. If I don't do this, it looks like it automatically assigns
its own values that make the nodes jump around the graph. The only
problem is that the nodes start drifting even though I'm assigning the
same 'x' and 'y' attributes as were previously used. I'm guessing
this is because the cooling parameter alpha is being reset and the
graph gets "hot" again? Is this correct? I assume this is what your
original suggestion of using d3.timer(force.resume); to keep the graph
from cooling off. This does not seem to work for me though. Is there
another way to assign this value and call force.start() without the
nodes drifting?
Reply all
Reply to author
Forward
0 new messages