Blob Blame History Raw
package GIFgraph::WithMap;

use strict;
use vars qw (@ISA %fields $AUTOLOAD $VERSION);
use Carp;
use CGI;

use MRP::BaseClass;

$VERSION = 1.0;

# use AUTOLOAD to pass everything that we can't do to _GIFgraph.
# don't know if this is kosha. AHHHH.
sub AUTOLOAD {
  my $thing = shift;
  my ($package, $function) = $AUTOLOAD =~ m/^(.*)::([^:]+)$/;

  if(ref($thing)) {
    my $delegate = $thing->_GIFgraph;
    $function = join '::', $delegate, $function;
    return $thing->$function(@_);
  }
  die "Could not find method $AUTOLOAD via $thing";
}

sub isa {
  my ($thing, $type) = @_;
  return $thing->_GIFgraph->isa($type) || $thing->SUPER::isa($type);
}

sub new {
  my $class = shift;
  my $GIFgraph = shift;
  my $baseClass = new MRP::BaseClass;

  my $self = $class->rebless($GIFgraph, $baseClass);

  $self->_GIFgraph(ref $GIFgraph);

  return $self;
}

sub draw_data {
  my ($self, $gd, $data) = @_;
  my $fuzz = $self->fuzz;
  my $map = "";
  my $mapname = $self->mapname || confess "You must set the name for this map before calling plot";
  my $seriesnames = $self->seriesnames;
  my $links = $self->links;

  my $delegateFunc = join '::', $self->_GIFgraph, 'draw_data';
  $self->$delegateFunc($gd, $data);
  
  $map = '<MAP NAME="' . $mapname . '">'."\n";
  foreach my $series (1 .. $self->{numsets}) {
    my (@up, @down);
    foreach my $point (0 .. $self->{numpoints}) {
      next unless defined($$data[$series][$point]);
      my ($x, $y) = $self->val_to_pixel($point+1, $$data[$series][$point], $series);
      push @up, $x, $y+$fuzz;
      unshift @down, $x, $y-$fuzz;
    }
    my $seriesname = $seriesnames->[$series-1] || 'series '.$series;
    my $link = $links->[$series-1] || "#$seriesname";
    $map .= join("\n\t",
		 '<AREA',
		 'alt="' . $seriesname . '"',
		 'href="' . $link . '"',
		 'onMouseOver="self.status=' . "'$seriesname'" . '; return true"',
		 'onMouseOut="self.status=' . "''" . '; return true"',
		 'shape=polygon',
		 'coords="' .  join(', ', @up, @down) . '"',
		 );
    $map .= ">\n";
  }
  $map .= "</MAP>\n";

  $self->map($map);
}

BEGIN {
  @ISA = qw (MRP::BaseClass);
  %fields = (
	     'fuzz' => 1,
	     'map' => undef,
	     'mapname' => undef,
	     'seriesnames' => [],
	     'links' => [],
	     '_GIFgraph' => undef,
	    );
  GIFgraph::WithMap->check4Clashes;
}

$VERSION;

__END__

=head1 NAME

GIFgraph::WithMap - generates HTML map text while plotting graph

=head1 DESCRIPTION

Generates the html map block for a graph so that data series become
'clickable',

=head1 SYNOPSIS

This module extends GIFgraph objects such as GIFgraph::lines. You can
do everything that you would with a GIFgraph object. In addition, when
the data is plotted, it generates some MAP html text.

The series will be labeled 'series 1', 'series 2' etc. unless the
$obj->seriesnames has been set. For each series, it will create a
polygon area, with the following structure (assuming series is named
'Green', and that the links member array is empty:

 <AREA
        alt="Green"
        href="#Green"
        onMouseOver="self.status='Green'; return true"
        onMouseOut="self.status=''; return true"
        shape=polygon
        coords="87, 41, 165, 250, 165, 246, 87, 37">

So - clicking on the series will take you to #Green. If you don't
specify #Green in the document, clicking on it will do very little. If
you have (e.g. in the key) then it should take you there.

=head1 new

Use something like

 $map = new GIFgraph::WithMap(new GIFgraph::lines(400,300));

=head1 fields

=over

=item map

Once plot has been called, map contains the map text.

 print $graphWithMap->map;

=item fuzz

This is the up/down fuzz used to construct the ploygon. It defaults to
1 - so the polygon will be three pixles wide (the pixle drawn and one
above and below it).

=item mapname

Set this before calling plot. This is the name of the map, as given by
usemap="#mapname" in <img>. It is a fatal error to try to plot without
a name.

=item seriesnames

Array of names for the series. If a name is absent for a series (or
all series) then it will be named Series #. This must be set before
the graph is plotted.

 $graphWithMap->seriesnames('name1', 'name2');

=links

Array of links for the series. If a link is absent for a series (or
all series) then it will be named #SeriesName (where SeriesName is
generated as described in L<seriwsnames>).

=back

=head1 Guts

This module uses MRP::BaseClass to implement member access functions.

In the constructor, a new object is created that is a meld of an
MRP::BaseClass derived object, and the GIFgraph object passed in. The
type of that object is stoored in _GIFgraph. In AUTOLOAD, first I
check to see if the GIFgraph object's package named in _GIFgraph
implements the function. If it does, then I return the value of that
function - i.e. it behaves as if the object inherits from that
package. If that fails, then I return the value of SUPER::AUTLOLOAD,
which will presumably handle it or die gracefully. Hey presto -
dynamic parenting.

@ISA does not include anything to implement a GIFgraph object as a
parent. However, I have overridden 'isa' to account for this.