8.3 Adding GD Graphics to Restrictionmap.pm


Now that you've seen an overview of the GD.pm Perl module, let's enhance the Restrictionmap.pm module that you've seen previously in Chapter 5, Chapter 6, and Chapter 7 so it can output PNG graphics files.

Reviewing how the Restrictionmap.pm module works, you can see that its get_graphic method examines the _graphictype attribute to determine what graphics drawing method to call. Following is the get_graphic method again, along with the attributes the class defines. (Recall that this class inherits from the class Restriction.pm , so many things are defined there):

 # Class data and methods {     # A list of all attributes with default values.     my %_attributes = (         #    key   = restriction enzyme name         #    value = space-separated string of recognition sites => regular expressions         _rebase      => { },    # A Rebase.pm object         _sequence    => '',     # DNA sequence data in raw format (only bases)         _enzyme      => '',     # space separated string of one or more enzyme names         _map         => { },    # a hash: enzyme names => arrays of locations          _graphictype => 'text', # one of 'text' or 'png' or some other         _graphic     => '',     # a graphic display of the restriction map     );              # Return a list of all attributes     sub _all_attributes {             keys %_attributes;     } } sub get_graphic {     my($self) = @_;     # If the graphic is not stored, calculate and store it     unless($self->{_graphic}) {         unless($self->{_graphictype}) {             croak 'Attribute graphictype not set (default is "text")';         }         # if graphictype is "xyz", method that makes the graphic is "_drawmap_xyz"         my $drawmapfunctionname = "_drawmap_" . $self->{_graphictype};         # Calculate and store the graphic         $self->{_graphic} = $self->$drawmapfunctionname;     }     # Return the stored graphic     return $self->{_graphic}; } 

As you recall, the get_graphic method requires that some graphic type be defined in the _graphictype attribute. It then tries to call a method whose name incorporates the name of the graphic type; for instance, if the graphic type is "png", the get_graphic method tries to call a graphic drawing method called _drawmap_png .

How might you design such a get_graphic method?

8.3.1 Designing Graphics

There are several ways to construct a PNG graphic to draw a restriction map. You can extract the data necessary using _drawmap_text and its helper methods, _initialize_annotation_text , _add_annotation_text , and _formatmaptext . These methods use the methods of the Restriction class to get the locations of restriction sites for each enzyme and then make a text version of an annotated sequence string that shows the names of the restriction enzymes above their respective restriction sites in the sequence.

Having found the locations of the restriction sites in the sequence, there are many ways you can visually represent the map. You can incorporate a design of double-stranded DNA, for instance, to serve as a background to the actual sequence you can draw on top of the double helical design. You can use colors and different sizes and choices of fonts to make the restriction sites stand out. You can draw the annotation in various ways, perhaps giving the restriction enzyme and the location in some visually appealing way near the restriction site. You may or may not want to draw the entire sequence; perhaps you could truncate long stretches of sequence that have no restriction sites, for instance, to make the relevant information (the restriction site locations) fit better into a smaller, more easily grasped space, ideally on one screen.

The following _drawmap_png method uses the output of the _drawmap_text method and makes a PNG graphic out of it.

 # # Method to output graphics in PNG format # sub _drawmap_png {     my($self) = @_;     # Get text version of graphic     my @maptext = split( /\n+/, $self->_drawmap_text);     # Now make a PNG graphic from the text version     use GD;     #     # Layout information: fonts, margins, image size     #     # Use built-in GD fixed-width font 'gdMediumBoldFont' (could use TrueType fonts)     #     # Font character size in pixels     my ($fontwidth, $fontheight) = (gdMediumBoldFont->width,          gdMediumBoldFont->height);     # Margins top, bottom, right, left, and between lines     my ($tmarg, $bmarg, $rmarg, $lmarg, $linemarg) = (10, 10, 10, 10, 5);     # Image width is length of line times width of a character, plus margins     my ($imagewidth) = (length($maptext[0]) * $fontwidth) + $lmarg + $rmarg;     # Image height is height of font plus margin times number of lines, plus margins     my ($imageheight) = (($fontheight + $linemarg) * (scalar @maptext)) + $tmarg          + $bmarg;     my $image = new GD::Image($imagewidth, $imageheight);     # First one becomes background color     my $white = $image->colorAllocate(255, 255, 255);     my $black = $image->colorAllocate(0, 0, 0);     my $red = $image->colorAllocate(255, 0, 0);     # Origin at upper left hand corner     my ($x, $y) = ($lmarg, $tmarg);     #     # Draw the lines on the image     #     foreach my $line (@maptext) {         chomp $line;         # Draw annotation in red         if($line =~ / /) { #annotation has spaces             $image->string(gdMediumBoldFont, $x, $y, $line, $red);         # Draw sequence in black         }else{ #sequence             $image->string(gdMediumBoldFont, $x, $y, $line, $black);         }         $y += ($fontheight + $linemarg);     }     return $image->png; } 

_drawmap_png has a fairly straightforward logic, given that I just drew the same text as is formatted in the _drawmap_text method. So, it's no surprise that the first step of the _drawmap_png method is to get the formatted text. Recall that the lines of the text are joined into a string so the graphic can be stored in a scalar variable. So, the scalar string is split on newlines back into an array @maptext :

 # Get text version of graphic my @maptext = split( /\n+/, $self->_drawmap_text); 

Now, the GD module is loaded:

 # Now make a PNG graphic from the text version use GD; 

Next, various layout dimensions are calculated:

 # # Layout information: fonts, margins, image size # # Use built-in GD fixed-width font 'gdMediumBoldFont' (could use TrueType fonts) # # Font character size in pixels my ($fontwidth, $fontheight) = (gdMediumBoldFont->width, gdMediumBoldFont->height); # Margins top, bottom, right, left, and between lines my ($tmarg, $bmarg, $rmarg, $lmarg, $linemarg) = (10, 10, 10, 10, 5); # Image width is length of line times width of a character, plus margins my ($imagewidth) = (length($maptext[0]) * $fontwidth) + $lmarg + $rmarg; # Image height is height of font plus margin times number of lines, plus margins my ($imageheight) = (($fontheight + $linemarg) * (scalar @maptext)) + $tmarg + $bmarg; 

GD includes a handful of built-in, fixed-width fonts. It also permits the use of TrueType fonts; however, although they are freely available, high quality, very extensive , and probably available on your system, they are nevertheless not available everywhere. As a result, I've decided to use a built-in font that's always available with GD.

However, as a result, the method has the font name hardcoded in a few places. (See the exercises at the end of the chapter.) For instance, GD built-in font names are objects with methods associated with them, in particular, methods that return the width and height of the font in pixels, which my _drawmap_png method saves in the variables $fontwidth and $fontheight .

Various margins are then set for the page as a whole and for the spacing between lines.

Finally, the overall dimensions of the image are calculated:

 # Image width is length of line times width of a character, plus margins my ($imagewidth) = (length($maptext[0]) * $fontwidth) + $lmarg + $rmarg; # Image height is height of font plus margin times number of lines, plus margins my ($imageheight) = (($fontheight + $linemarg) * (scalar @maptext)) + $tmarg + $bmarg; 

The comments explain the logic. Recall that @maptext is the array that contains the text to be formatted. $maptext[0] is the first line, which is either a full line and thus the maximum width, or it's less than a full line but is the only line, and thus the maximum width. (scalar @maptext) returns the number of lines in the @maptext array (because an array in scalar context returns the size of the array).

After those preliminary calculations, the constructor for the GD class can be called to create a new image object $image of the correct dimensions:

 my $image = new GD::Image($imagewidth, $imageheight); 
8.3.1.1 Applying color

The colors I use can now be allocated for the image object. In GD, the first call to the colorAllocate method determines the background color for the entire image. The triples of numbers are the red, blue, and green values that are combined to form the various possible colors:

 # First one becomes background color my $white = $image->colorAllocate(255, 255, 255); my $black = $image->colorAllocate(0, 0, 0); my $red = $image->colorAllocate(255, 0, 0); 

Next, the x and y (horizontal and vertical) positions are initialized at the origin of the image to be drawn. In GD, the origin of the picture is 0,0 at the upper left corner. Increasing values of x move from left to right across the image; increasing values of y move from top to bottom down the image. Given that I want to have top and left margins, I set the image origin at the upper left corner, offset by the values of the margins:

 # Origin at upper left corner my ($x, $y) = ($lmarg, $tmarg); 

Now, everything is set up, and it's time to draw the image. The text to be drawn is processed line-by-line ; after each line is drawn, the vertical position $y is increased by the height of the font plus the interline margin.

There are two kinds of lines: sequence and annotation. I assume that each annotation line contains some spaces (this won't always be true; see the exercises). Annotation lines are printed as strings in the color red, and sequence lines are printed in the color black.

 # # Draw the lines on the image # foreach my $line (@maptext) {     chomp $line;     # Draw annotation in red     if($line =~ / /) { #annotation has spaces         $image->string(gdMediumBoldFont, $x, $y, $line, $red);         # Draw sequence in black     }else{ #sequence         $image->string(gdMediumBoldFont, $x, $y, $line, $black);     }     $y += ($fontheight + $linemarg); } 
8.3.1.2 Calling the method

Finally, after all the text lines have been formatted and added to the image, the png GD method is called on the image. This produces a PNG image, which is returned from my _drawmap_png method:

 return $image->png; 

Because that method was called by the method _getgraphic , the PNG graphic is both saved in the _graphic attribute and sent off to the user 's browser, which knows how to display a PNG image.

In other circumstances you might want to save the image to a file, which you can do by opening a file, assigning it a filehandle (say IMG), and printing the $image->png output to the file:

 print IMG $image->png; 

8.3.2 Adding JPEG Output to Restrictionmap.pm

Let's consider how to add JPEG output to Restrictionmap.pm . I need a _drawmap_jpg method to handle the case when a JPEG image is requested by the programmer setting the _graphictype attribute to "jpg".

Looking over the GD documentation and the _drawmap_png method I've just written, I see that the last line of my _drawmap_png method:

 return $image->png; 

can be replaced by:

 return $image->jpeg; 

in order to output a JPEG format. So, here's my new _drawmap_jpg method, almost an exact duplicate of _drawmap_png (see the exercises at the end of the chapter).

 # # Method to output graphics in JPEG format # sub _drawmap_jpg {     my($self) = @_;     # Get text version of graphic     my @maptext = split( /\n+/, $self->_drawmap_text);     # Now make a JPEG graphic from the text version     use GD;     #     # Layout information: fonts, margins, image size     #     # Use built-in GD fixed-width font 'gdMediumBoldFont' (could use TrueType fonts)     #     # Font character size in pixels     my ($fontwidth, $fontheight) = (gdMediumBoldFont->width,          gdMediumBoldFont->height);     # Margins top, bottom, right, left, and between lines     my ($tmarg, $bmarg, $rmarg, $lmarg, $linemarg) = (10, 10, 10, 10, 5);     # Image width is length of line times width of a character, plus margins     my ($imagewidth) = (length($maptext[0]) * $fontwidth) + $lmarg + $rmarg;     # Image height is height of font plus margin times number of lines, plus margins     my ($imageheight) =                (($fontheight + $linemarg) * (scalar @maptext)) + $tmarg + $bmarg;     my $image = new GD::Image($imagewidth, $imageheight);     # First one becomes background color     my $white = $image->colorAllocate(255, 255, 255);     my $black = $image->colorAllocate(0, 0, 0);     my $red = $image->colorAllocate(255, 0, 0);     # Origin at upper left hand corner     my ($x, $y) = ($lmarg, $tmarg);     #     # Draw the lines on the image     #     foreach my $line (@maptext) {         chomp $line;         # Draw annotation in red         if($line =~ / /) { #annotation has spaces             $image->string(gdMediumBoldFont, $x, $y, $line, $red);         # Draw sequence in black         }else{ #sequence             $image->string(gdMediumBoldFont, $x, $y, $line, $black);         }         $y += ($fontheight + $linemarg);     }     return $image->jpeg; } 

To use this new method from the webrebase1 CGI Perl script, I'd have to change the requested graphictype attribute to jpeg ; I'd also have to change the HTML header line that gets printed out just before printing the image data. Now, that line is set to:

 print "Content-type: image/png\n\n"; 

but I'd have to change that to:

 print "Content-type: image/jpeg\n\n"; 

You'll notice if you try that out, the JPEG graphic seems a bit blurry around the edges of the letters . This is a result of the compression algorithm that JPEGs use. They tend to work very well with photographic images, but the kind of text and line images that webrebase1 creates do not fare as well under JPEG compression.



Mastering Perl for Bioinformatics
Mastering Perl for Bioinformatics
ISBN: 0596003072
EAN: 2147483647
Year: 2003
Pages: 156

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net