Tag: graphviz

2009-05-29 01:24 UTC All my known ancestors

Toying with my script from the previous post... Not limiting the number of generations, and processing it with neato instead of dot (still from Graphviz) gives this cool diagram... I haven't tried removing overlaps. I also have some changes to allow including siblings (the diagram below includes only direct ancestors). Posting it mostly because it looks neat, and it illustrates pretty well how exponential growth quickly makes family trees unwieldly. Our family database contains thousands of people not in the diagram below - siblings of everyone there, as well as the spouses and descendants of siblings, and for the most part this stretches "only" back to the early 1700's, with a few spindly arms going back to about 1480:




(Click for a somewhat larger version -- I haven't posted a full size one as it's HUGE; maybe when I've cleaned it up a bit more)

When I've had time to do some more cleanups I'll probably post a more recent iteration of the script I'm using.


2009-05-23 16:04 UTC Family tree using Graphviz and Ruby

My dad spent a lot of time putting together a family database, currently containing about 12000 people covering both my parents ancestors as well as tracking forward to contain a lot of living descendants. Unfortunately, since he started this over 16-17 years ago it's been managed as a custom dBase III+ app, and the code grew by accretion over at least 7 years (until my father died). Spurred on by an e-mail from a possible distant relative (who it turns out I've even met) I finally dumped the dbf files into an Sqlite database and put together a few scripts to generate diagrams from it.

Here's an example (click to enlarge). The birth/death dates are in Norwegian format (day.month.year)

Ancestors of Ole Martin Hokstad

The full SVG diagram (which is zoomable in Safari and Firefox) is here.

This tree shows the known ancestors of Ole Martin Hokstad - the first person amongst my direct ancestors to be born to the Hokstad name (there are one or two other families where the name Hokstad was taken at different times). In our case the name stems from the Hogstad farms in Frosta, near Trondheim, Norway. The farms kept being divided as a result of children inheriting parts etc.. 
At one point the farm Lille-Hogstad was bought by Ola Viktil, and one of his grandsons, Peter Magnus Hokstad Johansen combined two smaller properties to Hogstad Lille Vestre in 1854, which was then renamed Hokstad (presumably he didn't like the thought of the name Peter Magnus Hogstad Lille Vestre Johansen). His children, including my great-grandfather Ole Martin Hokstad, got the name by birth.

The tree above doesn't show any siblings, and leaves out a few people we don't have any certain information about. I did render one of all my know ancestors as well, but it's too huge to be practical to reproduce here (about 20 times the size of the tree above).

To produce this I put together a very quick and dirty little Ruby script:

require 'model'
require 'set'

id = ARGV[0].to_i

# Prevent double inclusion of a node $memo = Set.new

def filter_node(per) return nil if !per return nil if per.firstname.strip == "?" || per.lastname.strip == "?" || per.maidenname.strip == "?" return per end

def node (per,color) return false if $memo.member?(per.pk) $memo << per.pk name = [per.firstname, per.middlename, per.lastname, per.maidenname] name = name.collect do |n| n && n != "" ? n : nil }.compact.join(" ") label = "#{name}\n#{per.birthdate} - #{per.deathdate}" puts " p#{per.pk} [ shape = box, style=\"filled\","+ " fillcolor=\"#{color.to_s}\", label=\"#{label}\" ];" return true end

def ancestors per

father = filter_node(per.father) mother = filter_node(per.mother)

pk = per.pk arrowhead = "normal" if mother and father merge = "m#{mother.pk}and#{father.pk}" if !$memo.member?(merge) puts " p#{merge} [ shape = point ]" puts " p#{merge} -> p#{pk} [ arrowtail=none ]" arrowhead = "none" else $memo << merge end pk = merge end

if father if node(father,:green) puts " p#{father.pk} -> p#{pk} [ arrowhead=#{arrowhead} ]" ancestors(father) end end

if mother if node(mother,:gold) puts " p#{mother.pk} -> p#{pk} [ arrowhead=#{arrowhead} ]" ancestors(mother) end end end

def graph per puts "digraph ancestors {" node(per,:red) ancestors(per) puts "}" end

per = Person[:id => id] if !per puts "Unable to find #{id}" exit end

graph(per)

I'm not going to spend a lot of time going through the script, other than to point out the dependencies if you want to try this for yourself:

You need to create a class with the methods #pk that returns a unique key suitable to be part of a Graphviz dot-file node name, #father and #mother that returns an equivalent object for the father and mother respectively or nil if not known, and methods #firstname, #middlename, #lastname and #maidenname respectively that returns the names as strings. Whether it comes from a database or not is irrelevant - you can load it all into memory first if you like. In my case it's all from a Sequel model, as you can see I retrieve a Person object for the id provided as the root of the tree at the end of the script. 
I don't think I'll put in much effort to make this a generic package, but it should be easy enough to adapt if you know some Ruby. I will probably post a couple of variations to add output of siblings and also to generate an equivalent one for descendants instead of ancestors though.

I then use this little bash script to generate the SVG file (requires xsltproc)

#!/bin/sh

ruby ancestors.rb $1 >/tmp/$1.dot dot -Tsvg /tmp/$1.dot >/tmp/$1.svg xsltproc /opt/diagram-tools/notugly.xsl /tmp/$1.svg >$2

This assumes my diagram-tools GIT repository has been cloned into /opt/diagram-tools (git clone git://github.com/vidarh/diagram-tools.git /opt/diagram-tools), to pretty up the Graphviz output.





2009-05-18 22:54 UTC Making Graphviz output pretty with XSL - Updated

Over a year ago I posted an XSL file for prettying up Graphviz diagrams

I got lots of feedback, and recently I've been fixing various bugs, and I finally got around to adding support for more of the Graphviz node shapes etc.

Here's an example of various node types and colors with the new version. Note that while rounder corners are "sort of" working, I haven't found a good way of making the gradient fill work nicely with it. The current version has been tested with Graphviz 2.20.2.


(Scaled down a bit to fit my layout; if you can't see it you need a browser that can embed SVG's, such as any recent Firefox or Safari)


The XSL file can be found with various other Graphviz related tools at my diagram tools repository on GitHub

This is a fairly substantial set of changes, so please do let me know if you find any bugs (if so, I appreciate an example dot-file as well as the Graphviz XSL output and the "cleaned" XSL output, and the version number of the Graphviz version you're using - please try to upgrade to at least 2.20.2, though).

Thanks to Jonas Tingeborn, Earl Cummings and Kevin Keraudren for various bug fixes and help debugging. Thanks to Michael Kennedy for posting a variation with lots of gradients (I've pilfered a smallish subset for my updated version - but you can copy the rest from his version if you prefer)



2009-04-20 22:30 UTC Updated Graphviz tools on Github

I just added a new repository on GitHub containing the tools from my Graphviz / diagram related posts.
The repository can be found at: http://github.com/vidarh/diagram-tools/tree/master

These are the ones included and the appropriate articles:

The only new thing so far is that notugly.xsl is updated to work with Graphviz 2.22.2





2009-02-01 02:07 UTC Creating Graphviz graphs from Ruby arrays

As part of my compiler project I wanted a way to visualize the programs, and since the syntax tree (so far at least) is represented with plain Ruby arrays I decided to throw together a script to use Graphviz to generate some graphs. I've written about using Graphviz previously here
The code does not make any assumptions tied to my compiler, but it's NOT an attempt at visualizing arbitrary object structures. You need to pass it an Array object, which can contain other arrays or objects that responds to #to_s.
NOTE: The code makes NO attempt to deal with structures that have loops - you'll run out of stack space soon enough if you try that. Feel free to post your fixes to do that in the comments (easy enough - just need to keep track of visited objects).

An example example. Given this:

 [:defun, :parse_quoted, [:c],
 [:while, [:and, [:ne, [:assign, :c, [:getchar]], -1], [:ne, :c, 34]], [:do,
  [:putchar, :c]
  ]
 ]
 ]


I generate this image (the gradients and shadows are thanks to my previously described XSL transform to pretty up the Graphviz SVG output):



The code is quite straightforward, though I'm not quite happy with the amount of monkey-patching. There were two easy choices: monkey-patching or lots of #is_a? calls, which made it horribly messy. I wouldn't advocate including this into a larger app without cleaning it up first, but as a quick hack it works well.

module ToDot
 def self.escape str
  str.gsub(/([<>{} |\])/) { "\"+$1 }
 end
end

class String def to_dot_label; '\"'+ToDot::escape(self)+'\"'; end end

class Array def to_dot_label; "..."; end

def to_dot_edge src, shorten " #{src}" + (shorten ? "" : ":#{object_id}") + " -> #{object_id};\n" end

def to_dot_subgraph return "" if nil ary = self[0].is_a?(Array) shorten = !ary && self[1..-1].detect{|o| !o.is_a?(Array)} == nil s = " #{object_id} [label=\"" if shorten s += self[0].to_dot_label + "\", shape=rect];\n" else s += collect { |o| "<#{o.object_id}> " + o.to_dot_label }.join("|") s += "\"];\n" end s += collect {|o| o.to_dot_edge(object_id,shorten) }.join s += collect {|o| o.to_dot_subgraph }.join s end end

class Object def to_dot_subgraph; end def to_dot_edge src, shorten; end def to_dot_label; ToDot::escape(to_s); end

def to_dot s = "digraph G {\n" s += " node [shape=record style=filled fillcolor=lightblue " s += "fontname=Verdana height=0.05 fontsize=10.0 ];\n" s += to_a.to_dot_subgraph s += "}\n" end end


As for how to use it:

require 'arytodot'

puts someArray.to_dot

Then pipe the output to "dot -Tsvg >output file" and use your favorite XSL processor to render an image from it. I used "rsvg file.svg file.png". If you want to use my XSL transform to pretty it up, follow the instructions in the article linked to above.

Here's the full "parser" example from my compiler series (click for the full size version):




About me

E-mail: vidar@hokstad.com Skype: vhokstad
Twitter: vhokstad
View my LinkedIn profile.

I was born April 21st, 1975, in Oslo, Norway. Since 2000 I've been living in London, UK. I'm married and we just had our first child, Tristan Ikemefuna Hokstad.

I'm working for Aardvark Media as Director of Technology. I'm also currently on the board of SpatialQ, a startup in the GIS space, and an advisor to Skoach, a startup doing a time management app for people with ADD.

Twitter Updates

    follow me on Twitter