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.