/**
* @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()));
};