Skip to content

PerlMapScriptExamples35ex18

Thomas Bonfort edited this page Apr 6, 2012 · 2 revisions

Using HTML::Template allows (almost) complete separation of programming from display. Not only is this useful if separating responsibility for programming from html and web design, it is also a cleaner code which allows for easy changes later on as new functionality is added to the application.

There are many templating solutions in the Perl world. I like HTML::Template for its utter simplicity and effectiveness at doing what it claims to do.

The following Perl script/template combo shows a simple example of making a map and displaying it.

#!perl                                                                                                                                                                                                                                                                                                 
        #!/usr/bin/perl -w                                                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                       
        # Import the modules                                                                                                                                                                                                                                                                           
        use strict;                                                                                                                                                                                                                                                                                    
        use CGI::Pretty qw(:standard);                                                                                                                                                                                                                                                                 
        use HTML::Template;                                                                                                                                                                                                                                                                            
        use mapscript;                                                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                       
        # Create new template                                                                                                                                                                                                                                                                          
        my $template = HTML::Template->new( filename => 'index.tmpl' );                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                       
        $ENV{MS_ERRORFILE} = "path/to/mapserver.log";                     # Mapserver error log                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                       
        # create a new map object                                                                                                                                                                                                                                                                      
        my $mapObj = new mapscript::mapObj("mymapfile.map") or die("$mapscript::ms_error->{message}");                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                       
        my @draw_layers = param('draw_layer');                            # layers to be drawn submitted by the user                                                                                                                                                                                   
                                                                          # submitted as form variables, usually via                                                                                                                                                                                   
                                                                          # checkboxes checked or unchecked for the                                                                                                                                                                                    
                                                                          # layers to be drawn.                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                       
        my @layers;                                                       # layers to be sent back to the browser to                                                                                                                                                                                   
                                                                          # construct the set of checkboxes so the                                                                                                                                                                                     
                                                                          # user may choose what to draw                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                       
        my $tool = param("tool") eq "" ? "zoomin" : param("tool");        # currently selected tool                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                       
        my $x = param("map.x");                                           # coordinates of the mouseclick on the map                                                                                                                                                                                   
        my $y = $mapObj->{height} - param("map.y");                       # invert the y coordinate because image                                                                                                                                                                                      
                                                                          # origin is top-left while map origin is                                                                                                                                                                                     
                                                                          # bottom-left                                                                                                                                                                                                                
                                                                                                                                                                                                                                                                                                       
        my $zf = 2;                                                       # zoom in/out by this much                                                                                                                                                                                                   
        my $jitter = 20;                                                  # Catch jerky mouse movement                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                       
        my $mapname_suf = time() . ".png";                                # Tmp map and legend image names                                                                                                                                                                                             
        my $mapimgname = $mapObj->{name} . "_map_" . $mapname_suf;        #                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                       
        # Store max extent                                                                                                                                                                                                                                                                             
        my $mminx = $mapObj->{extent}->{minx};                                                                                                                                                                                                                                                         
        my $mminy = $mapObj->{extent}->{miny};                                                                                                                                                                                                                                                         
        my $mmaxx = $mapObj->{extent}->{maxx};                                                                                                                                                                                                                                                         
        my $mmaxy = $mapObj->{extent}->{maxy};                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                       
        # variables to hold new extent                                                                                                                                                                                                                                                                 
        my ($minx, $miny, $maxx, $maxy);                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                       
        # $currect are the current geo rect coords used to calc the new geo rect coords.                                                                                                                                                                                                               
        # $currect arrives at the server as a hidden form field submitted by the user.                                                                                                                                                                                                                 
        # split the form field value into component bounding coordinates.                                                                                                                                                                                                                              
        my ($f_minx, $f_miny, $f_maxx, $f_maxy) = split(" ", param("currect"));                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                       
        # Check if this is the first visit or a return visit. If $f_minx variable                                                                                                                                                                                                                      
        # (or any of the form variables calculated above) is empty then this is the                                                                                                                                                                                                                    
        # first visit                                                                                                                                                                                                                                                                                  
        if (($f_minx eq "") || ($tool eq "zoomall")) {                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                       
          # make new extent equal to max extent                                                                                                                                                                                                                                                        
          ($minx, $miny, $maxx, $maxy) = ($mminx, $mminy, $mmaxx, $mmaxy);                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                       
                for (0..$mapObj->{numlayers} - 1) {                                                                                                                                                                                                                                                    
                        my $layerObj = $mapObj->getLayer($_);                                                                                                                                                                                                                                          
                        my %layer = (                                                                                                                                                                                                                                                                  
                                                                        'layername' => $layerObj->{name},                                                                                                                                                                                              
                                                                        'layerindx' => $layerObj->{index},                                                                                                                                                                                             
                                                                        'layerchek' => $layerObj->{status} ? "checked" : ""                                                                                                                                                                            
                                                                );                                                                                                                                                                                                                                     
                        push(@layers, \%layer);                                                                                                                                                                                                                                                        
                }                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                       
        } else {                                                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                       
                # Calc the amount of $zf based on the kind of redraw tool.                                                                                                                                                                                                                             
                # Pan will have a $zf of 1, while zoomout will inverse the $zf.                                                                                                                                                                                                                        
                # Leave $zf alone in case of zoomin.                                                                                                                                                                                                                                                   
                if ($tool eq "pan") { $zf = 1; } elsif ($tool eq "zoomput") { $zf = 1 / $zf; }                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                       
          # Calculate cellsize in x and y directions. cellsize is the geographic                                                                                                                                                                                                                       
          # size of each pixel on the screen.                                                                                                                                                                                                                                                          
                my $old_ground_width = $f_maxx - $f_minx;                                                                                                                                                                                                                                              
                my $old_ground_height = $f_maxy - $f_miny;                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                       
          my $cx = $old_ground_width / $mapObj->{width};                                                                                                                                                                                                                                               
          my $cy = $old_ground_height / $mapObj->{height};                                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                       
                my $new_ground_width = $old_ground_width / $zf;                                                                                                                                                                                                                                        
                my $new_ground_height = $old_ground_height / $zf;                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                       
          # use the cellsize and the zoom to calc the new extent                                                                                                                                                                                                                                       
          $minx = ($f_minx + ($x * $cx)) - ($new_ground_width / 2);                                                                                                                                                                                                                                    
                $miny = ($f_miny + ($y * $cy)) - ($new_ground_height / 2);                                                                                                                                                                                                                             
                $maxx = ($f_minx + ($x * $cx)) + ($new_ground_width / 2);                                                                                                                                                                                                                              
                $maxy = ($f_miny + ($y * $cx)) + ($new_ground_height / 2);                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                       
                # now figure out what layers to draw.                                                                                                                                                                                                                                                  
                # loop over all the layers...                                                                                                                                                                                                                                                          
                for (0..$mapObj->{numlayers} - 1) {                                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                                                                                       
                        # get the layer                                                                                                                                                                                                                                                                
                        my $layerObj = $mapObj->getLayer($_);                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                       
                        # toggle layer on if requested by user, otherwise toggle off                                                                                                                                                                                                                   
                        $layerObj->{status} = grep($layerObj->{index} == $_, @draw_layers) ? 1 : 0;                                                                                                                                                                                                    
                        my %layer = (                                                                                                                                                                                                                                                                  
                                                                        'layername' => $layerObj->{name},                                                                                                                                                                                              
                                                                        'layerindx' => $layerObj->{index},                                                                                                                                                                                             
                                                                        'layerchek' => $layerObj->{status} ? "checked" : ""                                                                                                                                                                            
                                                                );                                                                                                                                                                                                                                     
                        push(@layers, \%layer);                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                       
                }                                                                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                                                                                       
        }                                                                                                                                                                                                                                                                                              
                                                                                                                                                                                                                                                                                                       
        # set the new map extent                                                                                                                                                                                                                                                                       
        $mapObj->{extent}->{minx} = $minx; $mapObj->{extent}->{miny} = $miny;                                                                                                                                                                                                                          
        $mapObj->{extent}->{maxx} = $maxx; $mapObj->{extent}->{maxy} = $maxy;                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                       
        # create the map                                                                                                                                                                                                                                                                               
        my $imgObj = $mapObj->draw() or die('Unable to draw map');                                                                                                                                                                                                                                     
        $imgObj->saveImage(                                                                                                                                                                                                                                                                            
                                                                                $mapimgname,                                                                                                                                                                                                           
                                                                                $mapscript::MS_PNG,                                                                                                                                                                                                    
                                                                                $mapObj->{interlace},                                                                                                                                                                                                  
                                                                                $mapObj->{transparent},                                                                                                                                                                                                
                                                                                $mapObj->{imagequality}                                                                                                                                                                                                
                                                                        );                                                                                                                                                                                                                             
                                                                                                                                                                                                                                                                                                       
        # ref to array of layers info to be sent back to the browser                                                                                                                                                                                                                                   
        $template->param(layers => \@layers);                                                                                                                                                                                                                                                          
                                                                                                                                                                                                                                                                                                       
        # create vars to be sent back to the browser                                                                                                                                                                                                                                                   
        $template->param(map_img => "path/to/$mapimgname");                                                                                                                                                                                                                                            
                                                                                                                                                                                                                                                                                                       
        $template->param(currect => "$minx $miny $maxx $maxy");                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                       
        #reset the tool to zoomin, if necessary                                                                                                                                                                                                                                                        
        $template->param(tool => ($tool eq "zoomall") ? "zoomin" : "$tool");                                                                                                                                                                                                                           
                                                                                                                                                                                                                                                                                                       
        # Send the obligatory Content-Type and output the template                                                                                                                                                                                                                                     
        print "Content-Type: text/html\n\n";                                                                                                                                                                                                                                                           
        print $template->output;                                                                                                                                                                                                                                                                       
        exit(0);                                                                                                                                                                                                                                                                                       

Meanwhile, in the template...

        <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                       
        <html>                                                                                                                                                                                                                                                                                         
        <head>                                                                                                                                                                                                                                                                                         
                <title>My Mapping Application</title>                                                                                                                                                                                                                                                  
        </head>                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                       
        <body>                                                                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                       
        <form name="mapform" action="index.cgi" method="post">                                                                                                                                                                                                                                         
                                                                                                                                                                                                                                                                                                       
                <table>                                                                                                                                                                                                                                                                                
                        <!-- start map toolbar -->                                                                                                                                                                                                                                                     
                        <tr>                                                                                                                                                                                                                                                                           
                                <td colspan="2">                                                                                                                                                                                                                                                       
                                        <input type="radio" name="tool" value="zoomin" checked>                                                                                                                                                                                                        
                                        <input type="radio" name="tool" value="zoomout">                                                                                                                                                                                                               
                                        <input type="radio" name="tool" value="zoomall" onClick="document.mapform.submit();">                                                                                                                                                                          
                                        <input type="radio" name="tool" value="pan">                                                                                                                                                                                                                   
                                </td>                                                                                                                                                                                                                                                                  
                        </tr>                                                                                                                                                                                                                                                                          
                        <!-- end map toolbar -->                                                                                                                                                                                                                                                       
                                                                                                                                                                                                                                                                                                       
                        <tr>                                                                                                                                                                                                                                                                           
                                <!-- start checkboxes for layers -->                                                                                                                                                                                                                                   
                                <td>                                                                                                                                                                                                                                                                   
                                <!-- start layers loop -->                                                                                                                                                                                                                                             
                                <tmpl_loop layers>                                                                                                                                                                                                                                                     
                                        <input type="checkbox" name="draw_layer" value="<tmpl_var layerindx>" <tmpl_var layerchek>>                                                                                                                                                                    
                                        <tmpl_var layername>                                                                                                                                                                                                                                           
                                </tmpl_loop>                                                                                                                                                                                                                                                           
                                <!-- end layers loop -->                                                                                                                                                                                                                                               
                                </td>                                                                                                                                                                                                                                                                  
                                <!-- end checkboxes for layers -->                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                                                                                       
                                <!-- start map image -->                                                                                                                                                                                                                                               
                                <td>                                                                                                                                                                                                                                                                   
                                        <input type="image" name="map" src="<tmpl_var map_img>" border="1">                                                                                                                                                                                            
                                        <input type="hidden" name="currect" value="<tmpl_var currect>">                                                                                                                                                                                                
                                        <input type="hidden" name="tool" value="<tmpl_var tool>">                                                                                                                                                                                                      
                                </td>                                                                                                                                                                                                                                                                  
                                <!-- end map image -->                                                                                                                                                                                                                                                 
                        </tr>                                                                                                                                                                                                                                                                          
                </table>                                                                                                                                                                                                                                                                               
                                                                                                                                                                                                                                                                                                       
        </form>                                                                                                                                                                                                                                                                                        
                                                                                                                                                                                                                                                                                                       
        </body>                                                                                                                                                                                                                                                                                        
        </html>                                                                                                                                                                                                                                                                                        

I encourage reading documentation for HTML::Template, and using it or some other templating solution to create web applications. Email me if further help is needed, or ask on the list.

-- Puneet Kishor (pkishor at geoanalytics dot com)

back to PerlMapScrip

Clone this wiki locally