text: /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
   * full list of contributors). Published under the Clear BSD license.  
   * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
   * full text of the license. */
  
  /**
   * @requires OpenLayers/BaseTypes/Class.js
   * @requires OpenLayers/Console.js
   * @requires OpenLayers/Lang.js
   */
  
  /**
   * Class: OpenLayers.Bounds
   * Instances of this class represent bounding boxes.  Data stored as left,
   * bottom, right, top floats. All values are initialized to null, however,
   * you should make sure you set them before using the bounds for anything.
   * 
   * Possible use case:
   * (code)
   *     bounds = new OpenLayers.Bounds();
   *     bounds.extend(new OpenLayers.LonLat(4,5));
   *     bounds.extend(new OpenLayers.LonLat(5,6));
   *     bounds.toBBOX(); // returns 4,5,5,6
   * (end)
   */
  OpenLayers.Bounds = OpenLayers.Class({
  
      /**
       * Property: left
       * {Number} Minimum horizontal coordinate.
       */
      left: null,
  
      /**
       * Property: bottom
       * {Number} Minimum vertical coordinate.
       */
      bottom: null,
  
      /**
       * Property: right
       * {Number} Maximum horizontal coordinate.
       */
      right: null,
  
      /**
       * Property: top
       * {Number} Maximum vertical coordinate.
       */
      top: null,
      
      /**
       * Property: centerLonLat
       * {<OpenLayers.LonLat>} A cached center location.  This should not be
       *     accessed directly.  Use <getCenterLonLat> instead.
       */
      centerLonLat: null,
  
      /**
       * Constructor: OpenLayers.Bounds
       * Construct a new bounds object.
       *
       * Parameters:
       * left - {Number} The left bounds of the box.  Note that for width
       *        calculations, this is assumed to be less than the right value.
       * bottom - {Number} The bottom bounds of the box.  Note that for height
       *          calculations, this is assumed to be more than the top value.
       * right - {Number} The right bounds.
       * top - {Number} The top bounds.
       */
      initialize: function(left, bottom, right, top) {
          if (left != null) {
              this.left = OpenLayers.Util.toFloat(left);
          }
          if (bottom != null) {
              this.bottom = OpenLayers.Util.toFloat(bottom);
          }
          if (right != null) {
              this.right = OpenLayers.Util.toFloat(right);
          }
          if (top != null) {
              this.top = OpenLayers.Util.toFloat(top);
          }
      },
  
      /**
       * Method: clone
       * Create a cloned instance of this bounds.
       *
       * Returns:
       * {<OpenLayers.Bounds>} A fresh copy of the bounds
       */
      clone:function() {
          return new OpenLayers.Bounds(this.left, this.bottom, 
                                       this.right, this.top);
      },
  
      /**
       * Method: equals
       * Test a two bounds for equivalence.
       *
       * Parameters:
       * bounds - {<OpenLayers.Bounds>}
       *
       * Returns:
       * {Boolean} The passed-in bounds object has the same left,
       *           right, top, bottom components as this.  Note that if bounds 
       *           passed in is null, returns false.
       */
      equals:function(bounds) {
          var equals = false;
          if (bounds != null) {
              equals = ((this.left == bounds.left) && 
                        (this.right == bounds.right) &&
                        (this.top == bounds.top) && 
                        (this.bottom == bounds.bottom));
          }
          return equals;
      },
  
      /** 
       * APIMethod: toString
       * 
       * Returns:
       * {String} String representation of bounds object. 
       */
      toString:function() {
          return [this.left, this.bottom, this.right, this.top].join(",");
      },
  
      /**
       * APIMethod: toArray
       *
       * Parameters:
       * reverseAxisOrder - {Boolean} Should we reverse the axis order?
       *
       * Returns:
       * {Array} array of left, bottom, right, top
       */
      toArray: function(reverseAxisOrder) {
          if (reverseAxisOrder === true) {
              return [this.bottom, this.left, this.top, this.right];
          } else {
              return [this.left, this.bottom, this.right, this.top];
          }
      },    
  
      /** 
       * APIMethod: toBBOX
       * 
       * Parameters:
       * decimal - {Integer} How many significant digits in the bbox coords?
       *                     Default is 6
       * reverseAxisOrder - {Boolean} Should we reverse the axis order?
       * 
       * Returns:
       * {String} Simple String representation of bounds object.
       *          (e.g. <i>"5,42,10,45"</i>)
       */
      toBBOX:function(decimal, reverseAxisOrder) {
          if (decimal== null) {
              decimal = 6; 
          }
          var mult = Math.pow(10, decimal);
          var xmin = Math.round(this.left * mult) / mult;
          var ymin = Math.round(this.bottom * mult) / mult;
          var xmax = Math.round(this.right * mult) / mult;
          var ymax = Math.round(this.top * mult) / mult;
          if (reverseAxisOrder === true) {
              return ymin + "," + xmin + "," + ymax + "," + xmax;
          } else {
              return xmin + "," + ymin + "," + xmax + "," + ymax;
          }
      },
   
      /**
       * APIMethod: toGeometry
       * Create a new polygon geometry based on this bounds.
       *
       * Returns:
       * {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates
       *     of this bounds.
       */
      toGeometry: function() {
          return new OpenLayers.Geometry.Polygon([
              new OpenLayers.Geometry.LinearRing([
                  new OpenLayers.Geometry.Point(this.left, this.bottom),
                  new OpenLayers.Geometry.Point(this.right, this.bottom),
                  new OpenLayers.Geometry.Point(this.right, this.top),
                  new OpenLayers.Geometry.Point(this.left, this.top)
              ])
          ]);
      },
      
      /**
       * APIMethod: getWidth
       * 
       * Returns:
       * {Float} The width of the bounds
       */
      getWidth:function() {
          return (this.right - this.left);
      },
  
      /**
       * APIMethod: getHeight
       * 
       * Returns:
       * {Float} The height of the bounds (top minus bottom).
       */
      getHeight:function() {
          return (this.top - this.bottom);
      },
  
      /**
       * APIMethod: getSize
       * 
       * Returns:
       * {<OpenLayers.Size>} The size of the box.
       */
      getSize:function() {
          return new OpenLayers.Size(this.getWidth(), this.getHeight());
      },
  
      /**
       * APIMethod: getCenterPixel
       * 
       * Returns:
       * {<OpenLayers.Pixel>} The center of the bounds in pixel space.
       */
      getCenterPixel:function() {
          return new OpenLayers.Pixel( (this.left + this.right) / 2,
                                       (this.bottom + this.top) / 2);
      },
  
      /**
       * APIMethod: getCenterLonLat
       * 
       * Returns:
       * {<OpenLayers.LonLat>} The center of the bounds in map space.
       */
      getCenterLonLat:function() {
          if(!this.centerLonLat) {
              this.centerLonLat = new OpenLayers.LonLat(
                  (this.left + this.right) / 2, (this.bottom + this.top) / 2
              );
          }
          return this.centerLonLat;
      },
  
      /**
       * APIMethod: scale
       * Scales the bounds around a pixel or lonlat. Note that the new 
       *     bounds may return non-integer properties, even if a pixel
       *     is passed. 
       * 
       * Parameters:
       * ratio - {Float} 
       * origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
       *          Default is center.
       *
       * Returns:
       * {<OpenLayers.Bounds>} A new bounds that is scaled by ratio
       *                      from origin.
       */
      scale: function(ratio, origin){
          if(origin == null){
              origin = this.getCenterLonLat();
          }
          
          var origx,origy;
  
          // get origin coordinates
          if(origin.CLASS_NAME == "OpenLayers.LonLat"){
              origx = origin.lon;
              origy = origin.lat;
          } else {
              origx = origin.x;
              origy = origin.y;
          }
  
          var left = (this.left - origx) * ratio + origx;
          var bottom = (this.bottom - origy) * ratio + origy;
          var right = (this.right - origx) * ratio + origx;
          var top = (this.top - origy) * ratio + origy;
          
          return new OpenLayers.Bounds(left, bottom, right, top);
      },
  
      /**
       * APIMethod: add
       * 
       * Parameters:
       * x - {Float}
       * y - {Float}
       * 
       * Returns:
       * {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
       *     this, but shifted by the passed-in x and y values.
       */
      add:function(x, y) {
          if ( (x == null) || (y == null) ) {
              var msg = OpenLayers.i18n("boundsAddError");
              OpenLayers.Console.error(msg);
              return null;
          }
          return new OpenLayers.Bounds(this.left + x, this.bottom + y,
                                       this.right + x, this.top + y);
      },
      
      /**
       * APIMethod: extend
       * Extend the bounds to include the point, lonlat, or bounds specified.
       *     Note, this function assumes that left < right and bottom < top.
       * 
       * Parameters: 
       * object - {Object} Can be LonLat, Point, or Bounds
       */
      extend:function(object) {
          var bounds = null;
          if (object) {
              // clear cached center location
              switch(object.CLASS_NAME) {
                  case "OpenLayers.LonLat":    
                      bounds = new OpenLayers.Bounds(object.lon, object.lat,
                                                      object.lon, object.lat);
                      break;
                  case "OpenLayers.Geometry.Point":
                      bounds = new OpenLayers.Bounds(object.x, object.y,
                                                      object.x, object.y);
                      break;
                      
                  case "OpenLayers.Bounds":    
                      bounds = object;
                      break;
              }
      
              if (bounds) {
                  this.centerLonLat = null;
                  if ( (this.left == null) || (bounds.left < this.left)) {
                      this.left = bounds.left;
                  }
                  if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) {
                      this.bottom = bounds.bottom;
                  } 
                  if ( (this.right == null) || (bounds.right > this.right) ) {
                      this.right = bounds.right;
                  }
                  if ( (this.top == null) || (bounds.top > this.top) ) { 
                      this.top = bounds.top;
                  }
              }
          }
      },
  
      /**
       * APIMethod: containsLonLat
       * 
       * Parameters:
       * ll - {<OpenLayers.LonLat>}
       * inclusive - {Boolean} Whether or not to include the border.
       *     Default is true.
       *
       * Returns:
       * {Boolean} The passed-in lonlat is within this bounds.
       */
      containsLonLat:function(ll, inclusive) {
          return this.contains(ll.lon, ll.lat, inclusive);
      },
  
      /**
       * APIMethod: containsPixel
       * 
       * Parameters:
       * px - {<OpenLayers.Pixel>}
       * inclusive - {Boolean} Whether or not to include the border. Default is
       *     true.
       *
       * Returns:
       * {Boolean} The passed-in pixel is within this bounds.
       */
      containsPixel:function(px, inclusive) {
          return this.contains(px.x, px.y, inclusive);
      },
      
      /**
       * APIMethod: contains
       * 
       * Parameters:
       * x - {Float}
       * y - {Float}
       * inclusive - {Boolean} Whether or not to include the border. Default is
       *     true.
       *
       * Returns:
       * {Boolean} Whether or not the passed-in coordinates are within this
       *     bounds.
       */
      contains:function(x, y, inclusive) {
          //set default
          if (inclusive == null) {
              inclusive = true;
          }
  
          if (x == null || y == null) {
              return false;
          }
  
          x = OpenLayers.Util.toFloat(x);
          y = OpenLayers.Util.toFloat(y);
  
          var contains = false;
          if (inclusive) {
              contains = ((x >= this.left) && (x <= this.right) && 
                          (y >= this.bottom) && (y <= this.top));
          } else {
              contains = ((x > this.left) && (x < this.right) && 
                          (y > this.bottom) && (y < this.top));
          }              
          return contains;
      },
  
      /**
       * APIMethod: intersectsBounds
       * Determine whether the target bounds intersects this bounds.  Bounds are
       *     considered intersecting if any of their edges intersect or if one
       *     bounds contains the other.
       * 
       * Parameters:
       * bounds - {<OpenLayers.Bounds>} The target bounds.
       * inclusive - {Boolean} Treat coincident borders as intersecting.  Default
       *     is true.  If false, bounds that do not overlap but only touch at the
       *     border will not be considered as intersecting.
       *
       * Returns:
       * {Boolean} The passed-in bounds object intersects this bounds.
       */
      intersectsBounds:function(bounds, inclusive) {
          if (inclusive == null) {
              inclusive = true;
          }
          var intersects = false;
          var mightTouch = (
              this.left == bounds.right ||
              this.right == bounds.left ||
              this.top == bounds.bottom ||
              this.bottom == bounds.top
          );
          
          // if the two bounds only touch at an edge, and inclusive is false,
          // then the bounds don't *really* intersect.
          if (inclusive || !mightTouch) {
              // otherwise, if one of the boundaries even partially contains another,
              // inclusive of the edges, then they do intersect.
              var inBottom = (
                  ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
                  ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
              );
              var inTop = (
                  ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
                  ((this.top > bounds.bottom) && (this.top < bounds.top))
              );
              var inLeft = (
                  ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
                  ((this.left >= bounds.left) && (this.left <= bounds.right))
              );
              var inRight = (
                  ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
                  ((this.right >= bounds.left) && (this.right <= bounds.right))
              );
              intersects = ((inBottom || inTop) && (inLeft || inRight));
          }
          return intersects;
      },
      
      /**
       * APIMethod: containsBounds
       * Determine whether the target bounds is contained within this bounds.
       * 
       * bounds - {<OpenLayers.Bounds>} The target bounds.
       * partial - {Boolean} If any of the target corners is within this bounds
       *     consider the bounds contained.  Default is false.  If false, the
       *     entire target bounds must be contained within this bounds.
       * inclusive - {Boolean} Treat shared edges as contained.  Default is
       *     true.
       *
       * Returns:
       * {Boolean} The passed-in bounds object is contained within this bounds. 
       */
      containsBounds:function(bounds, partial, inclusive) {
          if (partial == null) {
              partial = false;
          }
          if (inclusive == null) {
              inclusive = true;
          }
          var bottomLeft  = this.contains(bounds.left, bounds.bottom, inclusive);
          var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
          var topLeft  = this.contains(bounds.left, bounds.top, inclusive);
          var topRight = this.contains(bounds.right, bounds.top, inclusive);
          
          return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
                           : (bottomLeft && bottomRight && topLeft && topRight);
      },
  
      /** 
       * APIMethod: determineQuadrant
       * 
       * Parameters:
       * lonlat - {<OpenLayers.LonLat>}
       * 
       * Returns:
       * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
       *     coordinate lies.
       */
      determineQuadrant: function(lonlat) {
      
          var quadrant = "";
          var center = this.getCenterLonLat();
          
          quadrant += (lonlat.lat < center.lat) ? "b" : "t";
          quadrant += (lonlat.lon < center.lon) ? "l" : "r";
      
          return quadrant; 
      },
      
      /**
       * APIMethod: transform
       * Transform the Bounds object from source to dest. 
       *
       * Parameters: 
       * source - {<OpenLayers.Projection>} Source projection. 
       * dest   - {<OpenLayers.Projection>} Destination projection. 
       *
       * Returns:
       * {<OpenLayers.Bounds>} Itself, for use in chaining operations.
       */
      transform: function(source, dest) {
          // clear cached center location
          this.centerLonLat = null;
          var ll = OpenLayers.Projection.transform(
              {'x': this.left, 'y': this.bottom}, source, dest);
          var lr = OpenLayers.Projection.transform(
              {'x': this.right, 'y': this.bottom}, source, dest);
          var ul = OpenLayers.Projection.transform(
              {'x': this.left, 'y': this.top}, source, dest);
          var ur = OpenLayers.Projection.transform(
              {'x': this.right, 'y': this.top}, source, dest);
          this.left   = Math.min(ll.x, ul.x);
          this.bottom = Math.min(ll.y, lr.y);
          this.right  = Math.max(lr.x, ur.x);
          this.top    = Math.max(ul.y, ur.y);
          return this;
      },
  
      /**
       * APIMethod: wrapDateLine
       *  
       * Parameters:
       * maxExtent - {<OpenLayers.Bounds>}
       * options - {Object} Some possible options are:
       *
       * Allowed Options:
       *                    leftTolerance - {float} Allow for a margin of error 
       *                                            with the 'left' value of this 
       *                                            bound.
       *                                            Default is 0.
       *                    rightTolerance - {float} Allow for a margin of error 
       *                                             with the 'right' value of 
       *                                             this bound.
       *                                             Default is 0.
       * 
       * Returns:
       * {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the 
       *                       "dateline" (as specified by the borders of 
       *                       maxExtent). Note that this function only returns 
       *                       a different bounds value if this bounds is 
       *                       *entirely* outside of the maxExtent. If this 
       *                       bounds straddles the dateline (is part in/part 
       *                       out of maxExtent), the returned bounds will be 
       *                       merely a copy of this one.
       */
      wrapDateLine: function(maxExtent, options) {    
          options = options || {};
          
          var leftTolerance = options.leftTolerance || 0;
          var rightTolerance = options.rightTolerance || 0;
  
          var newBounds = this.clone();
      
          if (maxExtent) {
  
             //shift right?
             while ( newBounds.left < maxExtent.left && 
                     (newBounds.right - rightTolerance) <= maxExtent.left ) { 
                  newBounds = newBounds.add(maxExtent.getWidth(), 0);
             }
  
             //shift left?
             while ( (newBounds.left + leftTolerance) >= maxExtent.right && 
                     newBounds.right > maxExtent.right ) { 
                  newBounds = newBounds.add(-maxExtent.getWidth(), 0);
             }
          }
                  
          return newBounds;
      },
  
      CLASS_NAME: "OpenLayers.Bounds"
  });
  
  /** 
   * APIFunction: fromString
   * Alternative constructor that builds a new OpenLayers.Bounds from a 
   *     parameter string
   * 
   * Parameters: 
   * str - {String}Comma-separated bounds string. (e.g. <i>"5,42,10,45"</i>)
   * reverseAxisOrder - {Boolean} Does the string use reverse axis order?
   * 
   * Returns:
   * {<OpenLayers.Bounds>} New bounds object built from the 
   *                       passed-in String.
   */
  OpenLayers.Bounds.fromString = function(str, reverseAxisOrder) {
      var bounds = str.split(",");
      return OpenLayers.Bounds.fromArray(bounds, reverseAxisOrder);
  };
  
  /** 
   * APIFunction: fromArray
   * Alternative constructor that builds a new OpenLayers.Bounds
   *     from an array
   * 
   * Parameters:
   * bbox - {Array(Float)} Array of bounds values (e.g. <i>[5,42,10,45]</i>)
   * reverseAxisOrder - {Boolean} Does the array use reverse axis order?
   *
   * Returns:
   * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
   */
  OpenLayers.Bounds.fromArray = function(bbox, reverseAxisOrder) {
      return reverseAxisOrder === true ?
             new OpenLayers.Bounds(parseFloat(bbox[1]),
                                   parseFloat(bbox[0]),
                                   parseFloat(bbox[3]),
                                   parseFloat(bbox[2])) :
             new OpenLayers.Bounds(parseFloat(bbox[0]),
                                   parseFloat(bbox[1]),
                                   parseFloat(bbox[2]),
                                   parseFloat(bbox[3]));
  };
  
  /** 
   * APIFunction: fromSize
   * Alternative constructor that builds a new OpenLayers.Bounds
   *     from a size
   * 
   * Parameters:
   * size - {<OpenLayers.Size>} 
   *
   * Returns:
   * {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
   */
  OpenLayers.Bounds.fromSize = function(size) {
      return new OpenLayers.Bounds(0,
                                   size.h,
                                   size.w,
                                   0);
  };
  
  /**
   * Function: oppositeQuadrant
   * Get the opposite quadrant for a given quadrant string.
   *
   * Parameters:
   * quadrant - {String} two character quadrant shortstring
   *
   * Returns:
   * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
   *          you pass in "bl" it returns "tr", if you pass in "br" it 
   *          returns "tl", etc.
   */
  OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
      var opp = "";
      
      opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
      opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
      
      return opp;
  };
  