1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2 * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3 * full text of the license. */
6 * @requires OpenLayers/Console.js
10 * Class: OpenLayers.Bounds
11 * Instances of this class represent bounding boxes. Data stored as left,
12 * bottom, right, top floats. All values are initialized to null, however,
13 * you should make sure you set them before using the bounds for anything.
16 * > bounds = new OpenLayers.Bounds();
17 * > bounds.extend(new OpenLayers.LonLat(4,5));
18 * > bounds.extend(new OpenLayers.LonLat(5,6));
19 * > bounds.toBBOX(); // returns 4,5,5,6
21 OpenLayers.Bounds = OpenLayers.Class({
25 * {Number} Minimum horizontal coordinate.
31 * {Number} Minimum vertical coordinate.
37 * {Number} Maximum horizontal coordinate.
43 * {Number} Maximum vertical coordinate.
48 * Property: centerLonLat
49 * {<OpenLayers.LonLat>} A cached center location. This should not be
50 * accessed directly. Use <getCenterLonLat> instead.
55 * Constructor: OpenLayers.Bounds
56 * Construct a new bounds object.
59 * left - {Number} The left bounds of the box. Note that for width
60 * calculations, this is assumed to be less than the right value.
61 * bottom - {Number} The bottom bounds of the box. Note that for height
62 * calculations, this is assumed to be more than the top value.
63 * right - {Number} The right bounds.
64 * top - {Number} The top bounds.
66 initialize: function(left, bottom, right, top) {
68 this.left = OpenLayers.Util.toFloat(left);
71 this.bottom = OpenLayers.Util.toFloat(bottom);
74 this.right = OpenLayers.Util.toFloat(right);
77 this.top = OpenLayers.Util.toFloat(top);
83 * Create a cloned instance of this bounds.
86 * {<OpenLayers.Bounds>} A fresh copy of the bounds
89 return new OpenLayers.Bounds(this.left, this.bottom,
90 this.right, this.top);
95 * Test a two bounds for equivalence.
98 * bounds - {<OpenLayers.Bounds>}
101 * {Boolean} The passed-in bounds object has the same left,
102 * right, top, bottom components as this. Note that if bounds
103 * passed in is null, returns false.
105 equals:function(bounds) {
107 if (bounds != null) {
108 equals = ((this.left == bounds.left) &&
109 (this.right == bounds.right) &&
110 (this.top == bounds.top) &&
111 (this.bottom == bounds.bottom));
117 * APIMethod: toString
120 * {String} String representation of bounds object.
121 * (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
123 toString:function() {
124 return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
125 + " right-top=(" + this.right + "," + this.top + ")" );
132 * {Array} array of left, bottom, right, top
134 toArray: function() {
135 return [this.left, this.bottom, this.right, this.top];
142 * decimal - {Integer} How many significant digits in the bbox coords?
146 * {String} Simple String representation of bounds object.
147 * (ex. <i>"5,42,10,45"</i>)
149 toBBOX:function(decimal) {
150 if (decimal== null) {
153 var mult = Math.pow(10, decimal);
154 var bbox = Math.round(this.left * mult) / mult + "," +
155 Math.round(this.bottom * mult) / mult + "," +
156 Math.round(this.right * mult) / mult + "," +
157 Math.round(this.top * mult) / mult;
163 * APIMethod: toGeometry
164 * Create a new polygon geometry based on this bounds.
167 * {<OpenLayers.Geometry.Polygon>} A new polygon with the coordinates
170 toGeometry: function() {
171 return new OpenLayers.Geometry.Polygon([
172 new OpenLayers.Geometry.LinearRing([
173 new OpenLayers.Geometry.Point(this.left, this.bottom),
174 new OpenLayers.Geometry.Point(this.right, this.bottom),
175 new OpenLayers.Geometry.Point(this.right, this.top),
176 new OpenLayers.Geometry.Point(this.left, this.top)
182 * APIMethod: getWidth
185 * {Float} The width of the bounds
187 getWidth:function() {
188 return (this.right - this.left);
192 * APIMethod: getHeight
195 * {Float} The height of the bounds (top minus bottom).
197 getHeight:function() {
198 return (this.top - this.bottom);
205 * {<OpenLayers.Size>} The size of the box.
208 return new OpenLayers.Size(this.getWidth(), this.getHeight());
212 * APIMethod: getCenterPixel
215 * {<OpenLayers.Pixel>} The center of the bounds in pixel space.
217 getCenterPixel:function() {
218 return new OpenLayers.Pixel( (this.left + this.right) / 2,
219 (this.bottom + this.top) / 2);
223 * APIMethod: getCenterLonLat
226 * {<OpenLayers.LonLat>} The center of the bounds in map space.
228 getCenterLonLat:function() {
229 if(!this.centerLonLat) {
230 this.centerLonLat = new OpenLayers.LonLat(
231 (this.left + this.right) / 2, (this.bottom + this.top) / 2
234 return this.centerLonLat;
239 * Scales the bounds around a pixel or lonlat. Note that the new
240 * bounds may return non-integer properties, even if a pixel
245 * origin - {<OpenLayers.Pixel> or <OpenLayers.LonLat>}
249 * {<OpenLayers.Bound>} A new bounds that is scaled by ratio
253 scale: function(ratio, origin){
255 origin = this.getCenterLonLat();
262 // get origin coordinates
263 if(origin.CLASS_NAME == "OpenLayers.LonLat"){
271 var left = (this.left - origx) * ratio + origx;
272 var bottom = (this.bottom - origy) * ratio + origy;
273 var right = (this.right - origx) * ratio + origx;
274 var top = (this.top - origy) * ratio + origy;
276 return new OpenLayers.Bounds(left, bottom, right, top);
287 * {<OpenLayers.Bounds>} A new bounds whose coordinates are the same as
288 * this, but shifted by the passed-in x and y values.
291 if ( (x == null) || (y == null) ) {
292 var msg = OpenLayers.i18n("boundsAddError");
293 OpenLayers.Console.error(msg);
296 return new OpenLayers.Bounds(this.left + x, this.bottom + y,
297 this.right + x, this.top + y);
302 * Extend the bounds to include the point, lonlat, or bounds specified.
303 * Note, this function assumes that left < right and bottom < top.
306 * object - {Object} Can be LonLat, Point, or Bounds
308 extend:function(object) {
311 // clear cached center location
312 switch(object.CLASS_NAME) {
313 case "OpenLayers.LonLat":
314 bounds = new OpenLayers.Bounds(object.lon, object.lat,
315 object.lon, object.lat);
317 case "OpenLayers.Geometry.Point":
318 bounds = new OpenLayers.Bounds(object.x, object.y,
322 case "OpenLayers.Bounds":
328 this.centerLonLat = null;
329 if ( (this.left == null) || (bounds.left < this.left)) {
330 this.left = bounds.left;
332 if ( (this.bottom == null) || (bounds.bottom < this.bottom) ) {
333 this.bottom = bounds.bottom;
335 if ( (this.right == null) || (bounds.right > this.right) ) {
336 this.right = bounds.right;
338 if ( (this.top == null) || (bounds.top > this.top) ) {
339 this.top = bounds.top;
346 * APIMethod: containsLonLat
349 * ll - {<OpenLayers.LonLat>}
350 * inclusive - {Boolean} Whether or not to include the border.
354 * {Boolean} The passed-in lonlat is within this bounds.
356 containsLonLat:function(ll, inclusive) {
357 return this.contains(ll.lon, ll.lat, inclusive);
361 * APIMethod: containsPixel
364 * px - {<OpenLayers.Pixel>}
365 * inclusive - {Boolean} Whether or not to include the border. Default is
369 * {Boolean} The passed-in pixel is within this bounds.
371 containsPixel:function(px, inclusive) {
372 return this.contains(px.x, px.y, inclusive);
376 * APIMethod: contains
381 * inclusive - {Boolean} Whether or not to include the border. Default is
385 * {Boolean} Whether or not the passed-in coordinates are within this
388 contains:function(x, y, inclusive) {
390 if (inclusive == null) {
394 if (x == null || y == null) {
398 x = OpenLayers.Util.toFloat(x);
399 y = OpenLayers.Util.toFloat(y);
401 var contains = false;
403 contains = ((x >= this.left) && (x <= this.right) &&
404 (y >= this.bottom) && (y <= this.top));
406 contains = ((x > this.left) && (x < this.right) &&
407 (y > this.bottom) && (y < this.top));
413 * APIMethod: intersectsBounds
414 * Determine whether the target bounds intersects this bounds. Bounds are
415 * considered intersecting if any of their edges intersect or if one
416 * bounds contains the other.
419 * bounds - {<OpenLayers.Bounds>} The target bounds.
420 * inclusive - {Boolean} Treat coincident borders as intersecting. Default
421 * is true. If false, bounds that do not overlap but only touch at the
422 * border will not be considered as intersecting.
425 * {Boolean} The passed-in bounds object intersects this bounds.
427 intersectsBounds:function(bounds, inclusive) {
428 if (inclusive == null) {
431 var intersects = false;
433 this.left == bounds.right ||
434 this.right == bounds.left ||
435 this.top == bounds.bottom ||
436 this.bottom == bounds.top
439 // if the two bounds only touch at an edge, and inclusive is false,
440 // then the bounds don't *really* intersect.
441 if (inclusive || !mightTouch) {
442 // otherwise, if one of the boundaries even partially contains another,
443 // inclusive of the edges, then they do intersect.
445 ((bounds.bottom >= this.bottom) && (bounds.bottom <= this.top)) ||
446 ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top))
449 ((bounds.top >= this.bottom) && (bounds.top <= this.top)) ||
450 ((this.top > bounds.bottom) && (this.top < bounds.top))
453 ((bounds.left >= this.left) && (bounds.left <= this.right)) ||
454 ((this.left >= bounds.left) && (this.left <= bounds.right))
457 ((bounds.right >= this.left) && (bounds.right <= this.right)) ||
458 ((this.right >= bounds.left) && (this.right <= bounds.right))
460 intersects = ((inBottom || inTop) && (inLeft || inRight));
466 * APIMethod: containsBounds
467 * Determine whether the target bounds is contained within this bounds.
469 * bounds - {<OpenLayers.Bounds>} The target bounds.
470 * partial - {Boolean} If any of the target corners is within this bounds
471 * consider the bounds contained. Default is false. If true, the
472 * entire target bounds must be contained within this bounds.
473 * inclusive - {Boolean} Treat shared edges as contained. Default is
477 * {Boolean} The passed-in bounds object is contained within this bounds.
479 containsBounds:function(bounds, partial, inclusive) {
480 if (partial == null) {
483 if (inclusive == null) {
486 var bottomLeft = this.contains(bounds.left, bounds.bottom, inclusive);
487 var bottomRight = this.contains(bounds.right, bounds.bottom, inclusive);
488 var topLeft = this.contains(bounds.left, bounds.top, inclusive);
489 var topRight = this.contains(bounds.right, bounds.top, inclusive);
491 return (partial) ? (bottomLeft || bottomRight || topLeft || topRight)
492 : (bottomLeft && bottomRight && topLeft && topRight);
496 * APIMethod: determineQuadrant
499 * lonlat - {<OpenLayers.LonLat>}
502 * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which the
505 determineQuadrant: function(lonlat) {
508 var center = this.getCenterLonLat();
510 quadrant += (lonlat.lat < center.lat) ? "b" : "t";
511 quadrant += (lonlat.lon < center.lon) ? "l" : "r";
517 * APIMethod: transform
518 * Transform the Bounds object from source to dest.
521 * source - {<OpenLayers.Projection>} Source projection.
522 * dest - {<OpenLayers.Projection>} Destination projection.
525 * {<OpenLayers.Bounds>} Itself, for use in chaining operations.
527 transform: function(source, dest) {
528 // clear cached center location
529 this.centerLonLat = null;
530 var ll = OpenLayers.Projection.transform(
531 {'x': this.left, 'y': this.bottom}, source, dest);
532 var lr = OpenLayers.Projection.transform(
533 {'x': this.right, 'y': this.bottom}, source, dest);
534 var ul = OpenLayers.Projection.transform(
535 {'x': this.left, 'y': this.top}, source, dest);
536 var ur = OpenLayers.Projection.transform(
537 {'x': this.right, 'y': this.top}, source, dest);
538 this.left = Math.min(ll.x, ul.x);
539 this.bottom = Math.min(ll.y, lr.y);
540 this.right = Math.max(lr.x, ur.x);
541 this.top = Math.max(ul.y, ur.y);
546 * APIMethod: wrapDateLine
549 * maxExtent - {<OpenLayers.Bounds>}
550 * options - {Object} Some possible options are:
551 * leftTolerance - {float} Allow for a margin of error
552 * with the 'left' value of this
555 * rightTolerance - {float} Allow for a margin of error
556 * with the 'right' value of
561 * {<OpenLayers.Bounds>} A copy of this bounds, but wrapped around the
562 * "dateline" (as specified by the borders of
563 * maxExtent). Note that this function only returns
564 * a different bounds value if this bounds is
565 * *entirely* outside of the maxExtent. If this
566 * bounds straddles the dateline (is part in/part
567 * out of maxExtent), the returned bounds will be
568 * merely a copy of this one.
570 wrapDateLine: function(maxExtent, options) {
571 options = options || {};
573 var leftTolerance = options.leftTolerance || 0;
574 var rightTolerance = options.rightTolerance || 0;
576 var newBounds = this.clone();
581 while ( newBounds.left < maxExtent.left &&
582 (newBounds.right - rightTolerance) <= maxExtent.left ) {
583 newBounds = newBounds.add(maxExtent.getWidth(), 0);
587 while ( (newBounds.left + leftTolerance) >= maxExtent.right &&
588 newBounds.right > maxExtent.right ) {
589 newBounds = newBounds.add(-maxExtent.getWidth(), 0);
596 CLASS_NAME: "OpenLayers.Bounds"
600 * APIFunction: fromString
601 * Alternative constructor that builds a new OpenLayers.Bounds from a
605 * str - {String}Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
608 * {<OpenLayers.Bounds>} New bounds object built from the
611 OpenLayers.Bounds.fromString = function(str) {
612 var bounds = str.split(",");
613 return OpenLayers.Bounds.fromArray(bounds);
617 * APIFunction: fromArray
618 * Alternative constructor that builds a new OpenLayers.Bounds
622 * bbox - {Array(Float)} Array of bounds values (ex. <i>[5,42,10,45]</i>)
625 * {<OpenLayers.Bounds>} New bounds object built from the passed-in Array.
627 OpenLayers.Bounds.fromArray = function(bbox) {
628 return new OpenLayers.Bounds(parseFloat(bbox[0]),
631 parseFloat(bbox[3]));
635 * APIFunction: fromSize
636 * Alternative constructor that builds a new OpenLayers.Bounds
640 * size - {<OpenLayers.Size>}
643 * {<OpenLayers.Bounds>} New bounds object built from the passed-in size.
645 OpenLayers.Bounds.fromSize = function(size) {
646 return new OpenLayers.Bounds(0,
653 * Function: oppositeQuadrant
654 * Get the opposite quadrant for a given quadrant string.
657 * quadrant - {String} two character quadrant shortstring
660 * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if
661 * you pass in "bl" it returns "tr", if you pass in "br" it
664 OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
667 opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
668 opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';