Examples of how to create traditionally styled family pedigrees with D3 v3.
When I started learning how to draw pedigrees with D3, I had difficulty mixing and matching techniques from different examples and separating the basics from the fancy stylistic additions. Hopefully by showing the basics and how to incrementally add advanced features, you won't have to become an expert in D3 just to draw a pedigree.
My requirements were:
- A tree with square boxes and straight connecting lines. Most D3 tree examples used circles for nodes and curved connecting lines.
- There needed to be a fixed distance between generations. By default, D3 trees fill up all the available space given to them.
- The tree needed to be expandable and collapsible.
- Need a way to display both ancestors and descendants.
- Pan and zoom.
- Basic: A basic static pedigree.
- Text Wrap: A basic static pedigree with text wrap using d3plus.
- Expand and Collapse: Click on persons to expand and collapse the tree.
- Smooth Transitions: Changes are animated when the tree is expanded or collapsed.
- Ancestors and Descendants: Show both ancestors and descendants.
- Ancestors and Descendants - OOP: A more sane example of ancestors and descendants using classes.
Pan and zoom via the mouse are enabled in all examples.
In the code you'll often see the variable
das the only parameter of an anonymous function.
dis a D3 convention that stands for data.
D3 tree layouts are configured for top -> bottom displays. We want a left -> right display so the x and y coordinates are flipped for nodes and links only. This is made even more complicated by svg using screen coordinates instead of cartesian coordinates.
Say D3 calculates that a root node will be displayed at
(10,10)with a child below it at
(10,20). We switch the x and y which leaves the root at
(10,10)but moves the child to
(20,10)which is now to the right. This gives us the traditional left to right ancestral pedigree view.
To display descendants we swap the x and y then negate the x value which puts child nodes at the left.
You have to draw two different trees in order to display both ancestors and descendants which dramatically increases the complexity. I strongly suggest an OOP approach if you need to do that. As you can see in the source code, it gets pretty hairy otherwise.
Displaying formatted text in SVG can be a pain because you have little control. There is no built-in way to do text wrapping and you also can't easily style one word or phrase in a sentence. To get around these limitations you could use a
foreignObjectto display HTML and have all the formatting tools of CSS. However,
foreignObjectis not supported by any version of IE (11 is the latest at the time of writing). If you need to support any version of IE then you need to stick with pure SVG. Since we're using D3 we can use the handy D3plus library to help with text wrapping. See the Text Wrap example.
D3 does not handle pedigree collapse well. D3 trees are designed to only ever branch out; there is no built-in mechanism for allowing the tree to collapse. Here are some options for handling this ourself:
Duplicate common ancestor nodes instead of collapsing the pedigree. This is only feasible if you don't use a dynamic tree. When D3 processes updates to a dynamic tree, it relies on all nodes having a unique ID. So to make this work with a dynamic tree you would need to generate unique IDs for each duplicated person in the pedigree.
Allow the pedigree to collapse by only displaying a common ancestor once. We will let D3 draw the first connection then draw all other connections manually. This is not trivial, especially if you want to have a dynamic tree. There is an example of this. It doesn't appear to be a solution that scales well.