The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;

my $title = 'Panorama created with Panotools::Script'; # '<%MAPTITLE>'; # whatever
my $apiversion = '2.x'; # '<%APIVERSION>'; # 2.x
my $apikey = 'PUTAPIKEYHERE'; # '<%APIKEY>'; # PUTAPIKEYHERE
my $centrelat = 90 ; # '<%CENTRELAT>'; # 90
my $centrelon = 0 ; # '<%CENTRELON>'; # -180
my $initialzoom = 4; # '<%INITIALZOOM>'; # 4
my $prefix = './'; # test
my $maxzoom = 9; # '<%MAXZOOM>'; # 9
my $maxzoomp1 = 10; # '<%MAXZOOMP1>'; # 10
my $minres = 0; # 4

GetOptions ('title=s' => \$title,
            'apikey=s' => \$apikey,
            'centrelat=s' => \$centrelat,
            'centrelon=s' => \$centrelon,
            'initialzoom=i' => \$initialzoom,
            'prefix=s' => \$prefix,
            'maxzoom=i' => \$maxzoom,
            'maxzoomp1=i' => \$maxzoomp1,
            'minres=i' => \$minres);

print qq@<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml">
  <head>
    <meta http-equiv="content-type" content="text/html/xml; charset=utf-8" />
    <meta name="description" content="Based on GMapImageCutter, Richard Milton, Centre for Advanced Spatial Analysis (CASA), University College London (UCL)" />
    <meta name="keywords" content="Google, Maps, Image, Images, Tile, Cutter, GMapImageCutter, GMapCreator" />

    <title>$title</title>

    <style type="text/css">
    v\\:* {
      behavior:url(#default#VML);
    }
    </style>


    <script src="http://maps.google.com/maps?file=api&amp;v=$apiversion&amp;key=$apikey"
            type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[

    var centreLat=$centrelat;
    var centreLon=$centrelon;
    var initialZoom=$initialzoom;
    var imageWraps=false; //SET THIS TO false TO PREVENT THE IMAGE WRAPPING AROUND
    var map; //the GMap2 itself

/////////////////////
//Custom projection
/////////////////////
    function CustomProjection(a,b){
	this.imageDimension=65536;
	this.pixelsPerLonDegree=[];
	this.pixelOrigin=[];
	this.tileBounds=[];
	this.tileSize=256;
        this.isWrapped=b;
	var b=this.tileSize;
	var c=1;
	for(var d=0;d<a;d++){
          var e=b/2;
          this.pixelsPerLonDegree.push(b/360);
          this.pixelOrigin.push(new GPoint(e,e));
          this.tileBounds.push(c);
          b*=2;
          c*=2
        }
    }
 
    CustomProjection.prototype=new GProjection();
  
    CustomProjection.prototype.fromLatLngToPixel=function(latlng,zoom){
        var c=Math.round(this.pixelOrigin[zoom].x+latlng.lng()*this.pixelsPerLonDegree[zoom]);
        var d=Math.round(this.pixelOrigin[zoom].y+(-2*latlng.lat())*this.pixelsPerLonDegree[zoom]);
        return new GPoint(c,d)
    };

    CustomProjection.prototype.fromPixelToLatLng=function(pixel,zoom,unbounded){
        var d=(pixel.x-this.pixelOrigin[zoom].x)/this.pixelsPerLonDegree[zoom];
        var e=-0.5*(pixel.y-this.pixelOrigin[zoom].y)/this.pixelsPerLonDegree[zoom];
        return new GLatLng(e,d,unbounded)
    };

    CustomProjection.prototype.tileCheckRange=function(tile,zoom,tilesize){
        var tileBounds=this.tileBounds[zoom];
	if (tile.y<0 || tile.y >= tileBounds) {return false;}
        if (this.isWrapped) {
		if (tile.x<0 || tile.x>=tileBounds) { 
			tile.x = tile.x%tileBounds; 
			if (tile.x < 0) {tile.x+=tileBounds} 
		}
	}
	else { 
        	if (tile.x<0 || tile.x>=tileBounds) {return false;}
	}  
  	return true;
    }
      
    CustomProjection.prototype.getWrapWidth=function(zoom) {
        return this.tileBounds[zoom]*this.tileSize;
    }
////////////////////////////////////////////////////////////////////////////

    function customGetTileURL(a,b) {
      //converts tile x,y into keyhole string

      var c=Math.pow(2,b);

        var d=a.x;
        var e=a.y;
        var f="$prefix";
        for(var g=0;g<b;g++){
            c=c/2;
            if(e<c){
                if(d<c){f+="q"}
                else{f+="r";d-=c}
            }
            else{
                if(d<c){f+="t";e-=c}
                else{f+="s";d-=c;e-=c}
            }
        }
        return f+".jpg"
    }


    function getWindowHeight() {
        if (window.self&&self.innerHeight) {
            return self.innerHeight;
        }
        if (document.documentElement&&document.documentElement.clientHeight) {
            return document.documentElement.clientHeight;
        }
        return 0;
    }

    function resizeMapDiv() {
        //Resize the height of the div containing the map.
        //Do not call any map methods here as the resize is called before the map is created.
    	var d=document.getElementById("map");
        var offsetTop=0;
        for (var elem=d; elem!=null; elem=elem.offsetParent) {
            offsetTop+=elem.offsetTop;
        }
        var height=getWindowHeight()-offsetTop-16;
        if (height>=0) {
            d.style.height=height+"px";
        }
    }


    function load() {
      if (GBrowserIsCompatible()) {
        resizeMapDiv();
        var copyright = new GCopyright(1,
                              new GLatLngBounds(new GLatLng(-90, -180),
                                                new GLatLng(90, 180)),
                              0,
                              "<a href=\\"http://hugin.sourceforge.net/\\">$title</a>");
        var copyrightCollection = new GCopyrightCollection("Hugin");
        copyrightCollection.addCopyright(copyright);

        //create a custom picture layer
        var pic_tileLayers = [ new GTileLayer(copyrightCollection , 0, 17)];
        pic_tileLayers[0].getTileUrl = customGetTileURL;
        pic_tileLayers[0].isPng = function() { return false; };
        pic_tileLayers[0].getOpacity = function() { return 1.0; };
        var proj=new CustomProjection($maxzoomp1,imageWraps);
        var pic_customMap = new GMapType(pic_tileLayers, proj, "Pic",
            {maxResolution:$maxzoom, minResolution:$minres, errorMessage:"This space intentionally left blank"});


        //Now create the custom map. Would normally be G_NORMAL_MAP,G_SATELLITE_MAP,G_HYBRID_MAP
        map = new GMap2(document.getElementById("map"),{mapTypes:[pic_customMap]});
        map.addControl(new GLargeMapControl());
        map.addControl(new GMapTypeControl());
	map.addControl(new GOverviewMapControl());
        map.enableDoubleClickZoom();
	map.enableContinuousZoom();
	map.enableScrollWheelZoom();
        map.setCenter(new GLatLng(centreLat, centreLon), initialZoom, pic_customMap);

/////////////////////////////////////////////////////////////////////////////////////
//Add any markers here e.g.
//      map.addOverlay(new GMarker(new GLatLng(x,y)));
/////////////////////////////////////////////////////////////////////////////////////

      }
    }

    //]]>
    </script>
  </head>
  <body onresize="resizeMapDiv()" onload="load()" onunload="GUnload()">
    <div id="map"></div>
  </body>
</html>
@;

__END__

=head1 NAME

gmaptemplate - HTML template for viewing tiled images with the Google Map API

=head1 SYNOPSIS

gmaptemplate > index.html

 Options:
  --title title      The content of the HTML <title> tag
  --prefix prefix    File prefix of tiles relative to root directory
  --apikey key       A Google Maps API key
  --centrelat deg    Latitude -90 to 90
  --centrelon deg    Longitude -180 to 180
  --initialzoom num  Start zoom level
  --maxzoom num      Maximum zoom level
  --maxzoomp1 num    Maximum zoom level +1
  --minres num       Minimum zoom level

=head1 DESCRIPTION

B<gmaptemplate> is a simple tool to write a HTML file based on a template in
the Google Map Image Cutter tool:
L<http://www.casa.ucl.ac.uk/software/googlemapimagecutter.asp>

Note: to use this HTML online you will need a Google Maps API key from
L<http://code.google.com/apis/maps/>

=head1 LICENSE

The Google Map Image Cutter software and documentation is the property of CASA
and University College London. The software is provided free under the terms of
the GNU Lesser General Public License. CASA and University College London do
not accept any liability for any losses arising from the use of this software. 

=head1 SEE ALSO

L<http://hugin.sourceforge.net/>

=head1 AUTHOR

Bruno Postle - January 2010.

=cut