Writing simple force layout app using D3.js - javascript

I am starting to learn D3.js and wanted to write a simple app using force layout. The goal is to create 3 nodes which are floating around and can be dragged using the mouse. This is how far I have gotten using the documentation, however all I see is a small black circle in top-left corner of my window (I assume all three are overlapping over there). I have put comments on each step - at least that's what I think they are doing.
<!doctype html>
<html>
<head>
<title>Simple Force Layout</title>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.29.1"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.geom.js?1.29.1"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js?1.29.1"></script>
</head>
<body>
<div id="canvas"></div>
<script type="text/javascript">
var conf = {
canvasWidth: 600,
canvasHeight: 400
}
var nodes = [
{ 'name': 'Node 1' },
{ 'name': 'Node 2' },
{ 'name': 'Node 3' }
];
// Add nodes to force layout and start it
var force = d3.layout.force()
.nodes(nodes)
.size([conf.canvasWidth, conf.canvasHeight])
.start();
// Create an svg element
var svg = d3.select("#canvas")
.append("svg:svg")
.attr("width", conf.canvasWidth)
.attr("height", conf.canvasHeight);
// Create a circle for each node
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", 6)
.call(force.drag);
</script>
</body>
</html>
My questions:
What am I missing? What else do I need to do for the nodes to float around and be draggable?
I would like to have a mix or circular and rectangular nodes (based on some attribute of the node). How do I do this?
When I run the application, I see the following errors in Firebug:
"NetworkError: 404 Not Found - http://mbostock.github.com/d3/d3.geom.js?1.29.1"
d3.geom.js?1.29.1
"NetworkError: 404 Not Found - http://mbostock.github.com/d3/d3.layout.js?1.29.1"
Why is this? Some force layout examples I tried are also giving this error, but they seem to be working fine!

I will try to answer your question by showing you examples that work.
The force layout has this particularity that it starts by displaying
all the nodes in the upper part of the screen. Since your nodes have
no links between them (if they have I don't see it in your code)
it's normal that they're drawn on top of each other. You can try to
fix this initial render by setting the X and Y attributes of the
node to a random position on the screen. As for the floating
problem: I am not 100% sure this will work, as I haven't tested it,
but you could try applying different forces to your nodes, or at
least at the corners of your display window. This will assure that
they move and don't get outside of the screen (or use the window's
bounding box). Also you need to check some force attributes related
to speed and the stability of the layout if you want to be sure the
nodes will always move, not just float for some time and remain in a
fixed position. For the nodes to be draggable call force.drag as in
http://bl.ocks.org/1095795
This example shows how to present nodes with different shapes in the
force layout: http://bl.ocks.org/1062383.
I would suggest the single file version of D3 that usually has a
file called d3v2 or something similar...It might that you can not
access those files because of a network problem or CORS problem or
something similar, meaning the browser will not deliver you the
file. You seem to use a very old version of D3 (one year old?). A
version that looks more like what you see in the next snippet will
probably not throw you that error: If you
can't use the online version just download the file and put it in
the same directory.
Play with the D3 examples a little bit and read the documentation.

Related

D3-Force-Quicker stabilization of nodes & transition of elements in projectiles

I have implemented the following version of the force diagram to show inter-cluster movement of nodes.
https://jsfiddle.net/Edwig_Noronha/67ey5rz0/
The nodes are grouped into four clusters. After the first initialization of the force diagram ends I call a function to transition the nodes from source to destination clusters.
function moveNodes() {
Object.keys(inputdata).forEach(function(key, index) {
svg.selectAll("circle.viewernodes" + index)
.each(function(d) {
d.type = d.destination;
});
});
viewersTransitioned = true;
force.start();
}
However, The stabilization of the first initialization of the force diagram takes about 35 seconds. Hence the transition happens after that much time.
Q1) is it possible to achieve a quicker stabilization of the force diagram with collision detection?
The transition of the nodes from source to destination clusters happens along a linear path.
Q2) Is it possible to make the nodes move along projectile paths?
To achieve quicker stabilization you can do one of two things in my experience.
Initialize the nodes X and Y values to be near to their end/goal state
Such as nodes[i].x = 500 etc, then calling the simulation start.
This would somewhat defeat the purpose of what you're trying to show in your example, unless you don't want the nodes to be shown moving to the groups and just be in them to begin with...
Stronger force
Have the force moving/pulling the nodes be stronger. This would require an essentially fundamental change to your approach to this example. Instead of just transitioning their positions, create custom forces within your force-layout that affect the appropriate nodes only based on their attributes. Place these forces in the center of your 'sorting circles' and they would attract the nodes appropriately.
See here for what something like this would look like: https://bl.ocks.org/mbostock/1021841

Unable to add Fisheye effect to Labels in Forced Labelled Layout using D3.js

I am facing difficulty incorporating the fisheye effect in my current labelled-force-layout D3 visualization of a dense network of URLs. I was able to make several changes to the existing code to apply the fisheye successfully to the nodes and connecting links but everything breaks/doesn't work when I try to use the code snippet for the attached node-labels.
This is the sample JSON file (not dense data) being used to populate the graph:
[{"url":"http:\/\/understandblue.blogspot.com\/","parentURL":"http:\/\/understandblue.blogspot.com\/","level":"1","category":"1"}, {"url":"http:\/\/paperfriendly.blogspot.com\/","parentURL":"http:\/\/understandblue.blogspot.com\/","level":"2","category":"1"}, {"url":"http:\/\/4pawsforever.org","parentURL":"http:\/\/understandblue.blogspot.com\/","level":"2","category":"3"}, {"url":"en.wikipedia.org","parentURL":"http:\/\/understandblue.blogspot.com\/","level":"2","category":"3"}, {"url":"http:\/\/test9.blogspot.com\/","parentURL":"http:\/\/understandblue.blogspot.com\/","level":"2","category":"2"}, {"url":"http:\/\/www.creativecommons.org","parentURL":"http:\/\/understandblue.blogspot.com\/","level":"2","category":"3"}, {"url":"http:\/\/someniceblog.typepad.com","parentURL":"http:\/\/understandblue.blogspot.com\/","level":"2","category":"2"}, {"url":"http:\/\/autismhelp.org","parentURL":"http:\/\/someniceblog.typepad.com","level":"3","category":"3"}]
This is the javascript code being used right now to read the JSON file, create the required nodes/links/labels and apply the fisheye.
JavaScript code generating the visualization
This is the html page:
<!DOCTYPE html>
<html>
<head>
<title>Visualization</title>
<!-- D3 Scripts --->
<!-- <script src="d3.v2.js"></script> --->
<script src="d3.js"></script>
<script src="d3.min.js"></script>
<script src="fisheye.js"></script>
<script src="drawVisual.js"></script>
</head>
<body>
<div id="forcedLayoutGraph">
</div>
</body>
</html>
I do not how to add the fisheye for the anchornodes/links in the code. Can someone please help me fix this?!
EDIT: I've updated the HTML code for the page. Following are the public links to all the JS files being used here. I tried creating a JSFiddle for the same but am unable to get it to work since I'm unable to provide the JSON file as an external resource (I don't know how to do that).
Links to relevant JavaScript and JSON Files:
GraphPage D3 D3 min fisheye drawVisual JSON db sample
This is how the visualization looks like right now:
Basically, with the current version of the code (that includes the force for the labels to the nodes), all the nodes and labels are drawn at the top-left corner of the page with the links somewhere around the middle. The fisheye effect works on the links but not for the node+labels.
You're setting the positions of the text and node elements separately, but all you need to do it set the positions of the g elements that they are contained in:
node.attr("transform", function(d) {
return "translate(" + d.fisheye.x + "," + d.fisheye.y + ")";
});
Complete example here.

D3 radial force layout

I am working on a d3 force layout which requires the nodes to be placed in such a way that there is a central node according to which all the other nodes are radially placed.The nodes are linked to each other like a normal force layout with appropriate source and target. The central node is dictating the position of all the other nodes, so essentially it is the source of all the nodes. Right now all I have been able to manage to do is to place them in a linear fashion using the linkDistance property with one node as the reference, but I need it in a radial manner. I could have shown an image but apparently my reputation is too low and I am not being allowed to post one.Can someone help me out with this?
Take a look at this example:
link to jsfiddle
Central (root) node has special treatment that makes it always remain in the center of the graph. On initialization, central node's property fixed is set to true, so that d3 force layout simulation doesn't move it. Also, it is placed in the center of rectangle containing layout:
root.fixed = true;
root.x = width / 2;
root.y = height / 2;
Hope this helps.

d3 force graph: sticky nodes

I would like to add a behavior to a force directed graph layout in D3 in such a way that once dropped, a dragged-and-dropped svg node sticks in its place, no longer changing position no matter what else happens in the graph. I have done some reading about this API but I can't figure out a way to get that one working.
The problem I am trying to solve is allowing a user to "pick apart" a complex force graph.
Set the fixed property of the node to true on mousedown.
node.on("mousedown", function(d) { d.fixed = true; });
For example: http://bl.ocks.org/3750558

Get the real size of a SVG/G element

Is there any accurate way to get the real size of a svg element that includes stroke, filters or other elements contributing to the element's real size from within Javascript?
I have tried pretty much everything coming to my mind and now I feel I'm coming to a dead end :-(
Updated question to add more context (Javascript)
You can't get the values directly. However, you can get the dimensions of the bounding rectangle:
var el = document.getElementById("yourElement"); // or other selector like querySelector()
var rect = el.getBoundingClientRect(); // get the bounding rectangle
console.log( rect.width );
console.log( rect.height);
It is supported at least in the actual versions of all major browser.
Check fiddle
Both raphael js http://dmitrybaranovskiy.github.io/raphael/ and d3 js http://d3js.org/ have various methods to find the size of an svg object or sets of svg object. It depends on if it's a circle, square, path, etc... as to which method to use.
I suspect you are using complex shapes, so in that case bounding box would be your best bet http://raphaeljs.com/reference.html#Element.getBBox
(Edit: updated reference site.) http://dmitrybaranovskiy.github.io/raphael/reference.html#Element.getBBox
Here is an example using D3.js:
Starting with a div:
<div style="border:1px solid lightgray;"></div>
The javascript code looks like this:
var myDiv = d3.select('div');
var mySvg = myDiv.append('svg');
var myPath = mySvg.append('path');
myPath.attr({
'fill': '#F7931E',
'd': 'M37,17v15H14V17H37z M50,0H0v50h50V0z'
});
// Get height and width.
console.log(myPath.node().getBBox());
If it is an SVG used as a CSS background image and you're using React you can use background-image-size-hook.
import { useBackgroundImageSize } from 'background-image-size-hook'
const App = () => {
const [ref, svg] = useBackgroundImageSize()
console.log(svg) // { width, height, src }
return <SVGBackgroundImageComponent ref={ref} />
}
You didn't specify any programming language. So I can suggest to use Inkscape.
In the file menu you find document's properties and in the first page there's "resize page to content" command. In this way you remove all the white space around your draw and you see the real size. After width and height values apprear inside the header of svg.
I know that Inkscape supports scripting and command line operations but I don't know if it's possible to do the trimming operatation in this way. But if it's possible you can do that from every programming language.

Categories