Family tree layout with Dot/GraphViz

前端 未结 6 1381
慢半拍i
慢半拍i 2020-12-22 17:33

I am trying to draw a family tree with Dot and GraphViz.

This is what I currently have:

# just graph set-up
digraph simpsons {
ratio = \"auto\"
minc         


        
相关标签:
6条回答
  • 2020-12-22 17:51

    Gramps (www.gramps-project.org) generates dot files for family trees, with or without marriage nodes. There is also a way to see this in the Gramps interface itself. http://gramps-project.org/wiki/index.php?title=Graph_View So I would say, look at the output of your family tree as created by Gramps

    0 讨论(0)
  • 2020-12-22 17:56

    I don’t think you can take an arbitrary family tree and auto-generate a dot file where it always looks good in GraphViz.

    But I think you can always make it look good if you:

    • Use the rank=same other answers mentioned to get the 'T' connections desired by the OP
    • Use the ordering trick Brian Blank did to prevent weird lines
    • Assume no second marriages and half-siblings
    • Draw only a subset of the tree that obeys the following rules:
      • Let S be the “center” person
      • If S has siblings, make sure S is to right of all of them.
      • If S has a spouse and the spouse has siblings, make sure the spouse is to the left of all his/her siblings.
      • Don’t show nephews, nieces, aunts or uncles of S or S’s spouse
      • Don’t show spouses of siblings
      • Don’t show spouses of spouse’s siblings
      • Show children of S, but not their spouses or children
      • Show parents of S and parents of spouse

    This will end up showing no more than 3 generations at once, with S in the middle generation.

    In the picture below S=Homer (slightly modified from Brian Blank's version):

    digraph G {
      edge [dir=none];
      node [shape=box];
      graph [splines=ortho];
    
      "Herb"      [shape=box, regular=0, color="blue", style="filled" fillcolor="lightblue"] ;
      "Homer"     [shape=box, regular=0, color="blue", style="bold, filled" fillcolor="lightblue"] ;
      "Marge"     [shape=oval, regular=0, color="red", style="filled" fillcolor="pink"] ;
      "Clancy"    [shape=box, regular=0, color="blue", style="filled" fillcolor="lightblue"] ;
      "Jackeline" [shape=oval, regular=0, color="red", style="filled" fillcolor="pink"] ;
      "Abraham"   [shape=box, regular=0, color="blue", style="filled" fillcolor="lightblue"] ;
      "Mona"      [shape=oval, regular=0, color="red", style="filled" fillcolor="pink"] ;
      "Patty"     [shape=oval, regular=0, color="red", style="filled" fillcolor="pink"] ;
      "Selma"     [shape=oval, regular=0, color="red", style="filled" fillcolor="pink"] ;
      "Bart"      [shape=box, regular=0, color="blue", style="filled" fillcolor="lightblue"] ;
      "Lisa"      [shape=oval, regular=0, color="red", style="filled" fillcolor="pink"] ;
      "Maggie"    [shape=oval, regular=0, color="red", style="filled" fillcolor="pink"] ;
    
      a1 [shape=diamond,label="",height=0.25,width=0.25];
      b1 [shape=circle,label="",height=0.01,width=0.01];
      b2 [shape=circle,label="",height=0.01,width=0.01];
      b3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Abraham -> a1 -> Mona};
      {rank=same; b1 -> b2 -> b3};
      {rank=same; Herb; Homer};
      a1 -> b2
      b1 -> Herb
      b3 -> Homer
    
      p1 [shape=diamond,label="",height=0.25,width=0.25];
      q1 [shape=circle,label="",height=0.01,width=0.01];
      q2 [shape=circle,label="",height=0.01,width=0.01];
      q3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Homer -> p1 -> Marge};
      {rank=same; q1 -> q2 -> q3};
      {rank=same; Bart; Lisa; Maggie};
      p1 -> q2;
      q1 -> Bart;
      q2 -> Lisa;
      q3 -> Maggie;
    
      x1 [shape=diamond,label="",height=0.25,width=0.25];
      y1 [shape=circle,label="",height=0.01,width=0.01];
      y2 [shape=circle,label="",height=0.01,width=0.01];
      y3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Clancy -> x1 -> Jackeline};
      {rank=same; y1 -> y2 -> y3};
      {rank=same; Patty; Selma; Marge};
      x1 -> y2;
      y1 -> Marge;
      y2 -> Patty;
      y3 -> Selma;
    }
    

    This yields the following tree by GraphViz (with annotations I added with Power Point): enter image description here

    0 讨论(0)
  • 2020-12-22 17:59

    Although you can't control node placement, I found you can help node placement by ordering the nodes in a different order. I re-ordered some of the nodes as shown below and got a graph that produced no cross-overs.

    The following code:

    digraph G {
      edge [dir=none];
      node [shape=box];
    
      "Herb"      [shape=box, regular=1, color="blue"] ;
      "Homer"     [shape=box, regular=1, color="blue"] ;
      "Marge"     [shape=box, regular=1, color="pink"] ;
      "Clancy"    [shape=box, regular=1, color="blue"] ;
      "Jackeline" [shape=box, regular=1, color="pink"] ;
      "Abraham"   [shape=box, regular=1, color="blue"] ;
      "Mona"      [shape=box, regular=1, color="pink"] ;
      "Patty"     [shape=box, regular=1, color="pink"] ;
      "Selma"     [shape=box, regular=1, color="pink"] ;
      "Bart"      [shape=box, regular=1, color="blue"] ;
      "Lisa"      [shape=box, regular=1, color="pink"] ;
      "Maggie"    [shape=box, regular=1, color="pink"] ;
      "Ling"      [shape=box, regular=1, color="blue"] ;
    
      a1 [shape=circle,label="",height=0.01,width=0.01];
      b1 [shape=circle,label="",height=0.01,width=0.01];
      b2 [shape=circle,label="",height=0.01,width=0.01];
      b3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Abraham -> a1 -> Mona};
      {rank=same; b1 -> b2 -> b3};
      {rank=same; Herb; Homer};
      a1 -> b2
      b1 -> Herb
      b3 -> Homer
    
      p1 [shape=circle,label="",height=0.01,width=0.01];
      q1 [shape=circle,label="",height=0.01,width=0.01];
      q2 [shape=circle,label="",height=0.01,width=0.01];
      q3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Homer -> p1 -> Marge};
      {rank=same; q1 -> q2 -> q3};
      {rank=same; Bart; Lisa; Maggie};
      p1 -> q2;
      q1 -> Bart;
      q2 -> Lisa;
      q3 -> Maggie;
    
      x1 [shape=circle,label="",height=0.01,width=0.01];
      y1 [shape=circle,label="",height=0.01,width=0.01];
      y2 [shape=circle,label="",height=0.01,width=0.01];
      y3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Clancy -> x1 -> Jackeline};
      {rank=same; y1 -> y2 -> y3};
      {rank=same; Marge; Patty; Selma};
      {rank=same; Bart; Ling}
      x1 -> y2;
      y1 -> Marge;
      y2 -> Patty;
      y3 -> Selma;
      Selma -> Ling;
    }
    

    now produces this:

    family tree layout

    I don't fully understand why it's working, but here is the thought process into the changes that I made.

    1. I placed Clancy/Jackeline before Abraham/Mona thinking that they were on the wrong side. This changed the picture, but still wasn't perfect.
    2. I placed Homer/Marge first thinking that the software had to consider these pieces first and maybe place all other nodes relative to Homer/Marge. This further helped, but still wasn't perfect.
    3. Herb was still mis-placed, so I put Herb first so that graphviz might consider placement of herb first.

    It worked, but I still can't devise an algorithm that would ensure consistent trees with no overlapping edges. I feel that graphviz should do a better job without these hints. I don't know the algorithm used, but if they consider an objective function to minimize or eliminate overlapping edges, it should be possible to devise a better algorithm.

    0 讨论(0)
  • 2020-12-22 17:59

    I'm almost there, inspired by an old response on the graphviz-interest mailinglist and doug's answer.

    The following code:

    digraph G {
      edge [dir=none];
      node [shape=box];
    
      "Abraham"   [shape=box, regular=1, color="blue"] ;
      "Mona"      [shape=box, regular=1, color="pink"] ;
      "Clancy"    [shape=box, regular=1, color="blue"] ;
      "Jackeline" [shape=box, regular=1, color="pink"] ;
      "Herb"      [shape=box, regular=1, color="blue"] ;
      "Homer"     [shape=box, regular=1, color="blue"] ;
      "Marge"     [shape=box, regular=1, color="pink"] ;
      "Patty"     [shape=box, regular=1, color="pink"] ;
      "Selma"     [shape=box, regular=1, color="pink"] ;
      "Bart"      [shape=box, regular=1, color="blue"] ;
      "Lisa"      [shape=box, regular=1, color="pink"] ;
      "Maggie"    [shape=box, regular=1, color="pink"] ;
      "Ling"      [shape=box, regular=1, color="blue"] ;
    
      a1 [shape=circle,label="",height=0.01,width=0.01];
      b1 [shape=circle,label="",height=0.01,width=0.01];
      b2 [shape=circle,label="",height=0.01,width=0.01];
      b3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Abraham -> a1 -> Mona};
      {rank=same; b1 -> b2 -> b3};
      {rank=same; Herb; Homer};
      a1 -> b2
      b1 -> Herb
      b3 -> Homer
    
      p1 [shape=circle,label="",height=0.01,width=0.01];
      q1 [shape=circle,label="",height=0.01,width=0.01];
      q2 [shape=circle,label="",height=0.01,width=0.01];
      q3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Homer -> p1 -> Marge};
      {rank=same; q1 -> q2 -> q3};
      {rank=same; Bart; Lisa; Maggie};
      p1 -> q2;
      q1 -> Bart;
      q2 -> Lisa;
      q3 -> Maggie;
    
      x1 [shape=circle,label="",height=0.01,width=0.01];
      y1 [shape=circle,label="",height=0.01,width=0.01];
      y2 [shape=circle,label="",height=0.01,width=0.01];
      y3 [shape=circle,label="",height=0.01,width=0.01];
      {rank=same; Clancy -> x1 -> Jackeline};
      {rank=same; y1 -> y2 -> y3};
      {rank=same; Marge; Patty; Selma};
      {rank=same; Bart; Ling}
      x1 -> y2;
      y1 -> Marge;
      y2 -> Patty;
      y3 -> Selma;
      Selma -> Ling;
    }
    

    now produces this:

    alt text

    So, looks good except for that strange edge around Homer.If I could find a way to move Abraham, Mona and Herb to the left hand side of the picture then I would have a perfectly aligned picture.

    Any ideas on how to achieve that?

    0 讨论(0)
  • 2020-12-22 18:04

    Here is an other solution :

    digraph simpsons {  
      subgraph Generation0 {
        rank = same
        Abraham [shape = box, color = blue]
        Mona [shape = box, color = pink]
        AbrahamAndMona [shape = point]
        Abraham -> AbrahamAndMona [dir = none]
        AbrahamAndMona -> Mona [dir = none]
    
        Clancy [shape = box, color = blue]
        Jackeline [shape = box, color = pink]
        ClancyAndJackeline [shape = point]
        Clancy -> ClancyAndJackeline [dir = none]
        ClancyAndJackeline -> Jackeline [dir = none]
      }
    
      subgraph Generation0Sons {
        rank = same
        AbrahamAndMonaSons [shape = point]
        HerbSon [shape = point]
        HomerSon [shape = point]
        HerbSon -> AbrahamAndMonaSons [dir = none]
        HomerSon -> AbrahamAndMonaSons [dir = none]
    
        MargeSon [shape = point]
        PattySon [shape = point]
        SelmaSon [shape = point]
        MargeSon -> PattySon [dir = none] 
        PattySon -> SelmaSon [dir = none] 
      }
    
      AbrahamAndMona -> AbrahamAndMonaSons [dir = none]
      ClancyAndJackeline -> PattySon [dir = none]
    
      subgraph Generation1 {
        rank  =  same
        Herb [shape = box, color = blue] 
        Homer [shape = box, color = blue] 
        Marge [shape = box, color = pink] 
        Patty [shape = box, color = pink] 
        Selma [shape = box, color = pink] 
    
        HomerAndMarge [shape = point]
        Homer -> HomerAndMarge [dir = none]
        Marge -> HomerAndMarge [dir = none]
      }
    
      HerbSon -> Herb [dir = none]
      HomerSon -> Homer [dir = none]
      MargeSon -> Marge [dir = none]
      PattySon -> Patty [dir = none]
      SelmaSon -> Selma [dir = none]
    
      subgraph Generation1Sons {
        rank  =  same
        BartSon [shape = point] 
        LisaSon [shape = point] 
        MaggieSon [shape = point] 
    
        BartSon -> LisaSon [dir = none]
        LisaSon -> MaggieSon [dir = none]
      }
    
      HomerAndMarge -> LisaSon [dir = none]
    
      subgraph Generation2 {
        rank  =  same
        Bart [shape = box, color = blue] 
        Lisa [shape = box, color = pink] 
        Maggie [shape = box, color = pink] 
        Ling [shape = box, color = blue] 
      }
    
      Selma -> Ling [dir = none]
      BartSon -> Bart [dir = none]
      LisaSon -> Lisa [dir = none]
      MaggieSon -> Maggie [dir = none]
    }
    

    And the result :

    http://dl.dropbox.com/u/72629/simpsons.png

    0 讨论(0)
  • 2020-12-22 18:05

    To do this in graphviz is fairly straightforward; there are a couple of syntax patterns you need: (i) syntax to represent the line-to-line connection (the "T"-junction in your plots above); (ii) syntax to enforce the hierarchical structure (i.e., nodes of same generation on the same plane on the vertical axis). It's easier to show:

    digraph G {
        nodesep=0.6;
        edge [arrowsize=0.3];
    
        "g1" -> "g2" -> "g3" -> "g4"
    
        { rank = same;
            "g1"; "King"; "ph1"; "Queen";
        };
    
        { rank = same; 
            "g2"; "ph2"; "ph2L"; "ph2R"; "ph2LL"; "ph2RR"
        };
    
        { rank = same;
            "g3"; "ps1"; "ps2"; "pr1"; "pr2"
        };
    
        "King" -> "ph1" [arrowsize=0.0];
        "ph1" -> "Queen" [arrowsize=0.0];
    
        "ph1" -> "ph2" [arrowsize=0.0];
        "ph2LL" -> "ph2L" [arrowsize=0.0];
        "ph2L" -> "ph2" [arrowsize=0.0];
        "ph2" -> "ph2R" [arrowsize=0.0];
        "ph2R" -> "ph2RR" [arrowsize=0.0];
    
        "ph2LL" -> "ps1" [arrowsize=0.0];
        "ph2L"-> "pr1" [arrowsize=0.0];
        "ph2R" -> "ps2" [arrowsize=0.0];
        "ph2RR" -> "pr2" [arrowsize=0.0];
    
    }
    

    The code above will produce the graph below (i omitted the code i used to color the nodes). I left vislble the "guide" on the left (g1->g2....) just to show you how i enforced the positions among nodes of equal rank, you'll probably want to make it invisible in your own plots. Finally, the nodes with the labels beginning with 'ph' are the placeholder nodes for the "T-junctions."

    0 讨论(0)
提交回复
热议问题