wiki:PerlMapScriptExamples35ex18

MapScript HTML Template

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.


        #!/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 PerlMapScript

Last modified 13 years ago Last modified on Jan 29, 2009, 7:13:22 AM