Source: temp/jsdocinputdirs/cercalia.bounds.js

/**
 * @classdesc
 *
 * Class for boundingbox. It's a rectangle with geographical coordinates (WGS84):<br/>
 * `[left, bottom, right, top]`
 * 
 * @constructor
 * @param {Array.<number>} arrCoord Coordinates [left, bottom, right, top] 
 */
cercalia.Bounds = function(arrCoord) {
	
	/**
	 * @private
	 * @type {number}
	 */
	this.left_ = null;
	
	/**
	 * @private
	 * @type {number}
	 */
	this.bottom_ = null;
	
	/**
	 * @private
	 * @type {number}
	 */
	this.right_ = null;
	
	/**
	 * @type {number}
	 * @private
	 */
	this.top_ = null;
	
	/**
	 * @type {Array}
	 * @private
	 */
	this.centerLonLat_ = null;

	if(arrCoord && arrCoord instanceof Array){
		this.initialize_(arrCoord);
	}
	
	/**
	 * Class name
	 * @private
	 * @type {string}
	 */
	this.CLASS_NAME_ = "cercalia.Bounds";
};

/**
 * Returns the object type.
 * @return {string}
 */
cercalia.Bounds.prototype.getClass = function(){
	return this.CLASS_NAME_;
};

/**
 * Cleans the object bounds. It reinitializes.
 */
cercalia.Bounds.prototype.clear = function(){
	this.top_ = null;
	this.right_ = null;
	this.bottom_ = null;
	this.left_ = null;
	this.centerLonLat_ = null;
};

/**
 * @return {boolean} Returns if bounds are empty. (Have not been initialized)
 */
cercalia.Bounds.prototype.isEmpty = function(){
	return this.top_==null && this.right_==null && this.bottom_==null && this.left_==null; 
};

/**
 * Returns the top limit for Y axis.
 * @return {number} top 
 */
cercalia.Bounds.prototype.getTop = function(){
	return this.top_;
};

/**
 * Returns the right limit for X axis.
 * @return {number} right 
 */
cercalia.Bounds.prototype.getRight = function(){
	return this.right_;
};

/**
 * Returns the lower limit for Y axis.
 * @return {number} bottom 
 */
cercalia.Bounds.prototype.getBottom = function(){
	return this.bottom_;
};

/**
 * Returns the left limit for X axis.
 * @return {number} left 
 */
cercalia.Bounds.prototype.getLeft = function(){
	return this.left_;
};

/**
 * Returns the NorthWest coordinate
 * @return {cercalia.LonLat} Northwest coordinate 
 */
cercalia.Bounds.prototype.getNorthWest = function(){
	return new cercalia.LonLat(this.left_, this.top_);
};

/**
 * Returns the Southwest coordinate
 * @return {cercalia.LonLat} Southwest coordinate 
 */
cercalia.Bounds.prototype.getSouthWest = function(){
	return new cercalia.LonLat(this.left_, this.bottom_);
};

/**
 * Returns the NorthEast coordinate
 * @return {Array.<number>} array NorthEast coordinate
 */
cercalia.Bounds.prototype.getNorthEast = function(){
	return new cercalia.LonLat(this.right_, this.top_);
};

/**
 * Returns the SouthEast coordinate
 * @return {cercalia.LonLat} SouthEast coordinate 
 */
cercalia.Bounds.prototype.getSouthEast = function(){
	return new cercalia.LonLat(this.right_, this.bottom_);
};

/**
 * Reset if give a bounds 
 * @private 
 * @param {Array} coordinates [left, bottom, right, top]
 */
cercalia.Bounds.prototype.initialize_ = function(coordinates){
	this.left_ = coordinates[0];
	this.bottom_ = coordinates[1];
	this.right_ = coordinates[2];
	this.top_ = coordinates[3];
};

/**
 * Object clonation. 
 * @return {cercalia.Bounds} Object clone.
 */
cercalia.Bounds.prototype.clone = function(){
	return new cercalia.Bounds([this.left_, this.bottom_, this.right_, this.top_]);
};

/**
 * Returns an object increasing (x0.5) all the limits (top, bottom, left, right)
 * @return {cercalia.Bounds} bounds
 */
cercalia.Bounds.prototype.getDouble = function(){
	
    var dX = this.right_ - this.left_; 
    var dY = this.top_ - this.bottom_;
	
	return new cercalia.Bounds(
			[
			 	this.left_ - (dX/2),
			 	this.bottom_ - (dY/2),
			 	this.right_ + (dX/2),
			 	this.top_ + (dY/2)
			 ]
	);
};

/**
 * @param {number} p_percentage to increase for every limit (top, bottom, left, right)
 * @return {cercalia.Bounds} Returns the object 'cercalia.Bounds' increased
 */
cercalia.Bounds.prototype.cloneIncreasingPercentage = function(p_percentage){
	var percentage = p_percentage/100;
	var dX = this.right_ - this.left_; 
	var dY = this.top_ - this.bottom_;
	return new cercalia.Bounds([this.left_ - (dX*percentage),this.bottom_ - (dY*percentage),this.right_ + (dX*percentage),this.top_ + (dY*percentage)]);
};

/**
 * @param {cercalia.Bounds} bounds
 * @return {boolean} Returns if bounds have the same limits 
 */
cercalia.Bounds.prototype.equals = function(bounds) {
    var equals = false;
    if (bounds != null) {
        equals = (
        		  	(this.left_ == bounds.getLeft()) && 
        		  	(this.right_ == bounds.getRight()) &&
        		  	(this.top_ == bounds.getTop()) && 
        		  	(this.bottom_ == bounds.getBottom())
                 );
    }
    return equals;
};

/**
 * Returns the object in string [left, bottom, right, top]
 * @return {string} Returns a string with the bounds. Format: "left,bottom,right,top"
 */
cercalia.Bounds.prototype.toString = function() {
	return [this.left_, this.bottom_, this.right_, this.top_].join(",");
};

/**
 * @return {Array} Returns an array with the bounds. Format:   [left, bottom, right, top] 
 */
cercalia.Bounds.prototype.toArray = function() {
	return [this.left_, this.bottom_, this.right_, this.top_];
};

/**
 * @return {string} Returns BoundingBox-String "0.41,41.24,0.541,40.51"
 */
cercalia.Bounds.prototype.toBBOX = function() {
	return this.left_ + "," + this.bottom_ + "," + this.right_ + "," + this.top_;
};

/**
 * Width - limits.
 * @return {number} Bounds width
 */
cercalia.Bounds.prototype.getWidth = function(){
	return (this.right_ - this.left_);
};

/**
 * Height - limits.
 * @return {number} Height bounds
 */
cercalia.Bounds.prototype.getHeight = function(){
	return (this.top_ - this.bottom_);
};

/**
 * Array with size limits `[width,height]`
 * @return {number} Size bounds `[width, height]`
 */
cercalia.Bounds.prototype.getSize = function(){
	return [this.getWidth(), this.getHeight()];
};

/**
 * Returns the bounds center
 * @return {cercalia.LonLat} Map center
 */
cercalia.Bounds.prototype.getCenterLonLat = function() {
    if(!this.centerLonLat_) {
        this.centerLonLat_ = [(this.left_ + this.right_) / 2, (this.bottom_ + this.top_) / 2];
    }
    return new cercalia.LonLat(this.centerLonLat_[0],this.centerLonLat_[1]);
};

/**
 * Move the limits horizontally and vertically
 * @param {number} x Displacement X axis
 * @param {number} y Displacement Y axis
 * @return {cercalia.Bounds}  
 */
cercalia.Bounds.prototype.add = function(x,y){
    if ( (x == null) || (y == null) ) {
        alert('Bounds.add cannot receive null values');
    }
    return new cercalia.Bounds([this.left_ + x, this.bottom_ + y, this.right_ + x, this.top_ + y]);
};

/**
 * Expand the limits for including coordinates {ol.Coord} or more limits {cercalia.Bounds}.
 * @param {cercalia.LonLat|cercalia.Bounds} object 
 */
cercalia.Bounds.prototype.extend = function(object) {
    if (object) {
        switch(object.getClass()) {
            case "cercalia.Bounds":
                this.centerLonLat_ = null;

                if ( (this.left_ == null) || (object.getLeft() < this.left_)) {
                    this.left_ = object.getLeft();
                }
                if ( (this.bottom_ == null) || (object.getBottom() < this.bottom_) ) {
                    this.bottom_ = object.getBottom();
                }
                if ( (this.right_ == null) || (object.getRight() > this.right_) ) {
                    this.right_ = object.getRight();
                }
                if ( (this.top_ == null) || (object.getTop() > this.top_) ) {
                    this.top_ = object.getTop();
                }
                break;
            
            case "cercalia.LonLat":
            	this.extendXY(object.getLon(), object.getLat());
            	break;
                
            default: 
            	alert('CLASS_NAME desconocida para extender los "bounds":'+object.getClass());
        }
    }
};

/**
 * Expand the limits for including the coordinate XY {ol.Coord}
 * @param {number} x Longitud
 * @param {number} y Latitud
 */
cercalia.Bounds.prototype.extendXY = function(x, y) {
    // clear cached center location
    this.centerLonLat_ = null;

    if ((this.left_ == null) || (x < this.left_)) {
        this.left_ = x;
    }
    if ((this.bottom_ == null) || (y < this.bottom_)) {
        this.bottom_ = y;
    }
    if ((this.right_ == null) || (x > this.right_)) {
        this.right_ = x;
    }
    if ((this.top_ == null) || (y > this.top_)) {
        this.top_ = y;
    }
};

/**
 * Returns true if a coordinate (cercalia.LonLat object) is inside the bounds
 * @param {cercalia.LonLat} lonLat Coordinate.
 * @return {boolean} `true` or `false`.
 */
cercalia.Bounds.prototype.containsLonLat = function(lonLat) {
    var contains = this.contains(lonLat.getLon(), lonLat.getLat());
    return contains;
};

/**
 * Returns Devuelve si una coordenada se encuentra dentro de los límites 
 * del objeto
 * @param {number} x Latitude.
 * @param {number} y Longitude.
 * @param {boolean} inclusive  Inclusive, with the bounds.
 * @return {boolean} Returns if it's inside the object bounds. 
 */
cercalia.Bounds.prototype.contains = function(x, y, inclusive) {
    //set default
    if (inclusive == null) {
        inclusive = true;
    }

    if (x == null || y == null) {
        return false;
    }

    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;
};

/**
 * Determina si unos limites intersecta con los limites del objeto actual. Se
 * considera que intersectan si uno de sus aristas se encuentran dentro 
 * de los limites del otro
 * @param {cercalia.Bounds} bounds
 * @param {Object} options
 * @return {boolean}
 */
cercalia.Bounds.prototype.intersectsBounds = function(bounds, options) {
    var self = this;
    if (typeof options === "boolean") {
        options =  {inclusive: options};
    }
    options = options || {};
    if (options.worldBounds) {
        self = this.wrapDateLine(options.worldBounds);
        bounds = bounds.wrapDateLine(options.worldBounds);
    } else {
        self = this;
    }
    if (options.inclusive == null) {
        options.inclusive = true;
    }
    var intersects = false;
    var mightTouch = (
        self.left_ == bounds.getRight() ||
        self.right_ == bounds.getLeft() ||
        self.top_ == bounds.getBottom() ||
        self.bottom_ == bounds.getTop()
    );
    
    // if the two bounds only touch at an edge, and inclusive is false,
    // then the bounds don't *really* intersect.
    if (options.inclusive || !mightTouch) {
        // otherwise, if one of the boundaries even partially contains another,
        // inclusive of the edges, then they do intersect.
        var inBottom = (
            ((bounds.getBottom() >= self.bottom_) && (bounds.getBottom() <= self.top_)) ||
            ((self.bottom_ >= bounds.getBottom()) && (self.bottom_ <= bounds.getTop()))
        );
        var inTop = (
            ((bounds.getTop() >= self.bottom_) && (bounds.getTop() <= self.top_)) ||
            ((self.top_ > bounds.getBottom()) && (self.top_ < bounds.getTop()))
        );
        var inLeft = (
            ((bounds.getLeft() >= self.left_) && (bounds.getLeft() <= self.right_)) ||
            ((self.left_ >= bounds.getLeft()) && (self.left_ <= bounds.getRight()))
        );
        var inRight = (
            ((bounds.getRight() >= self.left_) && (bounds.getRight() <= self.right_)) ||
            ((self.right_ >= bounds.getLeft()) && (self.right_ <= bounds.getRight()))
        );
        intersects = ((inBottom || inTop) && (inLeft || inRight));
    }

    if (options.worldBounds && !intersects) {
        var world = options.worldBounds;
        var width = world.getWidth();
        var selfCrosses = !world.containsBounds(self);
        var boundsCrosses = !world.containsBounds(bounds);
        if (selfCrosses && !boundsCrosses) {
            bounds = bounds.add(-width, 0);
            intersects = self.intersectsBounds(bounds, {inclusive: options.inclusive});
        } else if (boundsCrosses && !selfCrosses) {
            self = self.add(-width, 0);
            intersects = bounds.intersectsBounds(self, {inclusive: options.inclusive});                
        }
    }
    return intersects;
};

/**
 * @param {cercalia.Bounds} bounds Bounds.
 * @param {boolean} partial If one destination corner is inside the bounds consider contains the bounds. The default value is false. If false, all the destination bounds must be inside this bounds.
 * @param {boolean} inclusive Include edges. Default value: true
 * @return {boolean} Returns if contains bounds inside object
 */
cercalia.Bounds.prototype.containsBounds = function(bounds, partial, inclusive) {
    if (partial == null) {
        partial = false;
    }
    if (inclusive == null) {
        inclusive = true;
    }
    var bottomLeft  = this.contains(bounds.getLeft(), bounds.getBottom(), inclusive);
    var bottomRight = this.contains(bounds.getRight(), bounds.getBottom(), inclusive);
    var topLeft  = this.contains(bounds.getLeft(), bounds.getTop(), inclusive);
    var topRight = this.contains(bounds.getRight(), bounds.getTop(), inclusive);
    
    return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
                     : (bottomLeft && bottomRight && topLeft && topRight);
};

/**
 * @param {cercalia.Bounds} maxExtent
 * @param {Object} options `options.leftTolerance|options.rightTolerance`
 * @return {cercalia.Bounds} 
 */
cercalia.Bounds.prototype.wrapDateLine = function(maxExtent, options) {    
    options = options || {};
    
    var leftTolerance = options.leftTolerance || 0;
    var rightTolerance = options.rightTolerance || 0;

    var newBounds = this.clone();

    if (maxExtent) {
        var width = maxExtent.getWidth();

        //shift right?
        while (newBounds.getLeft() < maxExtent.getLeft() && 
               newBounds.getRight() - rightTolerance <= maxExtent.getLeft() ) { 
            newBounds = newBounds.add(width, 0);
        }

        //shift left?
        while (newBounds.getLeft() + leftTolerance >= maxExtent.getRight() && 
               newBounds.getRight() > maxExtent.getRight() ) { 
            newBounds = newBounds.add(-width, 0);
        }
       
        // crosses right only? force left
        var newLeft = newBounds.getLeft() + leftTolerance;
        if (newLeft < maxExtent.getRight() && newLeft > maxExtent.getLeft() && 
               newBounds.getRight() - rightTolerance > maxExtent.getRight()) {
            newBounds = newBounds.add(-width, 0);
        }
    }
            
    return newBounds;
};

/**
 * @return distance Bounds width distance in meters.
 */
cercalia.Bounds.prototype.getWidthInMeters = function() {
    return Math.abs(cercalia.Util.distanceTo(this.getNorthEast(), this.getNorthWest()));
};

/**
 * @return distance Bounds height distance in meters.
 */
cercalia.Bounds.prototype.getHeightInMeters = function() {
    return Math.abs(cercalia.Util.distanceTo(this.getNorthWest(), this.getSouthWest()));
};