Mupparthy Ravindranath Mupparthy Ravindranath - 8 days ago 5
Javascript Question

d3js radial tidy tree - how are dx and dy populated?

I am a beginner to d3js, playing around to get a very simple radial tidy tree to work.

I have written the following piece of code to generate a tree.

139 function radialTree(window, svg, data) {
140 debugger;
141 console.log("Will build radial tree here...");
142 var result = radTree.transform(data);
143 console.log("tree data:", result);
144
145 g = svg.append("g").attr("transform", "translate(" + (w / 2 + 40) + "," + (h / 2 + 90) + ")");
146
147 var stratify = d3.stratify()
148 .parentId(function(d) { return d.parent;});
149 var tree = d3.tree()
150 .size([360, 500])
151 .separation(function(a,b) { return (a.parent == b.parent?1:2)/a.depth; });
152 var root = d3.hierarchy(result);
153
154 var link = g.selectAll(".link")
155 .data(root.descendants().slice(1))
156 .enter().append('path')
157 .attr('class', 'link')
158 .attr('d', function(d) {
159 debugger;
160 return "M" + project(d.x, d.y)
161 + "C" + project(d.x, (d.y + d.parent.y)/2)
162 + " " + project(d.parent.x, (d.y + d.parent.y)/2)
163 + " " + project(d.parent.x, d.parent.y)
164 });
165
166 var node = g.selectAll(".node")
167 .data(root.descendants())
168 .enter().append('g')
169 .attr('class', function(d) { return "node" + (d.children?" node--internal":" node--leaf"); })
170 .attr('transform', function(d) { return "translate(" + project(d.x, d.y) + ")"; })
171
172 node.append('circle').attr('r', 2.5);
173 node.append('text')
174 .attr('dy', '.31em')
175 .attr('x', function(d) { return d.x < 180 === !d.children ? 6: -6; })
176 .style('text-anchor', function(d) { return d.x < 180 === !d.children ? "start":"end"; })
177 .attr('transform', function(d) { return "rotate(" + (d.x<180? d.x-90 : d.x+90) + ")"; })
178 .text(function(d) { return d.name; });
179
180 return svg;
181 }
182
183 function project(x, y) {
184 var angle = (x-90)/180 * Math.PI, radius = y;
185 return [radius*Math.cos(angle), radius*Math.sin(angle)];
186 }
187


Since, I have the tree data in a form that is generated by stratify (i guess so), I am not calling the stratify() function explicitly. The json data fed to the tree function is as below:

tree data: { parent: null,
name: 'root',
id: 'root',
status: 'green',
depth: 0,
children:
[ { parent: 'root',
name: 'authServer',
id: 'authServer',
status: 'green',
depth: 1,
children: [Object] },
{ parent: 'root',
name: 'dndServer',
id: 'dndServer',
status: [Object],
depth: 1,
children: [Object] },
{ parent: 'root',
name: 'accServer',
id: 'accServer',
status: 'green',
depth: 1,
children: [Object] },
{ parent: 'root',
name: 'contactPolicyServer',
id: 'contactPolicyServer',
status: 'green',
depth: 1,
children: [Object] },
{ parent: 'root',
name: 'DB',
id: 'DB',
status: undefined,
depth: 1 },
{ parent: 'root',
name: 'fileServer',
id: 'fileServer',
status: 'green',
depth: 1,
children: [Object] },
{ parent: 'root',
name: 'campaignServer',
id: 'campaignServer',
status: 'green',
depth: 1,
children: [Object] } ] }


When this gets rendered on browser, I see only one circle drawn, and the web console shows lot of errors, because the svg transformation functions have received a NaN value.

enter image description here

While trying to debug the issue, I found that the the "d" object that is passed to callback function, didn't have keys named 'x' or 'y'. The debug output of object 'd' looks like below:

break in ind.js:159
157 .attr('class', 'link')
158 .attr('d', function(d) {
>159 debugger;
160 return "M" + project(d.x, d.y)
161 + "C" + project(d.x, (d.y + d.parent.y)/2)
repl
Press Ctrl + C to leave debug repl
> d
{ data:
{ parent: 'root',
name: 'authServer',
id: 'authServer',
status: 'green',
depth: 1,
children: [ [Object], [Object] ] },
height: 1,
depth: 1,
parent:
{ data:
{ parent: null,
name: 'root',
id: 'root',
status: 'green',
depth: 0,
children: [Object] },
height: 2,
depth: 0,
parent: null,
children:
[ [Object],
[Object],
[Object],
[Object],
[Object],
[Object],
[Object] ] },
children:
[ { data: [Object], height: 0, depth: 2, parent: [Object] },
{ data: [Object], height: 0, depth: 2, parent: [Object] } ] }


I am wondering when, where and how the values of d.x and d.y get populated , so that they can be used safely in callback functions...

Answer

The d3.tree() function sets the x and y properties. According to the documentation, d3.tree():

Lays out the specified root hierarchy, assigning the following properties on root and its descendants:

  • node.x - the x-coordinate of the node
  • node.y - the y-coordinate of the node

Thus, since you defined your d3.tree():

var tree = d3.tree()
    .size([360, 500])
    .separation(function(a,b){
        return (a.parent == b.parent ? 1 : 2)/a.depth;
    });

And your hierarchy:

var root = d3.hierarchy(result);

The next step should be:

root = tree(root);

That will populate the x and y properties.

PS: I'm just answering your question ("how are dx and dy populated?"), not debugging your code.

Comments