diff --git a/examples/wrapDateLine.html b/examples/wrapDateLine.html new file mode 100644 index 0000000000..7a4cf8b12f --- /dev/null +++ b/examples/wrapDateLine.html @@ -0,0 +1,61 @@ + + + + + + + +
+
+ This is an example that shows wrapping the date line. Wrapping the + date line is an option on the layer. +
+ + diff --git a/lib/OpenLayers/BaseTypes.js b/lib/OpenLayers/BaseTypes.js index 1b426f5c1b..70eb1e53c9 100644 --- a/lib/OpenLayers/BaseTypes.js +++ b/lib/OpenLayers/BaseTypes.js @@ -299,6 +299,32 @@ OpenLayers.LonLat.prototype = { return equals; }, + /** + * @param {OpenLayers.Bounds} maxExtent + * + * @returns A copy of this lonlat, but wrapped around the "dateline" (as + * specified by the borders of maxExtent) + * @type OpenLayers.LonLat + */ + wrapDateLine: function(maxExtent) { + + var newLonLat = this.clone(); + + if (maxExtent) { + //shift right? + while (newLonLat.lon < maxExtent.left) { + newLonLat.lon += maxExtent.getWidth(); + } + + //shift left? + while (newLonLat.lon > maxExtent.right) { + newLonLat.lon -= maxExtent.getWidth(); + } + } + + return newLonLat; + }, + /** @final @type String */ CLASS_NAME: "OpenLayers.LonLat" }; @@ -666,6 +692,50 @@ OpenLayers.Bounds.prototype = { return quadrant; }, + /** + * @param {OpenLayers.Bounds} maxExtent + * @param {Object} options + * @option {float} leftTolerance Allow for a margin of error with the + * 'left' value of this bound. + * Default is 0 + * @option {float} rightTolerance Allow for a margin of error with the + * 'right' value of this bound. + * Default is 0 + * + * @returns 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. + * @type OpenLayers.Bounds + */ + wrapDateLine: function(maxExtent, options) { + options = options || new Object(); + + 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; + }, + /** @final @type String */ CLASS_NAME: "OpenLayers.Bounds" }; diff --git a/lib/OpenLayers/Layer.js b/lib/OpenLayers/Layer.js index 3900fd9f71..76cf8f420a 100644 --- a/lib/OpenLayers/Layer.js +++ b/lib/OpenLayers/Layer.js @@ -139,6 +139,12 @@ OpenLayers.Layer.prototype = { /** @type Boolean */ displayOutsideMaxExtent: false, + + /** wrapDateLine -- #487 for more info. + * + * @type @Boolean + */ + wrapDateLine: false, /** @@ -165,6 +171,10 @@ OpenLayers.Layer.prototype = { this.events = new OpenLayers.Events(this, this.div, this.EVENT_TYPES); } + + if (this.wrapDateLine) { + this.displayOutsideMaxExtent = true; + } }, /** @@ -616,6 +626,10 @@ OpenLayers.Layer.prototype = { lonlat = new OpenLayers.LonLat(center.lon + delta_x * res , center.lat - delta_y * res); + + if (this.wrapDateLine) { + lonlat = lonlat.wrapDateLine(this.maxExtent); + } } // else { DEBUG STATEMENT } } return lonlat; @@ -641,23 +655,6 @@ OpenLayers.Layer.prototype = { return px; }, - /** - * Adjusts the extent of a bounds in map units by the layer's gutter - * in pixels. - * - * @param {OpenLayers.Bounds} bounds - * @type OpenLayers.Bounds - * @return A bounds adjusted in height and width by the gutter - */ - adjustBoundsByGutter: function(bounds) { - var mapGutter = this.gutter * this.map.getResolution(); - bounds = new OpenLayers.Bounds(bounds.left - mapGutter, - bounds.bottom - mapGutter, - bounds.right + mapGutter, - bounds.top + mapGutter); - return bounds; - }, - /** * Sets the opacity for the entire layer (all images) * @param {Float} opacity @@ -681,6 +678,38 @@ OpenLayers.Layer.prototype = { this.div.style.zIndex = zIdx; }, + /** + * This function will take a bounds, and if wrapDateLine option is set + * on the layer, it will return a bounds which is wrapped around the world. + * We do not wrap for bounds which *cross* the maxExtent.left/right, only + * bounds which are entirely to the left or entirely to the right. + * + * @param {OpenLayers.Bounds} bounds + * @private + */ + adjustBounds: function (bounds) { + + if (this.gutter) { + // Adjust the extent of a bounds in map units by the + // layer's gutter in pixels. + var mapGutter = this.gutter * this.map.getResolution(); + bounds = new OpenLayers.Bounds(bounds.left - mapGutter, + bounds.bottom - mapGutter, + bounds.right + mapGutter, + bounds.top + mapGutter); + } + + if (this.wrapDateLine) { + // wrap around the date line, within the limits of rounding error + var wrappingOptions = { + 'rightTolerance':this.getResolution() + }; + bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions); + + } + return bounds; + }, + /** @final @type String */ CLASS_NAME: "OpenLayers.Layer" }; diff --git a/lib/OpenLayers/Layer/KaMap.js b/lib/OpenLayers/Layer/KaMap.js index a1e736e124..3d6530c51e 100644 --- a/lib/OpenLayers/Layer/KaMap.js +++ b/lib/OpenLayers/Layer/KaMap.js @@ -49,6 +49,7 @@ OpenLayers.Layer.KaMap.prototype = * @type String */ getURL: function (bounds) { + bounds = this.adjustBounds(bounds); var mapRes = this.map.getResolution(); var scale = Math.round((this.map.getScale() * 10000)) / 10000; var pX = Math.round(bounds.left / mapRes); diff --git a/lib/OpenLayers/Layer/MapServer.js b/lib/OpenLayers/Layer/MapServer.js index c45268c7bd..cca83298a9 100644 --- a/lib/OpenLayers/Layer/MapServer.js +++ b/lib/OpenLayers/Layer/MapServer.js @@ -87,9 +87,7 @@ OpenLayers.Layer.MapServer.prototype = * @type String */ getURL: function (bounds) { - if(this.gutter) { - bounds = this.adjustBoundsByGutter(bounds); - } + bounds = this.adjustBounds(bounds); // Make a list, so that getFullRequestString uses literal "," var extent = [bounds.left, bounds. bottom, bounds.right, bounds.top]; diff --git a/lib/OpenLayers/Layer/TMS.js b/lib/OpenLayers/Layer/TMS.js index 9be25faee3..cce60e73ea 100644 --- a/lib/OpenLayers/Layer/TMS.js +++ b/lib/OpenLayers/Layer/TMS.js @@ -71,6 +71,7 @@ OpenLayers.Layer.TMS.prototype = * @type String */ getURL: function (bounds) { + bounds = this.adjustBounds(bounds); var res = this.map.getResolution(); var x = (bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w); var y = (bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h); diff --git a/lib/OpenLayers/Layer/WMS.js b/lib/OpenLayers/Layer/WMS.js index 0e87d27d52..8e0407b57c 100644 --- a/lib/OpenLayers/Layer/WMS.js +++ b/lib/OpenLayers/Layer/WMS.js @@ -94,9 +94,7 @@ OpenLayers.Layer.WMS.prototype = * @type String */ getURL: function (bounds) { - if(this.gutter) { - bounds = this.adjustBoundsByGutter(bounds); - } + bounds = this.adjustBounds(bounds); return this.getFullRequestString( {BBOX:bounds.toBBOX(), WIDTH:this.imageSize.w, diff --git a/lib/OpenLayers/Layer/WorldWind.js b/lib/OpenLayers/Layer/WorldWind.js index 5dd34f8b39..dee1784cfd 100644 --- a/lib/OpenLayers/Layer/WorldWind.js +++ b/lib/OpenLayers/Layer/WorldWind.js @@ -69,6 +69,7 @@ OpenLayers.Layer.WorldWind.prototype = * @type String */ getURL: function (bounds) { + bounds = this.adjustBounds(bounds); var zoom = this.getZoom(); var extent = this.map.getMaxExtent(); var deg = this.lzd/Math.pow(2,this.getZoom()); diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js index ecf9d71936..5879d805e3 100644 --- a/lib/OpenLayers/Map.js +++ b/lib/OpenLayers/Map.js @@ -1168,8 +1168,30 @@ OpenLayers.Map.prototype = { * @param {OpenLayers.Bounds} bounds */ zoomToExtent: function(bounds) { - this.setCenter(bounds.getCenterLonLat(), - this.getZoomForExtent(bounds)); + var center = bounds.getCenterLonLat(); + if (this.baseLayer.wrapDateLine) { + var maxExtent = this.getMaxExtent(); + + //fix straddling bounds (in the case of a bbox that straddles the + // dateline, it's left and right boundaries will appear backwards. + // we fix this by allowing a right value that is greater than the + // max value at the dateline -- this allows us to pass a valid + // bounds to calculate zoom) + // + bounds = bounds.clone(); + while (bounds.right < bounds.left) { + bounds.right += maxExtent.getWidth(); + } + //if the bounds was straddling (see above), then the center point + // we got from it was wrong. So we take our new bounds and ask it + // for the center. Because our new bounds is at least partially + // outside the bounds of maxExtent, the new calculated center + // might also be. We don't want to pass a bad center value to + // setCenter, so we have it wrap itself across the date line. + // + center = bounds.getCenterLonLat().wrapDateLine(maxExtent); + } + this.setCenter(center, this.getZoomForExtent(bounds)); }, /** Zoom to the full extent and recenter. diff --git a/tests/BaseTypes/test_Bounds.html b/tests/BaseTypes/test_Bounds.html index bbdca1ed45..197828f3aa 100644 --- a/tests/BaseTypes/test_Bounds.html +++ b/tests/BaseTypes/test_Bounds.html @@ -379,6 +379,98 @@ (bounds.top == object.y)), "obj Point to extends correclty modifies right and top"); } + + + function test_16_Bounds_wrapDateLine(t) { + t.plan( 13 ); + + var testBounds, wrappedBounds, desiredBounds; + + var maxExtent = new OpenLayers.Bounds(-10,-10,10,10); + var exactBounds = maxExtent.clone(); + var simpleBounds = new OpenLayers.Bounds( -5,-5,5,5); + + + + //bad maxextent + testBounds = simpleBounds.clone(); + wrappedBounds = testBounds.wrapDateLine(null); + t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds with a bad maxextent does nothing"); + + + + //exactly inside + testBounds = exactBounds.clone(); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(exactBounds), "wrapping a bounds precisely within (equal to) maxextent does nothing"); + + + //inside + testBounds = simpleBounds.clone(); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds within maxextent does nothing"); + +// LEFT // + + //straddling left + testBounds = simpleBounds.add(-10,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(testBounds), "wrapping a bounds that straddles the left of maxextent does nothing"); + + //left rightTolerance + testBounds = simpleBounds.add(-14,0); + wrappedBounds = + testBounds.wrapDateLine(maxExtent, {'rightTolerance': 1} ); + desiredBounds = simpleBounds.add(6,0); + t.ok(wrappedBounds.equals(desiredBounds), "wrapping a bounds rightTolerance left of maxextent works"); + + //exactly left + testBounds = exactBounds.add(-20,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(exactBounds), "wrapping an exact bounds once left of maxextent works"); + + //left + testBounds = simpleBounds.add(-20,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds once left of maxextent works"); + + //way left + testBounds = simpleBounds.add(-200,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds way left of maxextent works"); + +// RIGHT // + + //straddling right + testBounds = simpleBounds.add(10,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(testBounds), "wrapping a bounds that straddles the right of maxextent does nothing"); + + //right leftTolerance + testBounds = simpleBounds.add(14,0); + wrappedBounds = + testBounds.wrapDateLine(maxExtent, {'leftTolerance': 1} ); + desiredBounds = simpleBounds.add(-6,0); + t.ok(wrappedBounds.equals(desiredBounds), "wrapping a bounds leftTolerance right of maxextent works"); + + //exactly right + testBounds = exactBounds.add(20,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(exactBounds), "wrapping an exact bounds once right of maxextent works"); + + //right + testBounds = simpleBounds.add(20,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds once right of maxextent works"); + + //way right + testBounds = simpleBounds.add(200,0); + wrappedBounds = testBounds.wrapDateLine(maxExtent); + t.ok(wrappedBounds.equals(simpleBounds), "wrapping a bounds way right of maxextent works"); + + + + } // --> diff --git a/tests/BaseTypes/test_LonLat.html b/tests/BaseTypes/test_LonLat.html index d1fa92e5f5..3248ef17c3 100644 --- a/tests/BaseTypes/test_LonLat.html +++ b/tests/BaseTypes/test_LonLat.html @@ -88,6 +88,50 @@ t.ok( lonlat.equals(ll), "lonlat is set correctly"); } + function test_08_LonLat_wrapDateLine(t) { + t.plan( 6 ); + + var goodLL = new OpenLayers.LonLat(0,0); + var testLL, wrappedLL; + + //bad maxextent + var maxExtent = null; + + testLL = goodLL.clone(); + wrappedLL = testLL.wrapDateLine(maxExtent); + t.ok(wrappedLL.equals(goodLL), "wrapping a ll with a bad maxextent does nothing"); + + + //good maxextent + maxExtent = new OpenLayers.Bounds(-10,-10,10,10); + + //inside + testLL = goodLL.clone(); + wrappedLL = testLL.wrapDateLine(maxExtent); + t.ok(wrappedLL.equals(goodLL), "wrapping a ll within the maxextent does nothing"); + + //left + testLL = goodLL.add(-20,0); + wrappedLL = testLL.wrapDateLine(maxExtent); + t.ok(wrappedLL.equals(goodLL), "wrapping a ll once left of maxextent works"); + + //way left + testLL = goodLL.add(-200,0); + wrappedLL = testLL.wrapDateLine(maxExtent); + t.ok(wrappedLL.equals(goodLL), "wrapping a ll way left of maxextent works"); + + //right + testLL = goodLL.add(20,0); + wrappedLL = testLL.wrapDateLine(maxExtent); + t.ok(wrappedLL.equals(goodLL), "wrapping a ll once right of maxextent works"); + + //way right + testLL = goodLL.add(200,0); + wrappedLL = testLL.wrapDateLine(maxExtent); + t.ok(wrappedLL.equals(goodLL), "wrapping a ll way right of maxextent works"); + + } + // --> diff --git a/tests/Layer/test_WrapDateLine.html b/tests/Layer/test_WrapDateLine.html new file mode 100644 index 0000000000..aac91045f8 --- /dev/null +++ b/tests/Layer/test_WrapDateLine.html @@ -0,0 +1,176 @@ + + + + + + +
+ + diff --git a/tests/list-tests.html b/tests/list-tests.html index 14a794634e..5ff89a9dfc 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -49,6 +49,7 @@
  • Layer/test_TMS.html
  • Layer/test_Vector.html
  • Layer/test_GML.html
  • +
  • Layer/test_WrapDateLine.html
  • test_Tile.html
  • Tile/test_Image.html
  • test_Control.html