> Long time no genealogy, and Happy New Year from Australia...

Hi Ron and all,
I'll second on both counts, substituting California for Australia...

About a decade ago I created a couple of additional subroutines for
perl-gedcom Individual.pm which worked very nicely for me. They include
trees of both descendants and ancestors. I took it so far as to
dynamically creating trees using the pictures of the individuals and
formatting them using HTML tables.

At some point I had a major server issue and never got around to
reconstructing my genealogy website, so this is directly from a very old
backup and will only do the basic no-frills ASCII trees which must be
formatted with monospace characters i.e. using <PRE> </PRE> in HTML to
make any sense.

I never published the routines, as I wanted to remove some limitations
first (see below). As I don't see myself touching the code in the
foreseeable future, here it is.

Hope it helps!
Michael




# Copyright 2002-2011, Michael Ionescu (m...@ionescu.de)
# This software is free.  It is licensed under the same terms as Perl
itself.
# Written to be appended to the package Gedcom::Individual
# Thanks goes to Paul Johnson for Perl-Gedcom and to Berry B. and
Christian V. for their help!
# Should you use or amend the code, I'd love to hear about it. I do not,
however intend to carry out any code maintenance.

sub life
{
  my $self = shift;
  my ($birt,undef) = map { $_->date } $self->birt;
  my ($deat,undef) = map { $_->date } $self->deat;
  $birt=(defined $birt)?"*$birt":'';
  $deat=(defined $deat)?"+$deat":'';
  my $life=join(" ",$birt,$deat);
  $life eq " "?'':$life;
}

sub half_siblings
{
  my $self = shift;
  my @all_siblings_multiple = map { $_->children } ( map { $_->fams }
$self->parents );
  my @excludelist = ($self, $self->siblings);
  my @a = grep {
      my $cur = $_;
      my $half_sibling=1;
      foreach my $test(@excludelist){
        if($cur->{xref} eq $test->{xref} ){
      $half_sibling=0;
      last;
        }
      }
      push @excludelist, $cur if($half_sibling); # in order to avoid
multiple output
      $half_sibling;
    } @all_siblings_multiple;
  wantarray ? @a : $a[0]
}

sub half_brothers
{
  my $self = shift;
  my @a = grep { $_->tag_value("SEX") !~ /^F/i } $self->half_siblings;
  wantarray ? @a : $a[0]
}

sub half_sisters
{
  my $self = shift;
  my @a = grep { $_->tag_value("SEX") !~ /^M/i } $self->half_siblings;
  wantarray ? @a : $a[0]
}

sub collapsed_descendant_tree
{
  my $self = shift;
  my ($generations, $pattern, $prefix) = @_;
  $pattern     = '' unless $pattern;
  $prefix      = '' unless $prefix;
  $generations = 0  unless $generations;
  sub inspatt
  {
    my ($pattern, $self) = @_;
    my @output=();
    if($pattern){
      my @out=();
      foreach my $element (split(//,$pattern)){
        if($element eq 'X'){...@out=$self->{xref}
        }elsif($element eq 'C'){...@out=$self->cased_name
        }elsif($element eq 'N'){...@out=$self->name
        }elsif($element eq 'O'){...@out=($self->occu or "?")
        }elsif($element eq 'L'){...@out=$self->life
        }el...@out=$element
        }
        push @output, @out;
      }
    }else{
      push @output, "$self->{xref} ", $self->cased_name;
    }
    @output;
  }
  my @output=();
  return unless $generations > -1;
  my @kids = $self->children;
  my $kids = scalar @kids;
  my $currentkid = 0;
  push @output, ${prefix}, ($prefix ne ''?"\\__":"___"),
    inspatt($pattern,$self),"\n";
  return join "", @output unless $generations > 0;
  foreach my $fam ( $self->fams ){
     my $printspouse=1;
     foreach my $child ( $fam->children ){
        ++$currentkid;
        if($printspouse){
           my $spouse=$self->sex eq 'M'?$fam->wife:$fam->husband;
           push @output, "${prefix}  &", inspatt($pattern,$spouse), "\n";
           $printspouse=0;
        }
        push @output, $child->collapsed_descendant_tree(
          $generations-1, $pattern, $prefix.($currentkid==$kids?"   ":"  |")
        );
     }
  }
  join "", @output;
}

sub collapsed_ancestor_tree
{
  my $self = shift;
  my ($generations, $pattern, $prefix, $lastsex) = @_;
  $generations = 0  unless $generations;
  $prefix      = ' ' unless $prefix;
  $lastsex     = '' unless $lastsex;
  $pattern     = '' unless $pattern;
  my $thissex = $self->sex;
  my @output =();
  return unless $generations > -1;
  my $giveprefix=$prefix;
  if($thissex eq $lastsex){
    $prefix=~s/^(.*).(.{4})$/$1 $2/;
  }
  if($generations){
    push @output, map {
      $_->collapsed_ancestor_tree($generations-1,$pattern,$prefix."  
|",$thissex)
    } $self->father;
  }
  my $outprefix=$prefix;
  $outprefix=~s/(.*)./$1 /;
  push @output,"${outprefix}", ($lastsex?($thissex eq 'M'?'/':'\\'):' ');
  if($pattern){
    my @out;
    foreach my $element (split(//,$pattern)){
      if($element eq 'X'){...@out=$self->{xref}
      }elsif($element eq 'C'){...@out=$self->cased_name
      }elsif($element eq 'N'){...@out=$self->name
      }elsif($element eq 'O'){...@out=($self->occu or "?")
      }elsif($element eq 'L'){...@out=$self->life
      }el...@out=$element
      }
      push @output,@out;
    }
    push @output,"\n";
  }else{
    push @output,
      $self->{xref}," ", $self->cased_name, "\n";
  }
  if($generations){
    push @output, map {
      $_->collapsed_ancestor_tree($generations-1,$pattern,$prefix."  
|",$thissex)
    } $self->mother;
  }
  join "", @output;
}


=head2 Additional Individual functions

  my $text = $i->life;
  my @rel = $i->half_siblings;
  my @rel = $i->half_brothers;
  my @rel = $i->half_sisters;

=head2 ASCII-Art Family Trees

  my $str = $i->collapsed_descendant_tree(maxgenerations[,pattern])
  my $str = $i->collapsed_ancestor_tree(maxgenerations[,pattern])

These functions will output a string containing an ASCII-art family tree
going up or down at most maxgenerations generations from the individual.

The initial output per individual may be customized using a pattern
string. The string may consist of arbitrary characters including the
following symbols:
        X for XREF
        C for cased name
        N for name
        O for occupation
        L for life (*date of birth +deceased date)
Where a pattern of "C (L)[X]" would yield something like "John DOE
(*30.1.1777 +20.12.1800)[I6543]".

The idea behind outputting an XREF is that you may want to pass the tree
through a filter to alter
the family members' representations while keeping the tree-part intact.
  $str =~ s/^(.*?)I(\d+) (.*)$/$1<a href=\"I$2.html\">I$2 $3<\/a>/mg;
This would give each individual in the tree a weblink.

Caveat: These functions currently work only on unambiguous traditional
gedcom families 
(i.e. when there is exactly one female and one male spouse individual
linked to a family - no more, no less)



Reply via email to