I'm using d3.v4 for this. When I have
var treemap = d3.tree().size([height, width])
the tree is nicely centred
but (because I want to specify the vertical spacing between nodes) when I change this to
var treemap = d3.tree().size([height, width]).nodeSize([40]);
the tree moves to the top left:
Does anyone know why this might be happening?
I believe that d3.tree().size()
and d3.tree().nodeSize()
are exclusive in relation to one another. Setting one nullifies the other. The v3 documentation (here) is explicit in this regard, while the v4 documentation (here) might suggest that this is still true. Quick testing suggests v4 is the same as v3 in regards to this. (see also: this answer, or this one.)
More importantly, "When a node size is specified, the root node is always positioned at ⟨0, 0⟩." (API documentation).
When using size
, the positioning of each node takes advantage of available space - positioning in relation to the specified width/height with no consideration for node density. When using nodeSize
, positioning is relative to other nodes, not the available area for the diagram (though it will be anchored at [0,0]). By initially using .size
, you didn't have to worry about positioning. Using .nodeSize
, you'll have to set a transform for your nodes so that they are centered properly.
To position it properly, you'll need to set a transform on your container so that [0,0], and the root node, is more appropriately placed (as in the margin-less example below, at [0,height/2]):
var data = { "name": "Parent", "children": [ { "name": "Child A", "children": [ { "name": "Grandchild" } ] }, { "name": "Child B", } ] };
var width = 500;
var height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g").attr('transform','translate(0,'+ (height/2) +')');
var root = d3.hierarchy(data);
var tree = d3.tree()
.nodeSize([width/2,height/2]);
var link = g.selectAll(".link")
.data(tree(root).links())
.enter().append("path")
.attr("class", "link")
.attr("d", d3.linkHorizontal()
.x(function(d) { return d.y; })
.y(function(d) { return d.x; }));
var node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
node.append("circle")
.attr("r", 2.5);
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
svg {
background: #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
As compared with the automatic spacing of d3.tree().size()
:
var data = { "name": "Parent", "children": [ { "name": "Child A", "children": [ { "name": "Grandchild" } ] }, { "name": "Child B", } ] };
var width = 500;
var height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g");
var root = d3.hierarchy(data);
var tree = d3.tree()
.size([width,height]);
var link = g.selectAll(".link")
.data(tree(root).links())
.enter().append("path")
.attr("class", "link")
.attr("d", d3.linkHorizontal()
.x(function(d) { return d.y; })
.y(function(d) { return d.x; }));
var node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
node.append("circle")
.attr("r", 2.5);
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
svg {
background: #eee;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.9.1/d3.min.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With