diff --git a/examples/restricted-extent.html b/examples/restricted-extent.html
new file mode 100644
index 0000000000..2f8dc0d6ef
--- /dev/null
+++ b/examples/restricted-extent.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
OpenLayers Restricted Extent Example
+
+
+ Map navigation is limited by a combination of map and layer properties.
+ The base layer resolutions array controls the resolutions (or zoom
+ levels) available. The resolutions can be limited by setting a
+ maxResolution property or by explicitly specifying a resolutions
+ array.
+
+
+ Navigation limited by the maxExtent property. A map cannot be panned
+ so that the center of the viewport is outside of the bounds specified
+ in maxExtent. If you wish to further restrict panning, use the
+ restrictedExtent property. With restrictedExtent set, the map cannot
+ be panned beyond the given bounds. If the maxResolution allows the
+ map to be zoomed to a resolution that displays an area bigger than
+ the restrictedExtent, the viewport will remain centered on the
+ restrictedExtent.
+
+
+
+
+
+
+
diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js
index 996b6c630e..29b2965111 100644
--- a/lib/OpenLayers/Map.js
+++ b/lib/OpenLayers/Map.js
@@ -206,6 +206,17 @@ OpenLayers.Map = OpenLayers.Class({
*/
minExtent: null,
+ /**
+ * APIProperty: restrictedExtent
+ * {} Limit map navigation to this extent where possible.
+ * If a non-null restrictedExtent is set, panning will be restricted
+ * to the given bounds. In addition, zooming to a resolution that
+ * displays more than the restricted extent will center the map
+ * on the restricted extent. If you wish to limit the zoom level
+ * or resolution, use maxResolution.
+ */
+ restrictedExtent: null,
+
/**
* APIProperty: numZoomLevels
* {Integer} Number of zoom levels for the map. Defaults to 16. Set a
@@ -1036,10 +1047,47 @@ OpenLayers.Map = OpenLayers.Class({
* TBD: reconsider forceZoomChange in 3.0
*/
setCenter: function (lonlat, zoom, dragging, forceZoomChange) {
-
+
if (!this.center && !this.isValidLonLat(lonlat)) {
lonlat = this.maxExtent.getCenterLonLat();
}
+
+ if(this.restrictedExtent != null) {
+ // In 3.0, decide if we want to change interpretation of maxExtent.
+ if(lonlat == null) {
+ lonlat = this.getCenter();
+ }
+ if(zoom == null) {
+ zoom = this.getZoom();
+ }
+ var resolution = null;
+ if(this.baseLayer != null) {
+ resolution = this.baseLayer.resolutions[zoom];
+ }
+ var extent = this.calculateBounds(lonlat, resolution);
+ if(!this.restrictedExtent.containsBounds(extent)) {
+ var maxCenter = this.restrictedExtent.getCenterLonLat();
+ if(extent.getWidth() > this.restrictedExtent.getWidth()) {
+ lonlat = new OpenLayers.LonLat(maxCenter.lon, lonlat.lat);
+ } else if(extent.left < this.restrictedExtent.left) {
+ lonlat = lonlat.add(this.restrictedExtent.left -
+ extent.left, 0);
+ } else if(extent.right > this.restrictedExtent.right) {
+ lonlat = lonlat.add(this.restrictedExtent.right -
+ extent.right, 0);
+ }
+ if(extent.getHeight() > this.restrictedExtent.getHeight()) {
+ lonlat = new OpenLayers.LonLat(lonlat.lon, maxCenter.lat);
+ } else if(extent.bottom < this.restrictedExtent.bottom) {
+ lonlat = lonlat.add(0, this.restrictedExtent.bottom -
+ extent.bottom);
+ }
+ else if(extent.top > this.restrictedExtent.top) {
+ lonlat = lonlat.add(0, this.restrictedExtent.top -
+ extent.top);
+ }
+ }
+ }
var zoomChanged = forceZoomChange || (
(this.isValidZoomLevel(zoom)) &&
diff --git a/tests/test_Map.html b/tests/test_Map.html
index 9e1d6c05c6..13370edc87 100644
--- a/tests/test_Map.html
+++ b/tests/test_Map.html
@@ -472,6 +472,102 @@
t.ok( newNumControls == oldNumControls, "removing bad controlid doesnt crash or decrease control count")
}
+ function test_Map_restrictedExtent(t) {
+ t.plan(24);
+ var extent = new OpenLayers.Bounds(-180, -90, 180, 90);
+ var options = {
+ maxResolution: "auto"
+ };
+ var map = new OpenLayers.Map("map", options);
+ var layer = new OpenLayers.Layer.WMS(
+ "test",
+ "http://octo.metacarta.com/cgi-bin/mapserv?",
+ {map: "/mapdata/vmap_wms.map", layers: "basic"}
+ );
+ map.addLayer(layer);
+ map.zoomToMaxExtent();
+ var nw = new OpenLayers.LonLat(extent.left, extent.top);
+ var ne = new OpenLayers.LonLat(extent.right, extent.top);
+ var sw = new OpenLayers.LonLat(extent.left, extent.bottom);
+ var se = new OpenLayers.LonLat(extent.right, extent.bottom);
+
+ // try panning to northwest corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(nw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to northwest at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for nw, 0");
+ map.setCenter(nw, 5);
+ t.eq(map.getExtent().top, extent.top,
+ "map extent top properly restricted to northwest at zoom 5");
+ t.eq(map.getExtent().left, extent.left,
+ "map extent left properly restricted to northwest at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for nw, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(nw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ nw.toString(),
+ "map extent not restricted with null restrictedExtent for nw");
+
+ // try panning to northeast corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(ne, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to northeast at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for ne, 0");
+ map.setCenter(ne, 5);
+ t.eq(map.getExtent().top, extent.top,
+ "map extent top properly restricted to northeast at zoom 5");
+ t.eq(map.getExtent().right, extent.right,
+ "map extent right properly restricted to northeast at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for ne, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(ne, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ ne.toString(),
+ "map extent not restricted with null restrictedExtent for ne");
+
+ // try panning to southwest corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(sw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to southwest at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for sw, 0");
+ map.setCenter(sw, 5);
+ t.eq(map.getExtent().bottom, extent.bottom,
+ "map extent bottom properly restricted to southwest at zoom 5");
+ t.eq(map.getExtent().left, extent.left,
+ "map extent left properly restricted to southwest at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for sw, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(sw, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ sw.toString(),
+ "map extent not restricted with null restrictedExtent for sw");
+
+ // try panning to southeast corner
+ map.setOptions({restrictedExtent: extent});
+ map.setCenter(se, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ extent.getCenterLonLat().toString(),
+ "map extent properly restricted to southeast at zoom 0");
+ t.eq(map.zoom, 0, "zoom not restricted for se, 0");
+ map.setCenter(se, 5);
+ t.eq(map.getExtent().bottom, extent.bottom,
+ "map extent bottom properly restricted to southeast at zoom 5");
+ t.eq(map.getExtent().right, extent.right,
+ "map extent right properly restricted to southeast at zoom 5");
+ t.eq(map.zoom, 5, "zoom not restricted for se, 5");
+ map.setOptions({restrictedExtent: null});
+ map.setCenter(se, 0);
+ t.eq(map.getExtent().getCenterLonLat().toString(),
+ se.toString(),
+ "map extent not restricted with null restrictedExtent for se");
+ }
+
function test_99_Map_destroy (t) {
t.plan( 3 );
map = new OpenLayers.Map('map');
@@ -484,6 +580,6 @@
-
+