mapserv.js

Summary

Mapserv class provides support functions for advanced web clients using the MapServer. Performs coordinate management, layer management, and url construction services.


Class Summary
Mapserv This is the basic Mapserv class.

Method Summary
static void do_query()
           function to be called at query time.
static void map_draw()
           function to be called at map draw time.

/**
 * @fileoverview Mapserv class provides support functions for advanced
 * web clients using the MapServer.  Performs coordinate management,
 * layer management, and url construction services.
 */

// Support functions for advanced web clients using the
// MapServer. Original coding 02-25-2000. - SDL -
//
// Re-write for MapServer 3.6+ and DHTML standardization July 2002. - SDL -
// Simplified layer handling with the addition of DHTML legend containers (11/4/2004). - SDL -
// Big-time re-write to de-couple from dbox.js etc... (04/10/2006). - SDL - 

var MAPSERV_UNITS_METERS = 0; // unit types
var MAPSERV_UNITS_FEET = 1;
var MAPSERV_UNITS_MILES = 2;
var MAPSERV_UNITS_KILOMETERS = 3;
var MAPSERV_UNITS_DD = 4;

var MAPSERV_DRAW = 0; // callback types
var MAPSERV_QUERY = 1;

/**
 * Construct a new Mapserv object.
 * @class This is the basic Mapserv class.
 * 
 * @constructor
 * @param {String} mapserver The url for the MapServer instance that will create maps.
 * @param {String} mapfile The mapfile to be used.
 * @param {Double} minx The minimum x coordinate for the initial (default) map extent.
 * @param {Double} miny The minimum y coordinate for the initial (default) map extent.
 * @param {Double} maxx The maximum x coordinate for the initial (default) map extent.
 * @param {Double} maxy The maximum y coordinate for the initial (default) map extent.
 * @param {Integer} width The width (in pixels) of the maps to be created.
 * @param {Integer} height The height (in pixels) of the maps to be created.
 * @return A new mapserv object
 */
function Mapserv(mapserver, mapfile, minx, miny, maxx, maxy, width, height)
{  
  /**
   * mode in which the MapServer executable will be called.

   * <p>Valid values are map, query and nquery although it is certainly possible
   * to create more complex queries outside of the Mapserv object.</p>

   * @type String
   */
  this.mode = 'map'; 

  /**
   * a complete url for a map draw or query operation.
   * @type String
   */
  this.url = '';

  /**
 
   * an associative array of layer status, keyed by layer name; values
   * are booleans.

   * @type Array
   */
  this.layers = new Array(); 

  /**
   * the url for the MapServer executable that will create maps.
   * @type String
   */
  this.mapserver = mapserver;

  /**
   * the url for the MapServer executable that will handle queries.
   * @type String
   */
  this.queryserver = mapserver;

  /**
   * the MapServer configuration file for map draws.
   * @type String
   */
  this.mapfile = mapfile;

  /**
   * the MapServer configuration file for query operations.
   * @type String
   */
  this.queryfile = mapfile;

  /**

   * a four-member array of Doubles defining the rectangular extent of
   * the map (in map coordinates).

   * <p>
   * <ul>
   *  <li>extent[0] - minimum x map coordinate</li>
   *  <li>extent[1] - minimum y map coordinate</li>
   *  <li>extent[2] - maximum x map coordinate</li>
   *  <li>extent[3] - maximum y map coordinate</li>
   * </ul>
   * </p>

   * @type Array
   */
  this.extent = new Array(minx, miny, maxx, maxy);

  /**
   * a two-member array of Doubles defining a point of interest (in map
   * coordinates) to the current operation (typically from a user's
   * mouse click).

   * <p>
   * <ul>
   *  <li>point[0] is x</li>
   *  <li>point[1] is y</li>
   * </ul>
   * </p>

   * @type Array
   */
  this.point = new Array(-1, -1); 

  /**

   * a four-member array of Doubles defining a rectangular extent (in
   * map coordinates) for a query operation.

   * <p>
   * <ul>
   *  <li>queryextent[0] is minimum x</li>
   *  <li>queryextent[1] is minimum y</li>
   *  <li>queryextent[2] is maximum x</li>
   *  <li>queryextent[3] is maximum y</li>
   * </ul>
   * </p>

   * @type Array
   */
  this.queryextent = new Array(-1, -1, -1, -1);

  /**

   * a two-member array of Doubles defining a point of interest (in
   * image coordinates) to the current query operation (typically from
   * a user's mouse click).

   * <p>
   * <ul>
   *  <li>querypoint[0] is x</li>
   *  <li>querypoint[1] is y</li>
   * </ul>
   * </p>

   * @type Array
   */
  this.querypoint = new Array(-1, -1);

  /**
   * the width of the map image (in pixels).
   * @type Integer
   */
  this.width = width;

  /**
   * the height of the map image (in pixels).
   * @type Integer
   */
  this.height = height;

  /**

   * a url query string ("&this=that&some=other") used to pass any
   * application-specific options for a map draw operation that are
   * not covered in the {@link #draw} method.

   * @type String
   */
  this.options = '';

  /**

   * a url query string ("&this=that&some=other") used to store any
   * application-specific options for a query operation that are not
   * covered in the {@link #query} method.

   * @type String
   */
  this.queryoptions = '';

  /**
   * a reference map object.
   * @type Mapserv
   */
  this.referencemap = null;
  
  /**
   * calculated length of a pixel side in map units.
   * @type Double
   */
  this.cellsize = adjustExtent(this.extent, this.width, this.height);

  /**
   * the extent that was passed to this Mapserv instance at construction.
   * @type Array
   */
  this.defaultextent = this.extent;

  /**

   * the zoom factor to be applied on zoom-in operations (the inverse
   * is taken for zoom-out operations).

   */
  this.zoomsize = 2;

  /**
   * direction of zoom.

   * <p>
   * <ul>
   *  <li>-1 = out</li>
   *  <li>0 = none (pan)</li>
   *  <li>1 = in</li>
   * </ul>
   * </p>

   * @type Integer
   */
  this.zoomdir = 0; // pan to start

  /**
   * minimum scale to allow for map draws.

   * <p>value used is denominator of the representative fraction
   * (1:<b>x</b>)</p>

   * <p>value of -1 indicates no minimum</p>

   * @type Integer
   */
  this.minscale = -1;


  /**
   * maximum scale to allow for map draws.

   * <p>value used is denominator of the representative fraction
   * (1:<b>x</b>)</p>

   * <p>value of -1 indicates no maximum</p>

   * @type Integer
   */
  this.maxscale = -1;

  /**
   * amount by which to move the map in automatic pan operations.
   * <p>0 &lt; pansize &lt 1</p>
   * @type Float
   */
  this.pansize = .8;

  /** 
   * a value used in scale calculations.

   * <p>default: 72 (typical monitor resolution)</p>
   * @type Integer
   */
  this.pixelsPerInch = 72;

  /**
   * number of inches in the current map unit.
   * <p>default: 39.3701 (inches in a meter)</p>
   * @type Float
   */
  this.inchesPerMapUnit = 39.3701;

  // handlers/callbacks

  /**
   * function to be called at map draw time.

   * <p>Must be defined and set (via {@link #setHandler}) elsewhere in
   * the application, or nothing happens at draw time.</p>

   * <p>A minimal setup, handling nothing but the map draw, might be:<br>
   * <font size="-1">(note the "ms" variable below is a Mapserv object
   * instance, and "function" is intentionally mispelled to avoid
   * confusing our documentation generator)</font><p>

   * <pre>
   * function map_draw() {
   *   main.setImage(ms.url);
   * }
   * ms.setHandler(MAPSERV_DRAW, map_draw);
   * </pre>

   * <p>Typically, the function would be more complex, handling
   * reference map, scalebar, and legend drawing as well.  Developers
   * are free to implement essentially any desired functionality
   * here.</p>

   * @type Function
   */
  this.drawHandler = null;

  /**
   * function to be called at query time.

   * <p>Must be defined and set (via {@link #setHandler}) elsewhere in
   * the application, or nothing happens at query time.</p>

   * <p>A setup to display query results in a new window might be:<br>
   * <font size="-1">(note the "ms" variable below is a Mapserv object
   * instance, and "function" is intentionally mispelled to avoid
   * confusing our documentation generator)</font><p>

   * <pre>
   * function do_query() {
   *   querywin = window.open(ms.url, 'querywin');
   *   querywin.focus();
   * }
   * ms.setHandler(MAPSERV_QUERY, do_query);
   * </pre>

   * <p>Developers are free to implement essentially any desired
   * functionality here.</p>

   * @type Function
   */
  this.queryHandler = null;

  /**
   * object reference to avoid "this" conflicts with callbacks and event handlers
   * @private
   * @type Mapserv
   */
  var self = this; 

  //
  // private functions
  //

  /**

   * adjust values of given extent to fit {@link #width} and {@link
   * #height}

   * @private
   * @param {Array} extent extent Array to be adjusted
   * @param {Integer} width width of map image in pixels
   * @param {Integer} height of map image in pixels
   * @returns {Double} ground resolution of pixels in map image
   */
  function adjustExtent(extent, width, height) {
    var cellsize = Math.max((extent[2] - extent[0])/width, (extent[3] - extent[1])/height);

    if(cellsize > 0) {
      var ox = Math.max((width - (extent[2] - extent[0])/cellsize)/2,0);
      var oy = Math.max((height - (extent[3] - extent[1])/cellsize)/2,0);

      extent[0] = extent[0] - ox*cellsize;
      extent[1] = extent[1] - oy*cellsize;
      extent[2] = extent[2] + ox*cellsize;
      extent[3] = extent[3] + oy*cellsize;
    }	

    return(cellsize);
  }

  /**
   * convert an extent array to a polygon array.
   * @private
   * @param {Array} extent extent array to be converted
   * @returns {Array} polygon array (essentially a set of five
   * coordinate pairs)
   */
  function extentToPolygon(extent) {
    var polygon = new Array(10);

    polygon[0] = extent[0];
    polygon[1] = extent[3];
    polygon[2] = extent[2];
    polygon[3] = extent[3];
    polygon[4] = extent[2];
    polygon[5] = extent[1];
    polygon[6] = extent[0];
    polygon[7] = extent[1];
    polygon[8] = extent[0];
    polygon[9] = extent[3];

    return(polygon);
  }

  //
  // function prototypes follow (potential callbacks use 'self' instead of 'this')
  //

  /**
   * set the map units.

   * @param {Enumerated} type constant for desired units type.  Valid
   * type values are:

   * <ul>
   * <li>MAPSERV_UNITS_METERS (0)</li>
   * <li>MAPSERV_UNITS_FEET (1)</li>
   * <li>MAPSERV_UNITS_MILES (2)</li>
   * <li>MAPSERV_UNITS_KILOMETERS (3)</li>
   * <li>MAPSERV_UNITS_DD (4)</li>
   * </ul>
   */
  this.setUnits = function(type) {
    if (type == MAPSERV_UNITS_METERS)
      self.inchesPerMapUnit = 39.3701;
		else if (type == MAPSERV_UNITS_FEET)
      self.inchesPerMapUnit = 12.0;
    else if (type == MAPSERV_UNITS_MILES)
      self.inchesPerMapUnit = 63360.0;
    else if (type == MAPSERV_UNITS_KILOMETERS)
      self.inchesPerMapUnit = 39370.1;
    else if (type == MAPSERV_UNITS_DD)
      self.inchesPerMapUnit = 4374754; // at the equator
  }

  /**
   * set a handler function for draw/query operations.

   * @param {Enumerated} type constant for handler type to be set.
   * Valid type values are:

   * <ul>
   * <li>MAPSERV_DRAW (0)</li>
   * <li>MAPSERV_QUERY (1)</li>
   * </ul>


   * @param {Function} handler function to be called

   * @see #drawhandler
   * @see #queryhandler

   */
  this.setHandler = function(type, handler) {
    if (type == MAPSERV_DRAW)
      self.drawHandler = handler;
    else if (type == MAPSERV_QUERY)
      self.queryHandler = handler;    
  }

  /**

   * convert an image coordinate pair to a map coordinate pair, and
   * store in the {@link #point} field.

   * @param {Integer} x the x image coordinate
   * @param {Integer} y the y image coordinate
   */
  this.imageToMap = function(x, y) {
    var dx, dy;

    dx = this.extent[2] - this.extent[0];
    dy = this.extent[3] - this.extent[1];
    this.point[0] = this.extent[0] + this.cellsize*x;
    this.point[1] = this.extent[3] - this.cellsize*y;
  }

  /**
   * set the draw/query status of a layer.
   * @param {String} name The name of the layer to set status for
   * @param {Boolean} status <b>0/false</b> - off; <b>1/true</b> - on
   */
  this.setLayer = function(name, status) {
    self.layers[name] = status;
  }

  /**
   * Set draw/query status for all layers to off.
   */
  this.layersOff = function() {
    self.layers = new Array();
  }

  /**
   * Get the list of currently active layers.
   * @param {String} delimiter the character(s) to use to delimit the returned list
   * @returns A delimited string of active layers
   * @type String
   */
  this.getLayers = function(delimeter) {
    var list = new Array();
    var keys;

    for (key in this.layers)
      if(this.layers[key]) list.push(key);

    return list.join(delimeter);
  }

  /**

   * convert a box defined in image coordinates to an extent defined in
   * map coordinates and store in the {@link #extent} field.

   * @param {Integer} minx minimum x image coordinate
   * @param {Integer} miny minimum y image coordinate
   * @param {Integer} maxx maximum x image coordinate
   * @param {Integer} maxy maximum y image coordinate

   */
  this.applyBox = function(minx, miny, maxx, maxy) {
    var temp = new Array(4);

    temp[0] = this.extent[0] + this.cellsize*minx;
    temp[1] = this.extent[3] - this.cellsize*maxy;
    temp[2] = this.extent[0] + this.cellsize*maxx;	
    temp[3] = this.extent[3] - this.cellsize*miny;

    this.extent = temp;
 
    this.cellsize = adjustExtent(this.extent, this.width, this.height);

    if(this.minscale != -1 && this.getScale() < this.minscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.minscale);
    }
    if(this.maxscale != -1 && this.getScale() > this.maxscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.maxscale);
    }
  }

  /**

  * calculate a new map extent based on {@link #zoomdir}, {@link
  * #zoomsize}, and the passed point (in image coordinates), and store
  * in the {@link #extent} field.

  * @param {Integer} x image coordinate x
  * @param {Integer} y image coordinate y
  */
  this.applyZoom = function(x, y) {
    var dx, dy, mx, my;
    var zoom;

    if(this.zoomdir == 1 && this.zoomsize != 0)
      zoom = this.zoomsize;
    else if(this.zoomdir == -1 && this.zoomsize != 0)
      zoom = 1/this.zoomsize;
    else
      zoom = 1;

    dx = this.extent[2] - this.extent[0];
    dy = this.extent[3] - this.extent[1];
    mx = this.extent[0] + this.cellsize*x; // convert *click* to map coordinates
    my = this.extent[3] - this.cellsize*y;

    this.extent[0] = mx - .5*(dx/zoom);
    this.extent[1] = my - .5*(dy/zoom);
    this.extent[2] = mx + .5*(dx/zoom);
    this.extent[3] = my + .5*(dy/zoom);

    this.cellsize = adjustExtent(this.extent, this.width, this.height);

    if(this.minscale != -1 && this.getScale() < this.minscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.minscale);
    }
    if(this.maxscale != -1 && this.getScale() > this.maxscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.maxscale);
    }
  }


  /**

  * calculate a new map extent to be centered on an image coordinate
  * from the reference map image, and store in the {@link #extent}
  * field.

  * @param {Integer} x reference map image coordinate x value
  * @param {Integer} y reference map image coordinate y value

  */
  this.applyReference = function(x,y) {
    var mx, my;
    var dx, dy;

    if(!this.referencemap) return;

    dx = this.extent[2] - this.extent[0];
    dy = this.extent[3] - this.extent[1];
    mx = this.referencemap.extent[0] + this.referencemap.cellsize*x;
    my = this.referencemap.extent[3] - this.referencemap.cellsize*y;

    this.extent[0] = mx - .5*dx;
    this.extent[1] = my - .5*dy;
    this.extent[2] = mx + .5*dx;
    this.extent[3] = my + .5*dy;

    this.cellsize = adjustExtent(this.extent, this.width, this.height);
  }

  /**

   * construct a four-member query extent array in image coordinates
   * and store in the {@link #queryextent} field.

   * @param {Integer} minx minimum x image coordinate
   * @param {Integer} miny minimum y image coordinate
   * @param {Integer} maxx maximum x image coordinate
   * @param {Integer} maxy maximum y image coordinate
   */
  this.applyBoxQuery = function(minx, miny, maxx, maxy) {
    var temp = new Array(4);

    // convert to map coordinates
    // temp[0] = this.extent[0] + this.cellsize*minx;
    // temp[1] = this.extent[3] - this.cellsize*maxy;
    // temp[2] = this.extent[0] + this.cellsize*maxx;	
    // temp[3] = this.extent[3] - this.cellsize*miny;

    // leave in pixel coordinates
    temp[0] = minx;
    temp[1] = miny;
    temp[2] = maxx;
    temp[3] = maxy;

    this.queryextent = temp;
  }

  /**

   * construct a two-member query point array in image coordinates
   * and store in the {@link #querypoint} field.

   * @param {Integer} x image coordinate x value
   * @param {Integer} y image coordinate y value
   */
  this.applyPointQuery = function(x,y) {
    var dx, dy;

    // convert to map coordinates
    // dx = this.extent[2] - this.extent[0];
    // dy = this.extent[3] - this.extent[1];
    // this.querypoint[0] = this.extent[0] + this.cellsize*x;
    // this.querypoint[1] = this.extent[3] - this.cellsize*y;

    // leave in pixel coordinates
    this.querypoint[0] = x;
    this.querypoint[1] = y;
  }

  /**

  * construct a query url using the current settings of various
  * fields, store in the {@link #url} field, and call the {@link
  * #queryHandler}.

  * <p>Fields/methods used in assembling the url are:</p>

  * <ul>
  * <li>{@link #queryserver}</li>
  * <li>(mode=) {@link #mode}</li>
  * <li>(map=) {@link #queryfile}</li>
  * <li>(imgext=) {@link #extent}</li>
  * <li>(imgxy=) {@link #querypoint}</li>
  * <li>(imgbox=) {@link #queryextent}</li>
  * <li>(imgsize=) {@link #width} {@link #height}</li>
  * <li>(layers=) {@link #getLayers}</li>
  * <li>{@link #queryoptions}</li>
  * </ul>

  */
  this.query = function() {  
    var layerlist = this.getLayers('+');

    // point or box based queries 
    this.url = this.queryserver +
               '?mode=' + this.mode +
               '&map=' + this.queryfile +
	       '&imgext=' +  this.extent.join('+') +
               '&imgxy=' +  this.querypoint.join('+') +            
               '&imgbox=' + this.queryextent.join('+') +
               '&imgsize=' + this.width + '+' + this.height;
 
    if(layerlist) this.url += '&layers=' + layerlist;
    if(this.queryoptions) this.url += this.queryoptions;	   

    if (self.queryHandler) self.queryHandler();

    return;
  }

  /**

  * construct a pair of urls (one for the main map and one for the
  * reference map, if applicable) using the current settings of
  * various fields, store them in the {@link #url} and {@link
  * #referencemap}.url fields, respectively, and call the {@link
  * #drawHandler}.

  * <p>Fields/methods used in assembling the url for the main map
  * are:</p>

  * <ul>
  * <li>{@link #mapserver}</li>
  * <li>(map=) {@link #mapfile}</li>
  * <li>(mapext=) {@link #extent}</li>
  * <li>(mapsize=) {@link #width}+{@link #height}</li>
  * <li>(layers=) {@link #getLayers}</li>
  * <li>{@link #options}</li>
  * </ul>

  * <p>Fields/methods used in assembling the url for the reference map
  * are:</p>

  * <ul>
  * <li>{@link #mapserver}</li>
  * <li>(map=) {@link #referencemap}.mapfile</li>
  * <li>(mapext=) {@link #extent}</li>
  * <li>(mapsize=) {@link #width}+{@link #height}</li>
  * </ul>

  */
  this.draw = function() {
    var layerlist = this.getLayers('+');
 
    if(this.referencemap)
      this.referencemap.url = this.mapserver +
                              '?mode=reference' +
                              '&map=' + this.referencemap.mapfile +
                              '&mapext=' + this.extent.join('+') +
                              '&mapsize=' + this.width + '+' + this.height;  

    this.url = this.mapserver +
               '?mode=map' + 
               '&map=' + this.mapfile +
               '&mapext=' + this.extent.join('+') +
               '&mapsize=' + this.width + '+' + this.height +
               '&layers=' + layerlist +
	       this.options;

    if (self.drawHandler) self.drawHandler();
  }

  /** 

  * redraw the map at the default extent.

  * @see #defaultextent
  */
  this.zoomDefault = function() {
    this.mode = map;
    this.extent = this.defaultextent;
    this.cellsize = adjustExtent(this.extent, this.width, this.height);
    this.draw();
  }

  /**
   * set {@link #extent} using the given values.
   * @param {Double} minx minimum x coordinate
   * @param {Double} miny minimum y coordinate
   * @param {Double} maxx maximum x coordinate
   * @param {Double} maxy maximum y coordinate
   */
  this.setExtent = function(minx, miny, maxx, maxy) {
    this.extent[0] = minx;
    this.extent[1] = miny;
    this.extent[2] = maxx;
    this.extent[3] = maxy;

    this.cellsize = adjustExtent(this.extent, this.width, this.height);

    if(this.minscale != -1 && this.getScale() < this.minscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.minscale);    
    }
    if(this.maxscale != -1 && this.getScale() > this.maxscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.maxscale);
    }
  }

  /**

  * set {@link #extent} to be centered on the given point and minimally
  * fit the circle defined by the point and supplied radius.

  * @private
  * @param {Double} x map coordinate x value
  * @param {Double} y map coordinate y value

  * @param {Double} radius desired distance from given point to edge of map

  */
  this.setExtentFromRadius = function(x, y, radius) {
    this.extent[0] = x - radius;
    this.extent[1] = y - radius;
    this.extent[2] = x + radius;
    this.extent[3] = y + radius;

    this.cellsize = adjustExtent(this.extent, this.width, this.height);

    if(this.minscale != -1 && this.getScale() < this.minscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.minscale);    
    }
    if(this.maxscale != -1 && this.getScale() > this.maxscale) {
      x = (this.extent[2] + this.extent[0])/2;
      y = (this.extent[3] + this.extent[1])/2;
      this.setExtentFromScale(x, y, this.maxscale);
    }
  }


  /**

  * draw a new map centered on the given point, with an {@link #extent}
  * that minimally fits the circle defined by the point and supplied radius.

  * @param {Double} x map coordinate x value
  * @param {Double} y map coordinate y value

  * @param {Double} radius desired distance from given point to edge of map

  */
  this.zoomRadius = function(x, y, radius) {
    this.setExtentFromRadius(x, y, radius);
    this.draw();
  }

  /**
  * get the nominal scale based on current settings.
  * @returns {Double} scale the nominal scale
  */
  this.getScale = function() {
    var gd, md;
  
    md = (this.width-1)/(this.pixelsPerInch*this.inchesPerMapUnit);
    gd = this.extent[2] - this.extent[0];

    return(gd/md);
  }

  /**

  * set {@link #extent} centered on the given point using the given
  * scale.

  * @param {Double} x map coordinate x value
  * @param {Double} y map coordinate y value

  * @param {Integer} scale desired scale, given as denominator of the
  * representative fraction (1:<b>x</b>)

  */
  this.setExtentFromScale = function(x, y, scale) {
    // remove leading 1: and any commas
    // var scale = 1.0*scale.replace(/,|1:/g,"");

    if((this.minscale != -1) && (scale < this.minscale))
      scale = this.minscale;

    if((this.maxscale != -1) && (scale > this.maxscale))
      scale = this.maxscale;

    this.cellsize = (scale/this.pixelsPerInch)/this.inchesPerMapUnit;

    this.extent[0] = x - this.cellsize*this.width/2.0;
    this.extent[1] = y - this.cellsize*this.height/2.0;
    this.extent[2] = x + this.cellsize*this.width/2.0;
    this.extent[3] = y + this.cellsize*this.height/2.0;

    this.cellsize = adjustExtent(this.extent, this.width, this.height);
  }

  /**
   * recenter the map at the given point, using the current scale.
   * @param {Double} x map coordinate x value
   * @param {Double} y map coordinate y value
   */
  this.recenter = function(x, y) { 
    this.setExtentFromScale(x, y, this.getScale());  
    this.draw();
  }

  /**
   * recenter the map at the given point, using the given scale.
   * @param {Double} x map coordinate x value
   * @param {Double} y map coordinate y value

   * @param {Integer} scale desired scale, given as denominator of the
   * representative fraction (1:<b>x</b>)

   */
  this.zoomScale = function(x, y, scale) {  
    this.setExtentFromScale(x, y, scale);  
    this.draw();
  }

  /**

  * zoom in centered on the given point, using the current {@link
  * #zoomsize} setting.

  * @param {Double} x map coordinate x value
  * @param {Double} y map coordinate y value
  */
  this.zoomIn = function(x,y) {
    this.zoomdir = 1;
    this.applyZoom(x,y);  
    this.draw();
    this.zoomdir = 0;
  }

  /**

  * zoom out centered on the given point, using the inverse of the
  * current {@link #zoomsize} setting.

  * @param {Double} x map coordinate x value
  * @param {Double} y map coordinate y value
  */
  this.zoomOut = function(x,y) {
    this.zoomdir = -1;
    this.applyZoom(x,y);
    this.draw();
    this.zoomdir = 0;
  }

  /**

   * pan map in the given direction, using the current {@link
   * #pansize} setting.

   * @param {Enumerated} direction direction to pan. Specified as a
   * lowercase abbreviation of one of the eight cardinal/primary
   * intercardinal directions:
   * <p>
   * <ul>
   * <li>n</li>
   * <li>ne</li>
   * <li>e</li>
   * <li>se</li>
   * <li>s</li>
   * <li>sw</li>
   * <li>w</li>
   * <li>nw</li>
   * </ul>
   * </p>

   */
  this.pan = function(direction) {
    this.zoomdir = 0;

    if(direction == 'n') {
      x = (this.width-1)/2.0;
      y = 0 - this.height*this.pansize + this.height/2.0;
    } else if(direction == 'nw') {
      x = 0 - this.width*this.pansize + this.width/2.0;
      y = 0 - this.height*this.pansize + this.height/2.0;
    } else if(direction == 'ne') {
      x = (this.width-1) + this.width*this.pansize - this.width/2.0;
      y = 0 - this.height*this.pansize + this.height/2.0;
    } else if(direction == 's') {
      x = (this.width-1)/2.0;
      y = (this.height-1) + this.height*this.pansize - this.height/2.0;
    } else if(direction == 'sw') {
      x = 0 - this.width*this.pansize + this.width/2.0;
      y = (this.height-1) + this.height*this.pansize - this.height/2.0;
    } else if(direction == 'se') {
      x = (this.width-1) + this.width*this.pansize - this.width/2.0;
      y = (this.height-1) + this.height*this.pansize - this.height/2.0;
    } else if(direction == 'e') {
      x = (this.width-1) + this.width*this.pansize - this.width/2.0;
      y = (this.height-1)/2.0;
    } else if(direction == 'w') {
      x = 0 - this.width*this.pansize + this.width/2.0;
      y = (this.height-1)/2.0;
    }

    this.applyZoom(x,y);
    this.draw();
  }
}


Documentation generated by JSDoc on Fri May 5 14:01:20 2006