From ded64c00d42317ad0bac32a5e62f8597068c0bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sat, 1 Oct 2011 21:41:35 +0200 Subject: [PATCH 1/3] apply patch-3531-A0.diff from trac ticket #3531 --- examples/clientzoom.html | 78 +++++++++++ examples/clientzoom.js | 43 ++++++ lib/OpenLayers/Layer.js | 6 +- lib/OpenLayers/Layer/Grid.js | 153 ++++++++++++++++++--- lib/OpenLayers/Layer/TMS.js | 17 ++- lib/OpenLayers/Layer/TileCache.js | 13 +- lib/OpenLayers/Layer/WMTS.js | 37 ++++- lib/OpenLayers/Layer/XYZ.js | 15 +- lib/OpenLayers/Map.js | 2 + lib/OpenLayers/Tile/BackBufferable.js | 40 ++++-- lib/OpenLayers/Tile/Image.js | 18 +-- tests/Layer/ArcGIS93Rest.html | 4 +- tests/Layer/Grid.html | 190 ++++++++++++++++++++++++++ tests/Layer/MapServer.html | 4 +- tests/Layer/WMS.html | 8 +- tests/Layer/WMTS.html | 31 +++++ tests/Tile/BackBufferable.html | 118 +++++++++++++++- tests/Tile/Image.html | 7 +- 18 files changed, 718 insertions(+), 66 deletions(-) create mode 100644 examples/clientzoom.html create mode 100644 examples/clientzoom.js diff --git a/examples/clientzoom.html b/examples/clientzoom.html new file mode 100644 index 0000000000..ba92df08d5 --- /dev/null +++ b/examples/clientzoom.html @@ -0,0 +1,78 @@ + + + + + + + OpenLayers Client Zoom Example + + + + + + + + +

Client Zoom

+
+ client zoom continuous zooming +
+

+ + This example demonstrates the "client zoom" + functionality, where OpenLayers stretches the layer div if the + current resolution isn't supported by that layer's tile server. + +

+ +
+ +
+ +

+ + This map is configured with 22 resolutions, while the OSM tile + server supports only the first 19 resolutions. When the zoom + level is 19, 20 or 22 "client zoom" is applied on the OSM + layer, i.e. the OSM layer div is stretched as necessary. + +

+ +

+ + The map's initial zoom is 18. So if you zoom in using the zoom + bar's "+" button you'll see that "client zoom" is applied. + +

+ +

+ + The map is even configured with fractionalZoom set + to true. So "client zoom" also applies if you choose arbitrary + zoom levels using the slider of the zoom bar, or shift-drag + boxes to zoom to arbitrary extents. + +

+ +

+ + Enabling "client zoom" on a layer is done through the + serverResolutions option. See the clientzoom.js source + to see how this is done. + +

+
+ + diff --git a/examples/clientzoom.js b/examples/clientzoom.js new file mode 100644 index 0000000000..65ae758c3c --- /dev/null +++ b/examples/clientzoom.js @@ -0,0 +1,43 @@ +var map; + +function init() { + + map = new OpenLayers.Map({ + div: "map", + projection: "EPSG:900913", + units: "m", + maxExtent: new OpenLayers.Bounds( + -20037508.34, -20037508.34, 20037508.34, 20037508.34 + ), + controls: [], + fractionalZoom: true + }); + + var osm = new OpenLayers.Layer.OSM(null, null, { + resolutions: [156543.03390625, 78271.516953125, 39135.7584765625, + 19567.87923828125, 9783.939619140625, 4891.9698095703125, + 2445.9849047851562, 1222.9924523925781, 611.4962261962891, + 305.74811309814453, 152.87405654907226, 76.43702827453613, + 38.218514137268066, 19.109257068634033, 9.554628534317017, + 4.777314267158508, 2.388657133579254, 1.194328566789627, + 0.5971642833948135, 0.25, 0.1, 0.05], + serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625, + 19567.87923828125, 9783.939619140625, + 4891.9698095703125, 2445.9849047851562, + 1222.9924523925781, 611.4962261962891, + 305.74811309814453, 152.87405654907226, + 76.43702827453613, 38.218514137268066, + 19.109257068634033, 9.554628534317017, + 4.777314267158508, 2.388657133579254, + 1.194328566789627, 0.5971642833948135], + transitionEffect: 'resize' + }); + + map.addLayers([osm]); + map.addControls([ + new OpenLayers.Control.Navigation(), + new OpenLayers.Control.Attribution(), + new OpenLayers.Control.PanZoomBar() + ]); + map.setCenter(new OpenLayers.LonLat(659688.852138, 5710701.2962197), 18); +} diff --git a/lib/OpenLayers/Layer.js b/lib/OpenLayers/Layer.js index 3d2af3f1df..a1b3f98ed6 100644 --- a/lib/OpenLayers/Layer.js +++ b/lib/OpenLayers/Layer.js @@ -1242,11 +1242,11 @@ OpenLayers.Layer = OpenLayers.Class({ * {} An which is the passed-in * ,translated into view port pixels. */ - getViewPortPxFromLonLat: function (lonlat) { + getViewPortPxFromLonLat: function (lonlat, resolution) { var px = null; if (lonlat != null) { - var resolution = this.map.getResolution(); - var extent = this.map.getExtent(); + resolution = resolution || this.map.getResolution(); + var extent = this.map.calculateBounds(null, resolution); px = new OpenLayers.Pixel( (1/resolution * (lonlat.lon - extent.left)), (1/resolution * (extent.top - lonlat.lat)) diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js index 0ad307eed9..fb82c253f7 100644 --- a/lib/OpenLayers/Layer/Grid.js +++ b/lib/OpenLayers/Layer/Grid.js @@ -98,6 +98,13 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { */ tileLoadingDelay: 100, + /** + * Property: serverResolutions + * {Array(Number}} This property is documented in subclasses as + * an API property. + */ + serverResolutions: null, + /** * Property: timerId * {Number} - The id of the tileLoadingDelay timer. @@ -221,8 +228,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * dragging - {Boolean} */ moveTo:function(bounds, zoomChanged, dragging) { + OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); - + bounds = bounds || this.map.getExtent(); if (bounds != null) { @@ -238,18 +246,41 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { // We want to redraw whenever even the slightest part of the // current bounds is not contained by our tile. // (thus, we do not specify partial -- its default is false) - if ( forceReTile || + if ( forceReTile || (!dragging && !tilesBounds.containsBounds(bounds))) { this.initSingleTile(bounds); } } else { - + // if the bounds have changed such that they are not even // *partially* contained by our tiles (IE user has // programmatically panned to the other side of the earth) // then we want to reTile (thus, partial true). - // - if (forceReTile || !tilesBounds.containsBounds(bounds, true)) { + + forceReTile = forceReTile || + !tilesBounds.containsBounds(bounds, true); + + var resolution = this.map.getResolution(); + var serverResolution = + this.getServerResolution(resolution); + + if(resolution !== serverResolution) { + bounds = this.map.calculateBounds(null, serverResolution); + if(forceReTile) { + // stretch the layer div + var scale = serverResolution / resolution; + this.transformDiv(scale); + } + } else { + // reset the layer width, height, left, top, to deal with + // the case where the layer was previously transformed + this.div.style.width = '100%'; + this.div.style.height = '100%'; + this.div.style.left = '0%'; + this.div.style.top = '0%'; + } + + if(forceReTile) { this.initGriddedTiles(bounds); } else { this.scheduleMoveGriddedTiles(); @@ -258,6 +289,88 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { } }, + /** + * Method: getServerResolution + * Return the server-supported resolution that is the closest to + * the resolution passed as a parameter. If no resolution is + * passed the map resolution is used. + * + * Parameters: + * resolution - {Number} The base resolution. + * + * Returns: + * {Number} The closest server supported resolution. + */ + getServerResolution: function(resolution) { + resolution = resolution || this.map.getResolution(); + if(this.serverResolutions && + OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) { + var i, serverResolution; + for(var i=this.serverResolutions.length-1; i>= 0; i--) { + serverResolution = this.serverResolutions[i]; + if(serverResolution > resolution) { + resolution = serverResolution; + break; + } + } + if(i === -1) { + throw 'no appropriate resolution in serverResolutions'; + } + } + return resolution; + }, + + /** + * Method: getServerZoom + * Return the zoom value corresponding to the best zoom supported by the server + * resolution. + * + * Returns: + * {Number} The closest server supported zoom. + */ + getServerZoom: function() { + return this.map.getZoomForResolution(this.getServerResolution()); + }, + + /** + * Method: transformDiv + * Transform the layer div. + * + * Parameters: + * scale - {Number} The value by which the layer div is to + * be scaled. + */ + transformDiv: function(scale) { + + // scale the layer div + + this.div.style.width = 100 * scale + '%'; + this.div.style.height = 100 * scale + '%'; + + // and translate the layer div as necessary + + var size = this.map.getSize(); + var lcX = parseInt(this.map.layerContainerDiv.style.left); + var lcY = parseInt(this.map.layerContainerDiv.style.top); + var x = (lcX - (size.w / 2.)) * (scale - 1); + var y = (lcY - (size.h / 2.)) * (scale - 1); + + this.div.style.left = x + '%'; + this.div.style.top = y + '%'; + }, + + + /** + * Method: getResolutionScale + * Return the value by which the layer is currently scaled. + * + * Returns: + * {Number} The resolution scale. + */ + getResolutionScale: function() { + return parseInt(this.div.style.width) / 100; + }, + /** * Method: moveByPx * Move the layer based on pixel vector. @@ -459,7 +572,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * bounds - {} */ initGriddedTiles:function(bounds) { - + // work out mininum number of rows and columns; this is the number of // tiles required to cover the viewport plus at least one for panning @@ -470,7 +583,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { Math.max(1, 2 * this.buffer); var origin = this.getTileOrigin(); - var resolution = this.map.getResolution(); + var resolution = this.getServerResolution(); var tileLayout = this.calculateGridLayout(bounds, origin, resolution); @@ -492,7 +605,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left); var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top); - + do { var row = this.grid[rowidx++]; @@ -701,17 +814,25 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { moveGriddedTiles: function() { var shifted = true; var buffer = this.buffer || 1; - var tlLayer = this.grid[0][0].position; + var scale = this.getResolutionScale(); + var tlLayer = this.grid[0][0].position.clone(); + tlLayer.x *= scale; + tlLayer.y *= scale; + tlLayer = tlLayer.add(parseInt(this.div.style.left), + parseInt(this.div.style.top)); var offsetX = parseInt(this.map.layerContainerDiv.style.left); var offsetY = parseInt(this.map.layerContainerDiv.style.top); var tlViewPort = tlLayer.add(offsetX, offsetY); - if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) { + var tileSize = this.tileSize.clone(); + tileSize.w *= scale; + tileSize.h *= scale; + if (tlViewPort.x > -tileSize.w * (buffer - 1)) { this.shiftColumn(true); - } else if (tlViewPort.x < -this.tileSize.w * buffer) { + } else if (tlViewPort.x < -tileSize.w * buffer) { this.shiftColumn(false); - } else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) { + } else if (tlViewPort.y > -tileSize.h * (buffer - 1)) { this.shiftRow(true); - } else if (tlViewPort.y < -this.tileSize.h * buffer) { + } else if (tlViewPort.y < -tileSize.h * buffer) { this.shiftRow(false); } else { shifted = false; @@ -737,7 +858,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { var grid = this.grid; var modelRow = grid[modelRowIndex]; - var resolution = this.map.getResolution(); + var resolution = this.getServerResolution(); var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h; var deltaLat = resolution * -deltaY; @@ -770,7 +891,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { */ shiftColumn: function(prepend) { var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w; - var resolution = this.map.getResolution(); + var resolution = this.getServerResolution(); var deltaLon = resolution * deltaX; for (var i=0, len=this.grid.length; i can include + * resolutions that the server supports and that you don't want to + * provide with this layer; you can also look at , which is + * an alternative to for that specific purpose. + * (b) The map can work with resolutions that aren't supported by + * the server, i.e. that aren't in . When the + * map is displayed in such a resolution data for the closest + * server-supported resolution is loaded and the layer div is + * stretched as necessary. */ serverResolutions: null, @@ -162,12 +171,12 @@ OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, { */ getURL: function (bounds) { bounds = this.adjustBounds(bounds); - var res = this.map.getResolution(); + var res = this.getServerResolution(); var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w)); var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h)); var z = this.serverResolutions != null ? OpenLayers.Util.indexOf(this.serverResolutions, res) : - this.map.getZoom() + this.zoomOffset; + this.getServerZoom() + this.zoomOffset; var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type; var url = this.url; if (OpenLayers.Util.isArray(url)) { diff --git a/lib/OpenLayers/Layer/TileCache.js b/lib/OpenLayers/Layer/TileCache.js index 3510af5c5f..36b1bd2e94 100644 --- a/lib/OpenLayers/Layer/TileCache.js +++ b/lib/OpenLayers/Layer/TileCache.js @@ -37,8 +37,15 @@ OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, { /** * APIProperty: serverResolutions - * {Array} A list of all resolutions available on the server. Only set this - * property if the map resolutions differs from the server. + * {Array} A list of all resolutions available on the server. Only set this + * property if the map resolutions differ from the server. This + * property serves two purposes. (a) can include + * resolutions that the server supports and that you don't want to + * provide with this layer. (b) The map can work with resolutions + * that aren't supported by the server, i.e. that aren't in + * . When the map is displayed in such a resolution + * data for the closest server-supported resolution is loaded and the + * layer div is stretched as necessary. */ serverResolutions: null, @@ -102,7 +109,7 @@ OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, { * passed-in bounds and appropriate tile size specified as parameters. */ getURL: function(bounds) { - var res = this.map.getResolution(); + var res = this.getServerResolution(); var bbox = this.maxExtent; var size = this.tileSize; var tileX = Math.round((bounds.left - bbox.left) / (res * size.w)); diff --git a/lib/OpenLayers/Layer/WMTS.js b/lib/OpenLayers/Layer/WMTS.js index 0e6045bf59..54d6d55be0 100644 --- a/lib/OpenLayers/Layer/WMTS.js +++ b/lib/OpenLayers/Layer/WMTS.js @@ -150,7 +150,23 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { * the property. Defaults to 0 (no zoom offset). */ zoomOffset: 0, - + + /** + * APIProperty: serverResolutions + * {Array} A list of all resolutions available on the server. Only set this + * property if the map resolutions differ from the server. This + * property serves two purposes. (a) can include + * resolutions that the server supports and that you don't want to + * provide with this layer; you can also look at , which is + * an alternative to for that specific purpose. + * (b) The map can work with resolutions that aren't supported by + * the server, i.e. that aren't in . When the + * map is displayed in such a resolution data for the closest + * server-supported resolution is loaded and the layer div is + * stretched as necessary. + */ + serverResolutions: null, + /** * Property: formatSuffixMap * {Object} a map between WMTS 'format' request parameter and tile image file suffix @@ -308,6 +324,17 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { // copy/set any non-init, non-simple values here return obj; }, + + /** + * Method: getIdentifier + * Get the current index in the matrixIds array. + */ + getIdentifier: function() { + return this.serverResolutions != null ? + OpenLayers.Util.indexOf(this.serverResolutions, + this.getServerResolution()) : + this.getServerZoom() + this.zoomOffset; + }, /** * Method: getMatrix @@ -316,7 +343,7 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { getMatrix: function() { var matrix; if (!this.matrixIds || this.matrixIds.length === 0) { - matrix = {identifier: this.map.getZoom() + this.zoomOffset}; + matrix = {identifier: this.getIdentifier()}; } else { // get appropriate matrix given the map scale if possible if ("scaleDenominator" in this.matrixIds[0]) { @@ -324,7 +351,7 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, { var denom = OpenLayers.METERS_PER_INCH * OpenLayers.INCHES_PER_UNIT[this.units] * - this.map.getResolution() / 0.28E-3; + this.getServerResolution() / 0.28E-3; var diff = Number.POSITIVE_INFINITY; var delta; for (var i=0, ii=this.matrixIds.length; i can include + * resolutions that the server supports and that you don't want to + * provide with this layer; you can also look at , which is + * an alternative to for that specific purpose. + * (b) The map can work with resolutions that aren't supported by + * the server, i.e. that aren't in . When the + * map is displayed in such a resolution data for the closest + * server-supported resolution is loaded and the layer div is + * stretched as necessary. */ serverResolutions: null, @@ -139,7 +148,7 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { * {Object} - an object with x, y and z properties. */ getXYZ: function(bounds) { - var res = this.map.getResolution(); + var res = this.getServerResolution(); var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)); var y = Math.round((this.maxExtent.top - bounds.top) / @@ -147,7 +156,7 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, { var resolutions = this.serverResolutions || this.resolutions; var z = this.zoomOffset == 0 ? OpenLayers.Util.indexOf(resolutions, res) : - this.map.getZoom() + this.zoomOffset; + this.getServerZoom() + this.zoomOffset; var limit = Math.pow(2, z); if (this.wrapDateLine) diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js index cad15bf620..3f0f93fe55 100644 --- a/lib/OpenLayers/Map.js +++ b/lib/OpenLayers/Map.js @@ -536,6 +536,8 @@ OpenLayers.Map = OpenLayers.Class({ // the layerContainerDiv is the one that holds all the layers id = this.id + "_OpenLayers_Container"; this.layerContainerDiv = OpenLayers.Util.createDiv(id); + this.layerContainerDiv.style.width = '100px'; + this.layerContainerDiv.style.height = '100px'; this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1; this.eventsDiv.appendChild(this.layerContainerDiv); diff --git a/lib/OpenLayers/Tile/BackBufferable.js b/lib/OpenLayers/Tile/BackBufferable.js index db897c2f7c..689d7386d5 100644 --- a/lib/OpenLayers/Tile/BackBufferable.js +++ b/lib/OpenLayers/Tile/BackBufferable.js @@ -90,6 +90,22 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, { * Returns: * {DOMElement} A copy of the tile's markup. */ + + /** + * Method: getTileResolution + * Get the tile's actual resolution. + * + * Returns: + * {Number} + */ + getTileResolution: function() { + var layer = this.layer, + map = layer.map, + mapResolution = map.getResolution(); + return layer.getServerResolution ? + layer.getServerResolution(mapResolution) : + mapResolution; + }, /** * Method: setBackBufferData @@ -99,7 +115,7 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, { setBackBufferData: function() { this.backBufferData = OpenLayers.Util.extend(this.backBufferData, { bounds: this.bounds, - resolution: this.layer.map.getResolution() + resolution: this.getTileResolution() }); }, @@ -119,8 +135,9 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, { data = this.backBufferData, tile = this.getTile(), backBuffer = data.tile, - resolution = data.resolution, - ratio = resolution ? resolution / map.getResolution() : 1, + prevResolution = data.resolution, + nextResolution = this.getTileResolution(), + ratio = prevResolution ? prevResolution / nextResolution : 1, // Cases where we don't position and return a back buffer, but only // update backBufferData and return undefined: @@ -131,7 +148,8 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, { // (3) we don't have a tile available that we could use as buffer noTile = !(tile && tile.childNodes.length > 0), // (4) no backbuffer is displayed for a tile that's still loading - noBackBuffer = !backBuffer && this.isLoading; + noBackBuffer = !backBuffer && this.isLoading; + if (notNeeded || noParent || noTile || noBackBuffer) { this.setBackBufferData(); return; @@ -139,26 +157,26 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, { // Create a back buffer tile and add it to the DOM if (!backBuffer) { - backBuffer = this.createBackBuffer(); + backBuffer = this.insertBackBuffer(); // some browsers fire the onload event before the image is // displayed, so we keep the buffer until the whole layer finished // loading to avoid visual glitches layer.events.register("loadend", this, this.resetBackBuffer); data.tile = backBuffer; - layer.div.insertBefore(backBuffer, tile); } // Position the back buffer now that we have one var lonLat = {lon: data.bounds.left, lat: data.bounds.top}, - position = map.getPixelFromLonLat(lonLat), + position = layer.getViewPortPxFromLonLat(lonLat, nextResolution), containerStyle = map.layerContainerDiv.style, leftOffset = parseInt(containerStyle.left, 10), topOffset = parseInt(containerStyle.top, 10), style = backBuffer.style; - style.left = (position.x - leftOffset) + "px"; - style.top = (position.y - topOffset) + "px"; - style.width = (this.size.w * ratio) + "px"; - style.height = (this.size.h * ratio) + "px"; + + style.left = (position.x - leftOffset) + "%"; + style.top = (position.y - topOffset) + "%"; + style.width = (this.size.w * ratio) + "%"; + style.height = (this.size.h * ratio) + "%"; return backBuffer; }, diff --git a/lib/OpenLayers/Tile/Image.js b/lib/OpenLayers/Tile/Image.js index ff2b6ace99..c3037256b6 100644 --- a/lib/OpenLayers/Tile/Image.js +++ b/lib/OpenLayers/Tile/Image.js @@ -187,10 +187,10 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, { */ positionTile: function() { var style = this.frame.style; - style.left = this.position.x + "px"; - style.top = this.position.y + "px"; - style.width = this.size.w + "px"; - style.height = this.size.h + "px"; + style.left = this.position.x + "%"; + style.top = this.position.y + "%"; + style.width = this.size.w + "%"; + style.height = this.size.h + "%"; }, /** @@ -320,14 +320,16 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, { }, /** - * Method: createBackBuffer - * Create a copy of this tile's markup for the 's backBufferDiv + * Method: insertBackBuffer + * Create a copy of this tile's markup and insert it to the layer + * div. * * Returns: - * {DOMElement} a clone of the tile content + * {DOMElement} The back buffer. */ - createBackBuffer: function() { + insertBackBuffer: function() { var frame = this.frame.cloneNode(false); + this.layer.div.insertBefore(frame, this.frame); frame.appendChild(this.imgDiv); this.imgDiv = null; return frame; diff --git a/tests/Layer/ArcGIS93Rest.html b/tests/Layer/ArcGIS93Rest.html index 184a9f7c01..ff11955dbb 100644 --- a/tests/Layer/ArcGIS93Rest.html +++ b/tests/Layer/ArcGIS93Rest.html @@ -75,8 +75,8 @@ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams), "image src is created correctly via addtile" ); - t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" ); - t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" ); var firstChild = layer.div.firstChild.firstChild; t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" ); diff --git a/tests/Layer/Grid.html b/tests/Layer/Grid.html index d64dfca90b..fd8afb6e9d 100644 --- a/tests/Layer/Grid.html +++ b/tests/Layer/Grid.html @@ -624,6 +624,195 @@ t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" ); } + function test_getServerResolution(t) { + + t.plan(4); + + var layer = new OpenLayers.Layer.Grid('', '', {}, {}); + var res; + + res = layer.getServerResolution(1); + t.eq(res, 1, '[1] getServerResolution return value is correct'); + + layer.serverResolutions = [2, 1]; + res = layer.getServerResolution(1); + t.eq(res, 1, '[2] getServerResolution return value is correct'); + + layer.serverResolutions = [2]; + res = layer.getServerResolution(1); + t.eq(res, 2, '[3] getServerResolution return value is correct'); + + var exc; + layer.serverResolutions = [0.5]; + try { + res = layer.getServerResolution(1); + } catch(e) { + exc = e; + } + t.ok(exc != undefined, '[4] getServerResolution generates exception'); + } + + function test_getServerZoom(t) { + + t.plan(5); + + var resolution, zoom; + var map = new OpenLayers.Map('map', { + resolutions: [8, 4, 2, 1, 0.5], + getResolution: function() { + return resolution; + } + }); + var layer = new OpenLayers.Layer.WMS('', '', {}, {isBaseLayer: true}); + map.addLayer(layer); + + resolution = 8; + zoom = layer.getServerZoom(); + t.eq(zoom, 0, '[1] getServerZoom return value is correct'); + + resolution = 4; + zoom = layer.getServerZoom(); + t.eq(zoom, 1, '[2] getServerZoom return value is correct'); + + layer.serverResolutions = [2, 1]; + resolution = 1; + zoom = layer.getServerZoom(); + t.eq(zoom, 3, '[3] getServerZoom return value is correct'); + + layer.serverResolutions = [2]; + resolution = 0.5; + zoom = layer.getServerZoom(); + t.eq(zoom, 2, '[4] getServerZoom return value is correct'); + + var exc; + layer.serverResolutions = [0.5]; + resolution = 1; + try { + zoom = layer.getServerZoom(); + } catch(e) { + exc = e; + } + t.ok(exc != undefined, '[4] getServerZoom generates exception'); + + map.destroy(); + } + + function test_moveTo_scale(t) { + + t.plan(11); + + var map = new OpenLayers.Map('map', { + resolutions: [32, 16, 8, 4, 2, 1] + }); + var layer = new OpenLayers.Layer.WMS('', '', {}, { + isBaseLayer: true, + serverResolutions: [32, 16, 8] + }); + map.addLayer(layer); + + // initial resolution is 8 + map.setCenter(new OpenLayers.LonLat(0, 0), 2); + + // test initial conditions + t.eq(layer.div.style.width, '100%', 'layer div scale is 1'); + + // change from resolution 8 to 4 + map.zoomTo(3); + t.eq(layer.div.style.width, '200%', '[8->4] layer div scale is 2'); + + // change from resolution 8 to 2 + map.zoomTo(2); map.zoomTo(4); + t.eq(layer.div.style.width, '400%', '[8->2] layer div scale is 4'); + + // change from resolution 8 to 1 + map.zoomTo(2); map.zoomTo(5); + t.eq(layer.div.style.width, '800%', '[8->1] layer div scale is 8'); + + // change from resolution 4 to 2 + map.zoomTo(3); map.zoomTo(4); + t.eq(layer.div.style.width, '400%', '[4->2] layer div scale is 4'); + + // change from resolution 4 to 1 + map.zoomTo(3); map.zoomTo(5); + t.eq(layer.div.style.width, '800%', '[4->1] layer div scale is 8'); + + // change from resolution 2 to 1 + map.zoomTo(4); map.zoomTo(5); + t.eq(layer.div.style.width, '800%', '[2->1] layer div scale is 8'); + + // change from resolution 1 to 2 + map.zoomTo(5); map.zoomTo(4); + t.eq(layer.div.style.width, '400%', '[1->2] layer div scale is 4'); + + // change from resolution 1 to 4 + map.zoomTo(5); map.zoomTo(3); + t.eq(layer.div.style.width, '200%', '[1->4] layer div scale is 2'); + + // change from resolution 1 to 8 + map.zoomTo(5); map.zoomTo(2); + t.eq(layer.div.style.width, '100%', '[1->8] layer div scale is 1'); + + // change from resolution 1 to 16 + map.zoomTo(5); map.zoomTo(1); + t.eq(layer.div.style.width, '100%', '[1->16] layer div scale is 1'); + + map.destroy(); + } + + function test_transformDiv(t) { + + t.plan(8); + + var map = new OpenLayers.Map('map4'); + var layer = new OpenLayers.Layer.WMS('', '', {}, { + isBaseLayer: true + }); + map.addLayer(layer); + map.zoomToMaxExtent(); + + // the layer container's dimensions are 100px width 100px height + // the position of the viewport center is 384, 256 + + layer.transformDiv(2); + + t.eq(layer.div.style.width, '200%', '[1] layer div has correct width'); + t.eq(layer.div.style.height, '200%', '[1] layer div has correct height'); + + t.eq(layer.div.style.left, '-384%', '[1] layer div has correct left'); + t.eq(layer.div.style.top, '-256%', '[1] layer div has correct top'); + + // now move the layer container and test again + + map.layerContainerDiv.style.left = '-1024px'; + map.layerContainerDiv.style.top = '768px'; + + layer.transformDiv(2); + + t.eq(layer.div.style.width, '200%', '[2] layer div has correct width'); + t.eq(layer.div.style.height, '200%', '[2] layer div has correct height'); + + t.eq(layer.div.style.left, '-1408%', '[2] layer div has correct left'); + t.eq(layer.div.style.top, '512%', '[2] layer div has correct top'); + + map.destroy(); + } + + function test_getResolutionScale(t) { + t.plan(1); + + var map = new OpenLayers.Map('map'); + var layer = new OpenLayers.Layer.WMS('', '', {}, { + isBaseLayer: true + }); + map.addLayer(layer); + map.zoomToMaxExtent(); + + layer.transformDiv(2); + var scale = layer.getResolutionScale(); + t.eq(scale, 2, 'getResolutionScale returns correct value'); + + map.destroy(); + } @@ -631,5 +820,6 @@ + diff --git a/tests/Layer/MapServer.html b/tests/Layer/MapServer.html index 3f96578402..0fab1613e3 100644 --- a/tests/Layer/MapServer.html +++ b/tests/Layer/MapServer.html @@ -56,8 +56,8 @@ t.eq( tile.url, url + "?" + OpenLayers.Util.getParameterString(tParams).replace(/,/g, "+"), "image src is created correctly via addtile" ); - t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" ); - t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" ); var firstChild = layer.div.firstChild.firstChild; t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" ); diff --git a/tests/Layer/WMS.html b/tests/Layer/WMS.html index c8a91a1751..51228b0afb 100644 --- a/tests/Layer/WMS.html +++ b/tests/Layer/WMS.html @@ -86,8 +86,8 @@ t.eq( tile.url, layer.getFullRequestString(tParams), "image src is created correctly via addtile" ); - t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" ); - t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" ); var firstChild = layer.div.firstChild.firstChild; t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" ); @@ -117,8 +117,8 @@ t.eq( tile.url, layer.getFullRequestString(tParams), "image src is created correctly via addtile" ); - t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" ); - t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" ); + t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" ); var firstChild = layer.div.firstChild.firstChild; t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" ); diff --git a/tests/Layer/WMTS.html b/tests/Layer/WMTS.html index aa1bed4033..98bb6cdb07 100644 --- a/tests/Layer/WMTS.html +++ b/tests/Layer/WMTS.html @@ -258,6 +258,37 @@ t.ok( layer2.grid == null, "tiles appropriately destroyed"); map.destroy(); } + + function test_getIdentifier(t) { + t.plan(2); + + var map = new OpenLayers.Map('map'); + var layer, identifier; + + layer = new OpenLayers.Layer.WMTS({ + name: "Blue Marble WMTS", + url: "http://example.com/wmts/", + layer: "world", + style: "blue_marble", + matrixSet: "arcgis_online", + tileSize: new OpenLayers.Size(512, 512), + requestEncoding: "REST", + }); + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0,0), 5); + + layer.zoomOffset = 2; + identifier = layer.getIdentifier(); + t.eq(identifier, 7, '[zoomOffset] getIdentifier return value is correct'); + + layer.serverResolutions = ['offset', 1.40625, 0.703125, 0.3515625, 0.17578125, + 0.087890625, 0.0439453125]; + identifier = layer.getIdentifier(); + t.eq(identifier, 6, '[serverResolutions] getIdentifier return value is correct'); + + map.destroy(); + } + diff --git a/tests/Tile/BackBufferable.html b/tests/Tile/BackBufferable.html index 95727ceb58..22d1a1e3bd 100644 --- a/tests/Tile/BackBufferable.html +++ b/tests/Tile/BackBufferable.html @@ -80,7 +80,7 @@ tile.isLoading = false; map.zoomIn(); tile.updateBackBuffer(); - t.eq(tile.backBufferData.tile.style.width, (layer.tileSize.w*2)+"px", + t.eq(tile.backBufferData.tile.style.width, (layer.tileSize.w*2)+"%", "backBuffer frame correctly resized"); map.removeLayer(layer); map.destroy(); @@ -106,9 +106,123 @@ map.destroy(); } + function test_updateBackBuffer_scaled_layer(t) { + t.plan(16); + + // + // set up + // + + var backBuffer; + + var map = new OpenLayers.Map('map', { + resolutions: [32, 16, 8, 4, 2, 1] + }); + + var serverResolitions = layer.serverResolutions; + layer.serverResolutions = [32, 16, 8]; + + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0, 0), 2); + + tile = new OpenLayers.Tile.Image(layer, position, bounds, null); + tile.backBufferMode = 2; // transition effect + + // mock createBackBuffer to avoid removing the image + // div from the tile + tile.insertBackBuffer = function() { + return this.frame.cloneNode(false); + }; + tile.removeBackBuffer = function() { + }; + + // + // test + // + + tile.draw(); + // check initial state + t.eq(tile.backBufferData.resolution, 8, + 'resolution 8 is set in the back buffer data'); + + tile.isLoading = false; + + // change resolution from 8 to 4 + map.zoomTo(3); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer == undefined, + '[8->4] updateBackBuffer returns undefined'); + + // change resolution from 8 to 2 + map.zoomTo(2); tile.setBackBufferData(); map.zoomTo(4); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer == undefined, + '[8->2] updateBackBuffer returns undefined'); + + // change resolution from 16 to 4 + map.zoomTo(1); tile.setBackBufferData(); map.zoomTo(3); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer != undefined, + '[16->4] updateBackBuffer returns a back buffer tile'); + t.eq(backBuffer.style.width, '512%', + '[16->4] back buffer width is as expected'); + t.eq(backBuffer.style.width, '512%', + '[16->4] back buffer height is as expected'); + + // change resolution from 32 to 1 + map.zoomTo(0); tile.setBackBufferData(); map.zoomTo(5); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer != undefined, + '[32->1] updateBackBuffer returns a back buffer tile'); + t.eq(backBuffer.style.width, '1024%', + '[32->1] back buffer width is as expected'); + t.eq(backBuffer.style.width, '1024%', + '[32->1] back buffer height is as expected'); + + // change resolution from 4 to 2 + map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(4); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer == undefined, + '[4->2] updateBackBuffer returns undefined'); + + // change resolution from 4 to 1 + map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(5); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer == undefined, + '[4->1] updateBackBuffer returns undefined'); + + // change resolution from 1 to 4 + map.zoomTo(5); tile.setBackBufferData(); map.zoomTo(3); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer == undefined, + '[1->4] updateBackBuffer returns undefined'); + + // change resolution from 4 to 8 + map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(2); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer == undefined, + '[4->8] updateBackBuffer returns undefined'); + + // change resolution from 4 to 16 + map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(1); + backBuffer = tile.updateBackBuffer(); + t.ok(backBuffer != undefined, + '[4->16] updateBackBuffer returns a back buffer tile'); + t.eq(backBuffer.style.width, '128%', + '[4->16] back buffer width is as expected'); + t.eq(backBuffer.style.width, '128%', + '[4->16] back buffer height is as expected'); + + // + // tear down + // + + map.removeLayer(layer); + map.destroy(); + }
- \ No newline at end of file + diff --git a/tests/Tile/Image.html b/tests/Tile/Image.html index 2c9f5da415..aa6041b9a9 100644 --- a/tests/Tile/Image.html +++ b/tests/Tile/Image.html @@ -300,8 +300,8 @@ map.destroy(); } - function test_createBackBuffer(t) { - t.plan(3); + function test_insertBackBuffer(t) { + t.plan(4); var map = new OpenLayers.Map('map'); var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", @@ -310,8 +310,9 @@ map.setCenter(new OpenLayers.LonLat(0,0), 5); var tile = layer.grid[0][0]; var img = tile.imgDiv; - var backBuffer = tile.createBackBuffer(); + var backBuffer = tile.insertBackBuffer(); t.eq(backBuffer.style.left, tile.frame.style.left, "backBuffer tile has same left style as frame"); + t.ok(backBuffer.parentNode === layer.div, "backBuffer inserted into layer div"); t.ok(backBuffer.firstChild === img, "image appended to backBuffer"); t.ok(tile.imgDiv == null, "image reference removed from tile"); map.destroy(); From fed387804efe8dc86adb1d83db9205ed9df83872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 7 Oct 2011 13:43:55 +0200 Subject: [PATCH 2/3] base-10 parseInt --- lib/OpenLayers/Layer/Grid.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js index babad41b69..e5fa551c9f 100644 --- a/lib/OpenLayers/Layer/Grid.js +++ b/lib/OpenLayers/Layer/Grid.js @@ -350,8 +350,8 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { // and translate the layer div as necessary var size = this.map.getSize(); - var lcX = parseInt(this.map.layerContainerDiv.style.left); - var lcY = parseInt(this.map.layerContainerDiv.style.top); + var lcX = parseInt(this.map.layerContainerDiv.style.left, 10); + var lcY = parseInt(this.map.layerContainerDiv.style.top, 10); var x = (lcX - (size.w / 2.)) * (scale - 1); var y = (lcY - (size.h / 2.)) * (scale - 1); @@ -368,7 +368,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { * {Number} The resolution scale. */ getResolutionScale: function() { - return parseInt(this.div.style.width) / 100; + return parseInt(this.div.style.width, 10) / 100; }, /** @@ -830,8 +830,8 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { var tlLayer = this.grid[0][0].position.clone(); tlLayer.x *= scale; tlLayer.y *= scale; - tlLayer = tlLayer.add(parseInt(this.div.style.left), - parseInt(this.div.style.top)); + tlLayer = tlLayer.add(parseInt(this.div.style.left, 10), + parseInt(this.div.style.top, 10)); var offsetX = parseInt(this.map.layerContainerDiv.style.left); var offsetY = parseInt(this.map.layerContainerDiv.style.top); var tlViewPort = tlLayer.add(offsetX, offsetY); From d8283ba60239367f54010ebe3599a3f9eef0864d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 7 Oct 2011 13:57:24 +0200 Subject: [PATCH 3/3] better text for the clientzoom example --- examples/clientzoom.html | 46 +++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/examples/clientzoom.html b/examples/clientzoom.html index ba92df08d5..f69f65bd21 100644 --- a/examples/clientzoom.html +++ b/examples/clientzoom.html @@ -15,11 +15,8 @@ bottom: 5px; } #map { - width: 512px; - height: 512px; - } - #docs { - width: 512px; + width: 600px; + height: 400px; } @@ -31,8 +28,8 @@

This example demonstrates the "client zoom" - functionality, where OpenLayers stretches the layer div if the - current resolution isn't supported by that layer's tile server. + functionality, where OpenLayers stretches the layer div when the + resolution is not supported by that layer's tile service.

@@ -42,35 +39,32 @@

- This map is configured with 22 resolutions, while the OSM tile - server supports only the first 19 resolutions. When the zoom - level is 19, 20 or 22 "client zoom" is applied on the OSM - layer, i.e. the OSM layer div is stretched as necessary. + The map of this example is configured with 22 resolutions, while + the OSM tile server supports the first 19 resolutions only. When + the zoom level is 19, 20 or 21 "client zoom" is applied to the OSM + layer, i.e. the OSM layer div is stretched as necessary. The map's + initial zoom is 18. So if you zoom in using the zoom bar's "+" + button you'll effectively trigger "client zoom".

- The map's initial zoom is 18. So if you zoom in using the zoom - bar's "+" button you'll see that "client zoom" is applied. + For demonstration purpose the map of this example has + fractionalZoom set to true. So "client zoom" also + applies if you choose arbitrary zoom levels using the slider of the + zoom bar, or shift-drag boxes to zoom to arbitrary extents. + "client zoom" therefore allows continous zooming for tiled layers.

- The map is even configured with fractionalZoom set - to true. So "client zoom" also applies if you choose arbitrary - zoom levels using the slider of the zoom bar, or shift-drag - boxes to zoom to arbitrary extents. - -

- -

- - Enabling "client zoom" on a layer is done through the - serverResolutions option. See the clientzoom.js source - to see how this is done. + Enabling "client zoom" on a layer is done by passing + serverResolutions to the layer constructor. + serverResolutions is the list of resolutions supported + by the tile service. See the clientzoom.js source.