How to change orientation of a D3 tree layout by 90 degrees

d3.js, javascript, orientation, svg, tree

Solution

The example you're looking at is actually already flipped - this might be causing you some confusion. Trees in D3 are naturally top-down trees, and the code does a lot of x-y flipping to make the tree sideways.

Changing

  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) { p = calcLeft(d); return "translate(" + p.y + "," + p.x + ")"; })
      ;

to

  var nodeUpdate = node.transition()
      .duration(duration)
      .attr("transform", function(d) { p = calcLeft(d); return "translate(" + p.x + "," + p.y + ")"; })
      ;

will get the nodes displaying in the right position. Doing a similar change with any instance of swapped x-y coordinates inside update() fixes most of the positioning issues. One last thing is the elbow function/variable/whatever you want to call it, where

  return "M" + source.y + "," + source.x
         + "H" + (source.y+hy)
         + "V" + target.x + "H" + target.y;

should be changed to

  return "M" + source.x + "," + source.y
         + "V" + (source.y+hy)
         + "H" + target.x + "V" + target.y;

This changes the connector shape from horizontal vertical horizontal to vertical horizontal vertical. Note that this is a raw SVG line, not d3 at all. The changes I made (plus swapping width and height, and changing the AJAX JSON request to hardcoding the data - AJAX is hard to get working in fiddle) are all at http://jsfiddle.net/Zj3th/2/.

If you have no experience with d3 and SVG, I would definitely take a look and fully understand a simple example like http://blog.pixelingene.com/2011/07/building-a-tree-diagram-in-d3-js/ before you go further in modifying the code.

Problem

I just started getting involved in web visualizations, so I'm totally novice. My goal is to display a family tree where a root node would have both multiple parents and children. While looking for a solution I found this example: http://bl.ocks.org/jdarling/2503502 It's great because it seems to have the feature I need. However, I would like to alter the orientation (top-to-bottom). I tried to do so using this example: http://bl.ocks.org/mbostock/3184089 but failed. My code: ``` var tree = d3.layout.tree() .size([height, width]); var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.x, d.y]; }); var elbow = function (d, i){ var source = calcTop(d.source); var target = calcTop(d.target); var hx = (target.x-source.x)/2; if(d.isRight) hx = -hx; return "M" + source.x + "," + source.y + "H" + (source.x+hx) + "V" + target.y + "H" + target.x; }; var connector = elbow; var calcTop = function(d){ var top = d.x; if(!d.isRight){ top = d.x-halfHeight; top = halfHeight - top; } return {x : top, y : d.y}; }; var vis = d3.select("#chart") .append("svg") .attr("height", height + margin.top + margin.bottom) .attr("width", width + margin.right + margin.left) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); d3.json("tree.json", function(json) { root = json; root.x0 = height / 2; root.y0 = width / 2; var t1 = d3.layout.tree() .size([halfHeight, width]) .children(function(d){ return d.winners; }); var t2 = d3.layout.tree() .size([halfHeight, width]) .children(function(d){ return d.challengers; }); t1.nodes(root); t2.nodes(root); var rebuildChildren = function(node){ node.children = getChildren(node); if(node.children) node.children.forEach(rebuildChildren); } rebuildChildren(root); root.isRight = false; update(root); }); var toArray = function(item, arr){ arr = arr || []; var i = 0, l = item.children?item.children.length:0; arr.push(item); for(; i < l; i++){ toArray(item.children[i], arr); } return arr; }; function update(source) { // Compute the new tree layout. var nodes = toArray(source); // Normalize for fixed-depth. nodes.forEach(function(d) { d.x = d.depth * 180 + halfHeight; }); // Update the nodes… var node = vis.selectAll("g.node") .data(nodes, function(d) { return d.id || (d.id = ++i); }); // Enter any new nodes at the parent's previous position. var nodeEnter = node.enter().append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + source.x0 + "," + source.y0 + ")"; }) .on("click", click); nodeEnter.append("circle") .attr("r", 1e-6) .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); nodeEnter.append("text") .attr("dy", function(d) { return d.isRight?14:-8;}) .attr("text-anchor", "middle") .text(function(d) { return d.name; }) .style("fill-opacity", 1e-6); // Transition nodes to their new position. var nodeUpdate = node.transition() .duration(duration) .attr("transform", function(d) { p = calcTop(d); return "translate(" + p.x + "," + p.y + ")"; }); nodeUpdate.select("circle") .attr("r", 4.5) .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; }); nodeUpdate.select("text") .style("fill-opacity", 1); // Transition exiting nodes to the parent's new position. var nodeExit = node.exit().transition() .duration(duration) .attr("transform", function(d) { p = calcTop(d.parent||source); return "translate(" + p.x + "," + p.y + ")"; }) .remove(); nodeExit.select("circle") .attr("r", 1e-6); nodeExit.select("text") .style("fill-opacity", 1e-6); // Update the links... var link = vis.selectAll("path.link") .data(tree.links(nodes), function(d) { return d.target.id; }); // Enter any new links at the parent's previous position. link.enter().insert("path", "g") .attr("class", "link") .attr("d", function(d) { var o = {x: source.x0, y: source.y0}; return connector({source: o, target: o}); }); // Transition links to their new position. link.transition() .duration(duration) .attr("d", connector); // Transition exiting nodes to the parent's new position. link.exit() .transition() .duration(duration) .attr("d", function(d) { var o = calcTop(d.source||source); if(d.source.isRight) o.x -= halfHeight - (d.target.x - d.source.x); else o.x += halfHeight - (d.target.x - d.source.x); return connector({source: o, target: o}); }) .remove(); // Stash the old positions for transition. nodes.forEach(function(d) { var p = calcTop(d); d.x0 = p.x; d.y0 = p.y; }); // Toggle children on click. function click(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(source); ``` } } Would really appreciate the help!

Original source