From c53374c797b3eabce6b288c39743ccdde3e1e686 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 29 Sep 2011 10:03:02 -0600 Subject: [PATCH 01/34] always start the map extent on the right of the dateline --- lib/OpenLayers/Map.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js index cad15bf620..78eb0c3f74 100644 --- a/lib/OpenLayers/Map.js +++ b/lib/OpenLayers/Map.js @@ -1940,7 +1940,18 @@ OpenLayers.Map = OpenLayers.Class({ var valid = false; if (lonlat != null) { var maxExtent = this.getMaxExtent(); - valid = maxExtent.containsLonLat(lonlat); + valid = maxExtent.containsLonLat(lonlat); + if (!valid && this.baseLayer.wrapDateLine) { + lonlat = lonlat.clone(); + var worldWidth = maxExtent.getWidth(); + while(lonlat.lon > maxExtent.right) { + lonlat.lon -= worldWidth; + valid = maxExtent.containsLonLat(lonlat); + if (valid) { + break; + } + } + } } return valid; }, @@ -2254,12 +2265,9 @@ OpenLayers.Map = OpenLayers.Class({ } //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. + // for the center. // - center = bounds.getCenterLonLat().wrapDateLine(maxExtent); + center = bounds.getCenterLonLat(); } this.setCenter(center, this.getZoomForExtent(bounds, closest)); }, From 516914c935981e5bdfdb098e00b7aad2a7e91a61 Mon Sep 17 00:00:00 2001 From: tschaub Date: Thu, 29 Sep 2011 12:21:05 -0600 Subject: [PATCH 02/34] Push dateline wrapping to intersectsBounds method. It should simplify things by pushing the dateline wrapping stuff into the intersectsBounds method. --- lib/OpenLayers/BaseTypes/Bounds.js | 78 +++++++++++++++++++------- lib/OpenLayers/Tile.js | 27 ++------- tests/manual/dateline-smallextent.html | 18 ++++-- 3 files changed, 76 insertions(+), 47 deletions(-) diff --git a/lib/OpenLayers/BaseTypes/Bounds.js b/lib/OpenLayers/BaseTypes/Bounds.js index e33c49667f..06dc5e1d2f 100644 --- a/lib/OpenLayers/BaseTypes/Bounds.js +++ b/lib/OpenLayers/BaseTypes/Bounds.js @@ -428,48 +428,79 @@ OpenLayers.Bounds = OpenLayers.Class({ * * Parameters: * bounds - {} The target bounds. + * options - {Object} Optional parameters. + * + * Acceptable options: * 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. + * worldBounds - {} If a worldBounds is provided, two + * bounds will be considered as intersecting if they intersect when + * shifted to within the world bounds. This applies only to bounds that + * cross or are completely outside the world bounds. * * Returns: * {Boolean} The passed-in bounds object intersects this bounds. */ - intersectsBounds:function(bounds, inclusive) { - if (inclusive == null) { - inclusive = true; + intersectsBounds:function(bounds, options) { + options = options || {}; + if (typeof options === "boolean") { + options = {inclusive: true}; + } + if (options.worldBounds) { + var 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 = ( - this.left == bounds.right || - this.right == bounds.left || - this.top == bounds.bottom || - this.bottom == bounds.top + self.left == bounds.right || + self.right == bounds.left || + self.top == bounds.bottom || + self.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) { + 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.bottom >= this.bottom) && (bounds.bottom <= this.top)) || - ((this.bottom >= bounds.bottom) && (this.bottom <= bounds.top)) + ((bounds.bottom >= self.bottom) && (bounds.bottom <= self.top)) || + ((self.bottom >= bounds.bottom) && (self.bottom <= bounds.top)) ); var inTop = ( - ((bounds.top >= this.bottom) && (bounds.top <= this.top)) || - ((this.top > bounds.bottom) && (this.top < bounds.top)) + ((bounds.top >= self.bottom) && (bounds.top <= self.top)) || + ((self.top > bounds.bottom) && (self.top < bounds.top)) ); var inLeft = ( - ((bounds.left >= this.left) && (bounds.left <= this.right)) || - ((this.left >= bounds.left) && (this.left <= bounds.right)) + ((bounds.left >= self.left) && (bounds.left <= self.right)) || + ((self.left >= bounds.left) && (self.left <= bounds.right)) ); var inRight = ( - ((bounds.right >= this.left) && (bounds.right <= this.right)) || - ((this.right >= bounds.left) && (this.right <= bounds.right)) + ((bounds.right >= self.left) && (bounds.right <= self.right)) || + ((self.right >= bounds.left) && (self.right <= bounds.right)) ); intersects = ((inBottom || inTop) && (inLeft || inRight)); } + // document me + 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; }, @@ -577,8 +608,9 @@ OpenLayers.Bounds = OpenLayers.Class({ * 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. + * out of maxExtent), the returned bounds will always + * cross the left edge of the given maxExtent. + *. */ wrapDateLine: function(maxExtent, options) { options = options || {}; @@ -589,18 +621,24 @@ OpenLayers.Bounds = OpenLayers.Class({ var newBounds = this.clone(); if (maxExtent) { + var width = maxExtent.getWidth(); //shift right? while ( newBounds.left < maxExtent.left && (newBounds.right - rightTolerance) <= maxExtent.left ) { - newBounds = newBounds.add(maxExtent.getWidth(), 0); + newBounds = newBounds.add(width, 0); } //shift left? while ( (newBounds.left + leftTolerance) >= maxExtent.right && newBounds.right > maxExtent.right ) { - newBounds = newBounds.add(-maxExtent.getWidth(), 0); + newBounds = newBounds.add(-width, 0); } + + // crosses right? force left + if (newBounds.left < maxExtent.right && newBounds.right > maxExtent.right) { + newBounds = newBounds.add(-width, 0); + } } return newBounds; diff --git a/lib/OpenLayers/Tile.js b/lib/OpenLayers/Tile.js index bc7c1baf6f..aa3b89b1ee 100644 --- a/lib/OpenLayers/Tile.js +++ b/lib/OpenLayers/Tile.js @@ -175,29 +175,10 @@ OpenLayers.Tile = OpenLayers.Class({ var withinMaxExtent = false, maxExtent = this.layer.maxExtent; if (maxExtent) { - // prepare up to 3 versions of the layer's maxExtent, to make sure - // that the intersectsBounds check below catches all cases of - // extents that cross the dateline: - // (1) left bound positive, right bound negative (wrapped) - // (2) left bound positive, right bound positive (exceeding world) - // (3) left bound negative (exceeding world), right bound positive - var maxExtents = [maxExtent]; - if (this.layer.map.baseLayer.wrapDateLine) { - if (maxExtent.left > maxExtent.right) { - var worldWidth = this.layer.map.getMaxExtent().getWidth(); - maxExtent = this.layer.maxExtent.clone(); - maxExtent.left -= worldWidth; - maxExtents.push(maxExtent); - maxExtent = this.layer.maxExtent.clone(); - maxExtent.right += worldWidth; - maxExtents.push(maxExtent); - } - } - for (var i=maxExtents.length-1; i>=0; --i) { - if (this.bounds.intersectsBounds(maxExtents[i], false)) { - withinMaxExtent = true; - break; - } + var map = this.layer.map; + var worldBounds = map.baseLayer.wrapDateLine && map.getMaxExtent(); + if (this.bounds.intersectsBounds(maxExtent, {inclusive: false, worldBounds: worldBounds})) { + withinMaxExtent = true; } } diff --git a/tests/manual/dateline-smallextent.html b/tests/manual/dateline-smallextent.html index ed80a8a9e7..3dc9025f18 100644 --- a/tests/manual/dateline-smallextent.html +++ b/tests/manual/dateline-smallextent.html @@ -22,19 +22,29 @@ var map; function init(){ map = new OpenLayers.Map('map'); - var osm = new OpenLayers.Layer.OSM( - "OSM", null, + var base = new OpenLayers.Layer.WMS("marble", + "http://demo.opengeo.org/geoserver/wms", + {layers: "topp:naturalearth"}, {wrapDateLine: true} ); - var extent = new OpenLayers.Bounds(15849982.183008, -11368938.517442, -14206280.326992, -1350184.3474419); + var extent = new OpenLayers.Bounds(142.3828125,-70.902270266175,233.6171875,-12.039326531729); var wms = new OpenLayers.Layer.WMS( "world", "http://demo.opengeo.org/geoserver/wms", {layers: 'world', transparent: true}, {maxExtent: extent} ); + + var poly = extent.toGeometry(); + var vector = new OpenLayers.Layer.Vector(); + poly.components[0].components.forEach(function(point) { + vector.addFeatures([ + new OpenLayers.Feature.Vector(point) + ]); + }) - map.addLayers([osm, wms]); + map.addLayers([base, wms, vector]); + map.addControl(new OpenLayers.Control.LayerSwitcher()); map.zoomToExtent(extent); } From c2582c1970b8ebbdf5cc64e2b27b30e877fa1a5f Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 29 Sep 2011 13:35:00 -0600 Subject: [PATCH 03/34] use tolerance for crosses right / force left --- lib/OpenLayers/BaseTypes/Bounds.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/OpenLayers/BaseTypes/Bounds.js b/lib/OpenLayers/BaseTypes/Bounds.js index 06dc5e1d2f..e43e813227 100644 --- a/lib/OpenLayers/BaseTypes/Bounds.js +++ b/lib/OpenLayers/BaseTypes/Bounds.js @@ -623,20 +623,21 @@ OpenLayers.Bounds = OpenLayers.Class({ if (maxExtent) { var width = maxExtent.getWidth(); - //shift right? - while ( newBounds.left < maxExtent.left && - (newBounds.right - rightTolerance) <= maxExtent.left ) { + //shift right? + while (newBounds.left < maxExtent.left && + newBounds.right - rightTolerance <= maxExtent.left ) { newBounds = newBounds.add(width, 0); - } + } - //shift left? - while ( (newBounds.left + leftTolerance) >= maxExtent.right && + //shift left? + while (newBounds.left + leftTolerance >= maxExtent.right && newBounds.right > maxExtent.right ) { newBounds = newBounds.add(-width, 0); - } + } - // crosses right? force left - if (newBounds.left < maxExtent.right && newBounds.right > maxExtent.right) { + // crosses right? force left + if (newBounds.left + leftTolerance < maxExtent.right && + newBounds.right - rightTolerance > maxExtent.right) { newBounds = newBounds.add(-width, 0); } } From dc6989b9f04c335c7f79224846c5d913dc96c247 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 29 Sep 2011 13:35:22 -0600 Subject: [PATCH 04/34] adding missing comma --- lib/OpenLayers/BaseTypes/Bounds.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/OpenLayers/BaseTypes/Bounds.js b/lib/OpenLayers/BaseTypes/Bounds.js index e43e813227..6c85c8ab0b 100644 --- a/lib/OpenLayers/BaseTypes/Bounds.js +++ b/lib/OpenLayers/BaseTypes/Bounds.js @@ -448,7 +448,7 @@ OpenLayers.Bounds = OpenLayers.Class({ options = {inclusive: true}; } if (options.worldBounds) { - var self = this.wrapDateLine(options.worldBounds) + var self = this.wrapDateLine(options.worldBounds); bounds = bounds.wrapDateLine(options.worldBounds); } else { self = this; From 0277eb1ed3fd5ad6f952a7e74c52a3951eee97b8 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 29 Sep 2011 13:38:40 -0600 Subject: [PATCH 05/34] worldBounds option for containsLonLat. This is basically the same enhancement we made for intersectsBounds, and we no longer have to do dateline shifting in other components to get proper containsLonLat results. --- lib/OpenLayers/BaseTypes/Bounds.js | 33 ++++++++++++++++++++++++++++-- lib/OpenLayers/Map.js | 14 ++----------- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/lib/OpenLayers/BaseTypes/Bounds.js b/lib/OpenLayers/BaseTypes/Bounds.js index 6c85c8ab0b..83e0c79ded 100644 --- a/lib/OpenLayers/BaseTypes/Bounds.js +++ b/lib/OpenLayers/BaseTypes/Bounds.js @@ -358,14 +358,43 @@ OpenLayers.Bounds = OpenLayers.Class({ * * Parameters: * ll - {} + * options - {Object} Optional parameters + * + * Acceptable options: * inclusive - {Boolean} Whether or not to include the border. * Default is true. + * worldBounds - {} If a worldBounds is provided, the + * ll will be considered as contained if it exceeds the world bounds, + * but can be wrapped around the dateline so it is contained by this + * bounds. * * Returns: * {Boolean} The passed-in lonlat is within this bounds. */ - containsLonLat:function(ll, inclusive) { - return this.contains(ll.lon, ll.lat, inclusive); + containsLonLat: function(ll, options) { + options = options || {}; + if (typeof options === "boolean") { + options.inclusive = options; + } + var contains = this.contains(ll.lon, ll.lat, options.inclusive), + worldBounds = options.worldBounds; + if (worldBounds && !contains) { + var worldWidth = worldBounds.getWidth(); + ll = ll.clone(); + while(!contains && ll.lon > worldBounds.right) { + ll.lon -= worldWidth; + contains = worldBounds.containsLonLat( + ll, {inclusive: options.inclusive} + ); + } + while(!contains && ll.lon < worldBounds.left) { + ll.lon += worldWidth; + contains = worldBounds.containsLonLat( + ll, {inclusive: options.inclusive} + ); + } + } + return contains; }, /** diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js index 78eb0c3f74..44771f09ce 100644 --- a/lib/OpenLayers/Map.js +++ b/lib/OpenLayers/Map.js @@ -1940,18 +1940,8 @@ OpenLayers.Map = OpenLayers.Class({ var valid = false; if (lonlat != null) { var maxExtent = this.getMaxExtent(); - valid = maxExtent.containsLonLat(lonlat); - if (!valid && this.baseLayer.wrapDateLine) { - lonlat = lonlat.clone(); - var worldWidth = maxExtent.getWidth(); - while(lonlat.lon > maxExtent.right) { - lonlat.lon -= worldWidth; - valid = maxExtent.containsLonLat(lonlat); - if (valid) { - break; - } - } - } + var worldBounds = this.baseLayer.wrapDateLine && maxExtent; + valid = maxExtent.containsLonLat(lonlat, {worldBounds: worldBounds}); } return valid; }, From 23a17fc355b451031e5df7eb3d32ba26502c588a Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 29 Sep 2011 13:39:16 -0600 Subject: [PATCH 06/34] use tolerance for correct tile bounds shifting --- lib/OpenLayers/Tile.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/OpenLayers/Tile.js b/lib/OpenLayers/Tile.js index aa3b89b1ee..fb8b08d931 100644 --- a/lib/OpenLayers/Tile.js +++ b/lib/OpenLayers/Tile.js @@ -195,8 +195,12 @@ OpenLayers.Tile = OpenLayers.Class({ setBounds: function(bounds) { bounds = bounds.clone(); if (this.layer.map.baseLayer.wrapDateLine) { - var worldExtent = this.layer.map.getMaxExtent(); - bounds = bounds.wrapDateLine(worldExtent); + var worldExtent = this.layer.map.getMaxExtent(), + tolerance = this.layer.map.getResolution(); + bounds = bounds.wrapDateLine(worldExtent, { + leftTolerance: tolerance, + rightTolerance: tolerance + }); } this.bounds = bounds; }, From 2e6c9bfdc290724c1a6f09025c4b3ab15214bc05 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 29 Sep 2011 13:53:32 -0600 Subject: [PATCH 07/34] Remove action that is now covered by wrapDateLine. With the changes in wrapDateLine, we don't have to shift the bounds here any more. --- lib/OpenLayers/Layer/Grid.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js index 696d6e92cf..56d5cfb8e4 100644 --- a/lib/OpenLayers/Layer/Grid.js +++ b/lib/OpenLayers/Layer/Grid.js @@ -404,15 +404,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { */ calculateGridLayout: function(bounds, origin, resolution) { bounds = bounds.clone(); - if (this.map.baseLayer.wrapDateLine) { - var maxExtent = this.map.getMaxExtent(), - width = maxExtent.getWidth(); - // move the bounds one world width to the right until the origin is - // within the world extent - while (bounds.left < maxExtent.left) { - bounds.left += width; - bounds.right += width; - } + var map = this.map; + if (map.wrapDateLine) { + bounds = bounds.wrapDateLine(map.getMaxExtent()); } var tilelon = resolution * this.tileSize.w; From 085c1156c21f8ae0d93adc4d42aa37075c60bf1b Mon Sep 17 00:00:00 2001 From: ahocevar Date: Thu, 29 Sep 2011 13:54:05 -0600 Subject: [PATCH 08/34] simplified the example a bit; improved description --- tests/manual/dateline-smallextent.html | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/manual/dateline-smallextent.html b/tests/manual/dateline-smallextent.html index 3dc9025f18..1d05e849cf 100644 --- a/tests/manual/dateline-smallextent.html +++ b/tests/manual/dateline-smallextent.html @@ -4,7 +4,7 @@ - OpenLayers: Sketch handlers crossing the dateline + OpenLayers: Overlay layer extents crossing the dateline