Is D3 Force Layout the best option for this graph? - d3.js

I would like to create a D3 graph like the image in this post.
Force Layout seem to be the best option, but, my nodes has to have different distances, and each node has to have different size.
So, should I insist on Force Layout?
I couldn't find any example similar to my problem, and it's being very hard to understand how to write down some code to implement those different distances and sizes.
The graph I want to make (it's my first question, so I don't have reputation to put an image directly in this post):
https://i.ibb.co/Tk0hHkv/toaskd3.png

Different link distances and different radii can be achieved in d3js.
You can add a radius property to each node, i.e. your nodes should look something like {r:5, id:1, ...}. Then, when its time to create corresponding svg elements, you can do something like
var circles = svg.append("g").selectAll("circle").data(nodes);
circles.enter().append("circle").attr("r", function(d) { return d.r; });
Similarly, you can add a dist property to each of your links. Then add a link force to your simulation like this:
var sim = d3.forceSimulation(nodes)
.force("link", d3.forceLink(links)
.distance(function(l){
return l.dist;
})
.strength(1)
);
For a working example, you can check this codepen I just created. You can always check the official API reference for detailed information.
In my opinion, D3's force layout is a very flexible option for depicting graphs.

Related

Avoid overlapping data points in d3 v4 scatterplot

I have been working on a d3 visualization in v4, which is a scatter/bubble plot with data points that have been filled with an image (see JS Bin link).
The problem I have is that a lot of the data points overlap, so I would like to have the overlapping points move until they are next to each other (the exact location of the points isn't too important).
I am new to d3 and have been struggling to get my head around simulation (collision detection, forceCollide etc) and would appreciate some help on how I can achieve this.
My attempts so far result in the initial x and y data points being ignored and the result is one big circle of non-overlapping circles. But what I'm after is the initial x and y values to be preserved and ONLY the overlapping circles be moved (so the outliers should still be outliers).
I've created an example on JS Bin (below) to demonstrate what I have. In particular, the force simulation code (when uncommenting simulation.stop(); ) seems to overwrite the initial x and y values.
I feel like I'm almost there but I'm doing something in the wrong order...
var simulation = d3.forceSimulation(data)
.force('charge', d3.forceManyBody().strength(3))
.force('collision', d3.forceCollide().radius(function(d) { return d.radius + 1 }) )
.on('tick', function() {
svg.selectAll('.node')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; })
})
simulation.stop();
JS Bin Example
I have a similar chart issue and was able to get the desired behavior by applying 3 forces: d3.forceX, d3.forceY to maintain the plotted points and then d3.forceCollide which treats each circle not as a point but a circle with a radius so they don't overlap.
You had a couple issues. First, utilizing simulation.stop() will stop the simulation from running and applying your force calculations. I believe simulation.stop() is used if you want to manually control the simulation using simulation.tick() see d3 docs for .stop(). Also, I don't think using d3.forceManyBody is needed, but if there are other posters with more experience on that I'd love see a discussion. Lastly, I positioned the simulation to run after your chart was initialized. The graph needed to be initialized, so you could then reference your circle nodes and apply the force layout(or so I believe, like I said, I'm still new to d3).
You can check out an altered JSbin here
This is an older questions with no answers, but I thought I'd chime in if it helps someone else, and maybe stir up discussion on some of the parts I'm not as clear on either. Hopefully that is what you were looking for.

How can D3js pack layout use data join by key for updates?

I want to be able to implement the D3 General Update Pattern III as a D3 pack layout. This means that each char would be inside a circle and that the char circles would be inside a larger enclosing circle. Everything should stay consistent with the GUPIII except that, of course, the positioning of the chars will be in a larger circle instead of on a horizontal axis.
I have tried to follow the same GUPIII structure including the important aspect of using a data join with key.
var alphaBubble = d3.layout.pack()
.size([diameter, diameter])
.padding(5);
var nodes = alphaBubble.nodes(data);
var vis = svg.selectAll("circle")
.data(nodes, function(d) { return d; });
But data join with key does not seem to work with the pack layout.
I'm having trouble getting my head around possible alternative mechanisms such as pack.value or nest. I have not been able to grasp whether they would somehow accomplish what is going on in GUPIII where incoming (enter) chars are green, existing (update) chars are black, exiting chars are removed.
Join by key is important in order to maintain the relative position of the char. I realize that this aspect of relative position is not as easy to see in the pack layout as it is in the linear axis layout, but I have a necessity for this which is not apparent in this example. I just need to successfully implement a "by key" join with pack that retains existing nodes when new data comes in.
Typically what I see with the pack layout is that either no nodes are replaced on data updates (maybe only attributes such as size are altered), or else all nodes are replaced with new incoming data, depending on the context. But I want to retain the nodes that are the same (by some key, in this case the char itself), remove the ones that are no longer present, and add the new ones "by key". Can anyone help me translate the D3 General Update Pattern II to a pack layout? I would include my full code attempt, but I really think just looking at the GUPIII is more clear if you are familiar with how the pack layout can facilitate join by key on update.
Thank you.
Ok, through much trial and error and study of existing examples I have been able to solve my question as to how to implement the D3 General Update Pattern III as a pack layout. I was not able to find any example that used data join by key, which is necessary for object constancy on update, so I'm hoping my example may provide some guidance for others who need the same pattern of behavior.
My solution is here. And my Javascript code is here.
I will point out a few important highlights. First, unlike in the linear non-pack version, the data join-by-key value must be accessed as an object property (i.e. d.id) instead of directly (i.e. d).
// Data join by key to <g> nodes
var node = svg.selectAll(".node")
.data(nodes, function(d) {
return d.id;
});
// Data join by key to circles
var circles = svg.selectAll("circle")
.data(nodes, function(d) {
return d.id;
});
Next, since the alphabet character is represented in a circle, both the circle and the char must be contained in a <g> element, i.e. node. And here was the trickiest part: the data needs to be joined by key separately to both the node and the circle elements. This is because the pack layout has performed the necessary calculation of scale (i.e. circle radii) and coordinate positions. So the circles need the data's calculated radii and the <g> node elements need the calculated coordinate positions. So a separate data join was required for each.
Once I figured this out, all of the necessary operations and references fell into place and the pack version of the General Update Pattern started working properly. Be sure to compare the linear and pack versions to each other.
The alphabetization sort is, I guess, unnecessary since the pack layout makes no accommodation for ordering. But I retained the sorting for consistency and because I wanted to observe its effect.

d3 bubble force layout with anchored centre node

I've been trying to implement a d3 bubble force layout (someone else's existing example here: http://jsfiddle.net/andycooper/PcjUR/1/) but with one added requirement...
I want the bubbles to collect around a centre node which will 1) be the largest node, and 2) shouldn't move. In order to achieve that, I'm hoping to simply create a new svg circle (i.e. avoid adding to the data set in the variable "nodes") that I can then somehow reference in the following method to prevent collisions with it....
function collide(alpha) {
...
}
Sorry for such an open-ended question, but does anyone know if this is possible? (or hopefully has been attempted somewhere before?) So far the only d3 bubble force layouts I've seen have shown ALL nodes with movement bound by force.
Really appreciate any advice!

D3 collapsible tree, change colour of knots and avoid line/text overlap

So here I am with this d3 tree:
http://plnkr.co/edit/HwcZecZtLor51cyNSGSL?p=preview
As you may see, the tree is a bit complex and some of the leave names may be quite long. My main questions are:
Can we change the colour of specific knots (some blue, some red)
and is there any way I can make it so that the text of a leave does not overlap with the link with the previous level? This happens especially when the link is a straight line.
My JS skills are lacking to say the least as these are my baby steps into this world and any help would be greatly appreciated.
Thanks!
The answer to your first question is easy. The colour of a node is set in line 90 in your example --
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
To change the colour, all you need to do is adapt this line. In the function that returns the colour, you have access to the data that you bind to the nodes, so you can use any of the data attributes to decide the colour. Note that only the fill colour is set here and not the outline stroke colour, but you could easily add .style("stroke", ...).
The answer to your second question is much more complex. The functionality you're asking for is not built in to D3, so you would have to do that yourself. Note that this would be quite a complex thing to do because you would have to figure out the positions and bounding boxes of the links and text elements dynamically. Doing this in a general fashion would be a major project.
I suggest that you experiment with the label placements to avoid overlap as much as possible. This will be much easier.

d3.js forced directed graph effects on enter

I use d3.js exactly as shown here but with different data value.
When the graph is first shown all elements are scattered and for around a second rapidly move towards their position. This looks nice in the sample, but for my data it does not look so nice. Any way to turn it off so that nodes start in their designated place? Any way to customize this entry visualization?
You can loop through the nodes array and set the .x/.y and .px/.py values to an initial position that you want the nodes in, and by doing so will limit the amount they need to move in their initial layout. If you turn off the force-directed algorithm (the tick() function) then you can apply standard .transtion().duration(500).attr("transform", xx) behavior to the nodes and links to make them animate in a manner you prefer before or after running the force-directed algorithm, but make sure you set the .x/.y and .px/.py attributes to their final position, otherwise they will be reset as soon as you .tick()

Resources