Aligning nodes horizontally along a timeline

I apologize, I’m a complete newbie at Graphviz and haven’t seen an example which fits what I have in mind.

I would like create a visualization of events happening in parallel along a timeline.

The timeline would run along the top edge of the image and would have vertical lines indicating things like century markers, like 1700, 1800, 1900, etc.

Then each event would be a node box with some data in it. Each event would have a start year and and end year. So I would like to draw it so that the left edge of a node box aligns with its start year on the timeline and the right edge aligns with its end year along the timeline. And events can happen in parallel, so they can be stacked.

Something like this little illustration.

Thank you for any thoughts, clues or pointers to relevant examples I can investigate.
-j_jones

I don’t think Graphviz is the right tool for this; there’s no edges between nodes, for example, and the only auto-layout is the stacking.

If you’re up for a little bit of coding, I’d encourage writing some code to generate SVG to play around with this idea. In SVG you can make rectangles with text in them and set their left/right edges, draw vertical lines for the timeline. You’d need to keep track as you go of what’s overlapping to set the top height.

Or maybe p5.js would be a nice graphics toolkit for easy prototyping: https://p5js.org/get-started/

Or maybe something like this: https://stackoverflow.com/questions/63213527/is-there-a-way-to-create-range-plot-in-python-using-plotly

1 Like

Thank you. There will be edges between nodes, but I didn’t want to overcomplicate the question. Basically I was hoping to find a way to draw a family tree in such a way as to show both the parent-child relationships and the actual years in which they lived in a visualization that conveyed both ideas.

Agree, maybe Mermaid.js is suitable? Maybe there’s a custom timeline tool somewhere. Also loo for d3.js timeline visualizations.

1 Like

Sounds cool, but I think it quickly becomes unwieldy. Node overlap would seem to cause ugly edges.
Scenario:

  • A & B have 3 children (E, F, & G)
  • B dies (sadly)
  • A has one child (H) with new spouse T
  • E F & G all have children with their spouses (I J K L M)
  • All the above have children of their own - while A & T are still alive

What should the graph look like? Especially

  • Are all children nodes positioned “between” the parent nodes? below? other?
  • Where should spouse nodes be placed?
  • What do the edges look like?
1 Like

Yes, part of the problem is indicating the relationships in such a way you can see whose offspring is whose quickly becomes unwieldly. I intend to simplify the problem somewhat by leaving out siblings not directly related to the family line, but it’s still confusing when you have groups of people whose lifespans are all overlapping in various ways. I suppose this is why you never see family trees visualized this way.

btw the apparent potential inbreeding in my example is just an accident, though you can find plenty of similar examples if you have any distant relatives in any way associated with certain types of European royalty

I agree with Marc & Steven that this is not a good match for Graphviz, but given the specifics of your intended graph, Graphviz might be as good as any other tool.
Here is a graph generated by a ~50 line awk script + a small data structure:

Note that the vertical sequence is solely based on the sequence of the input records, then the awk script calculates the explicit node positions plus the timeline. The script does not allow for overlapping Y coordinates when the X coordinates do not overlap (e.g. greatgrandparents & greatgrandchildren). Not sure how difficult that would be to add.
As a guess, adding the edges would probably not require much change to the script.

The shell script:

echo '
TIMELINE|1700|1875|25
Event A|1710|1757|lightpink
Event B|1737|1812|lightpink
Event F|1708|1776|lightgreen
Event G|1782|1858|cyan
Event C|1717|1802|orchid1
Event D|1740|1813|pink
Event E|1717|1773|palegreen
' |
gawk -F'|' '
BEGIN{
  print "graph T {"
  print "  node [fixedsize=true height=.66 shape=rect style=\"filled,rounded\"] "
  row=0
  heightFactor=1.3
  Xmin=99999
  Xmax=-99999
}
/TIMELINE/{
  First=$2
  Last=$3
  incr=$4
  next
}
NF>=3{
  row++
  lbl[row]=$1 "\\n" $2 " - " $3 
  Start[row]=$2
  Stop[row]=$3
  Color[row]=$4
  if (Xmin>$2)  Xmin=$2
  if (Xmax<$3)  Xmax=$3
}
END{
  # timeline nodes later
  XleftPad=40
  if (Xmin>First)  Xmin=First
  if (Xmax<Last)   Xmax=Last
  widthFactor=8/(Xmax-Xmin)
  # first the timeline
  for (i=First;i<=Last;i+=incr){
    Xpos=XleftPad+((i-Xmin) * widthFactor * 72) 
    Ypos=(1 + row) * heightFactor * 72
    print "\tTLa_" i, "[shape=plaintext label=\"" i "\" style=\"\" fixedsize=false width=0 height=0 "
    print "\t\tpos=\"" Xpos "," Ypos "\"]"  

    Ypos=0
    print "\tTLb_" i, "[shape=plaintext label=\"\" style=\"\" shape=none "
    print "\t\tpos=\"" Xpos "," Ypos "\"]"  

    print "\tTLa_" i "  --   TLb_" i "  [penwidth=.3]"
  }
  # now the "events"
  for (i=1;i<=row;i++){
    deltaYears=(Stop[i] - Start[i])
    wid= deltaYears * widthFactor 
    Xpos=XleftPad+((Start[i] + deltaYears/2 - Xmin) * widthFactor * 72)
    Ypos=(1 + row-i) * heightFactor * 72
    print "\te_" i, "[label=\"" lbl[i] "\"  color=\"" Color[i] "\""
    print "\t\twidth=\"" wid "\" "
    print "\t\tpos=\"" Xpos "," Ypos "\"]" 
  }
  print "}"
}'

The generated neato input:

graph T {
  node [fixedsize=true height=.66 shape=rect style="filled,rounded"] 
	TLa_1700 [shape=plaintext label="1700" style="" fixedsize=false width=0 height=0 
		pos="40,460.8"]
	TLb_1700 [shape=plaintext label="" style="" shape=none 
		pos="40,0"]
	TLa_1700  --   TLb_1700  [penwidth=.3]
	TLa_1725 [shape=plaintext label="1725" style="" fixedsize=false width=0 height=0 
		pos="131.139,460.8"]
	TLb_1725 [shape=plaintext label="" style="" shape=none 
		pos="131.139,0"]
	TLa_1725  --   TLb_1725  [penwidth=.3]
	TLa_1750 [shape=plaintext label="1750" style="" fixedsize=false width=0 height=0 
		pos="222.278,460.8"]
	TLb_1750 [shape=plaintext label="" style="" shape=none 
		pos="222.278,0"]
	TLa_1750  --   TLb_1750  [penwidth=.3]
	TLa_1775 [shape=plaintext label="1775" style="" fixedsize=false width=0 height=0 
		pos="313.418,460.8"]
	TLb_1775 [shape=plaintext label="" style="" shape=none 
		pos="313.418,0"]
	TLa_1775  --   TLb_1775  [penwidth=.3]
	TLa_1800 [shape=plaintext label="1800" style="" fixedsize=false width=0 height=0 
		pos="404.557,460.8"]
	TLb_1800 [shape=plaintext label="" style="" shape=none 
		pos="404.557,0"]
	TLa_1800  --   TLb_1800  [penwidth=.3]
	e_1 [label="Event A\n1710 - 1757"  color="lightpink"
		width="2.37975" 
		pos="77.6456,403.2"]
	e_2 [label="Event B\n1737 - 1812"  color="lightpink"
		width="3.79747" 
		pos="176.785,345.6"]
	e_3 [label="Event F\n1708 - 1776"  color="lightgreen"
		width="3.44304" 
		pos="70.8861,288"]
	e_4 [label="Event G\n1782 - 1858"  color="cyan"
		width="3.8481" 
		pos="340.861,230.4"]
	e_5 [label="Event C\n1717 - 1802"  color="orchid1"
		width="4.3038" 
		pos="104.127,172.8"]
	e_6 [label="Event D\n1740 - 1813"  color="pink"
		width="3.6962" 
		pos="187.671,115.2"]
	e_7 [label="Event E\n1717 - 1773"  color="palegreen"
		width="2.83544" 
		pos="103.392,57.6"]
}

The command line:

sh forumTimeline2.sh |neato -n -Tpng >oo.png
1 Like