diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js index b378b7d24b..22fb1b8500 100644 --- a/lib/OpenLayers.js +++ b/lib/OpenLayers.js @@ -121,7 +121,6 @@ "OpenLayers/Marker/Box.js", "OpenLayers/Popup.js", "OpenLayers/Tile.js", - "OpenLayers/Tile/BackBufferable.js", "OpenLayers/Tile/Image.js", "OpenLayers/Tile/Image/IFrame.js", "OpenLayers/Tile/WFS.js", diff --git a/lib/OpenLayers/Layer.js b/lib/OpenLayers/Layer.js index 46ec873be7..ab115223ca 100644 --- a/lib/OpenLayers/Layer.js +++ b/lib/OpenLayers/Layer.js @@ -314,13 +314,6 @@ OpenLayers.Layer = OpenLayers.Class({ */ transitionEffect: null, - /** - * Property: SUPPORTED_TRANSITIONS - * {Array} An immutable (that means don't change it!) list of supported - * transitionEffect values. - */ - SUPPORTED_TRANSITIONS: ['resize'], - /** * Property: metadata * {Object} This object can be used to store additional information on a diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js index 30021877cb..ce6332a3c9 100644 --- a/lib/OpenLayers/Layer/Grid.js +++ b/lib/OpenLayers/Layer/Grid.js @@ -111,6 +111,34 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { */ timerId: null, + /** + * Property: backBuffer + * {DOMElement} The back buffer. + */ + backBuffer: null, + + /** + * Property: gridResolution + * {Number} The resolution of the current grid. Used for backbuffering. + * This property is updated each the grid is initialized. + */ + gridResolution: null, + + /** + * Property: backBufferResolution + * {Number} The resolution of the current back buffer. This property is + * updated each time a back buffer is created. + */ + backBufferResolution: null, + + /** + * Property: backBufferLonLat + * {Object} The top-left corner of the current back buffer. Includes lon + * and lat properties. This object is updated each time a back buffer + * is created. + */ + backBufferLonLat: null, + /** * Constructor: OpenLayers.Layer.Grid * Create a new grid layer @@ -133,7 +161,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { this.events.addEventType("tileloaded"); this.grid = []; - + this._moveGriddedTiles = OpenLayers.Function.bind( this.moveGriddedTiles, this ); @@ -159,6 +187,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { */ destroy: function() { this.clearGrid(); + // clearGrid should remove any back buffer from the layer, + // so no need to call removeBackBuffer here + this.grid = null; this.tileSize = null; OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); @@ -180,6 +211,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { } } this.grid = []; + this.gridResolution = null; } }, @@ -212,6 +244,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { // we do not want to copy reference to grid, so we make a new array obj.grid = []; + obj.gridResolution = null; return obj; }, @@ -240,14 +273,37 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { // total bounds of the tiles var tilesBounds = this.getTilesBounds(); - + + // the new map resolution + var resolution = this.map.getResolution(); + + // the server-supported resolution for the new map resolution + var serverResolution = this.getServerResolution(resolution); + if (this.singleTile) { // 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 || (!dragging && !tilesBounds.containsBounds(bounds))) { + + // In single tile mode with no transition effect, we insert + // a non-scaled backbuffer when the layer is moved. But if + // a zoom occurs right after a move, i.e. before the new + // image is received, we need to remove the backbuffer, or + // an ill-positioned image will be visible during the zoom + // transition. + + if(zoomChanged && this.transitionEffect !== 'resize') { + this.removeBackBuffer(); + } + + if(!zoomChanged || this.transitionEffect === 'resize') { + this.applyBackBuffer(serverResolution); + } + this.initSingleTile(bounds); } } else { @@ -260,10 +316,6 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { 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) { @@ -281,6 +333,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { } if(forceReTile) { + if(zoomChanged && this.transitionEffect === 'resize') { + this.applyBackBuffer(serverResolution); + } this.initGriddedTiles(bounds); } else { this.scheduleMoveGriddedTiles(); @@ -306,7 +361,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { if(this.serverResolutions && OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) { var i, serverResolution; - for(var i=this.serverResolutions.length-1; i>= 0; i--) { + for(i=this.serverResolutions.length-1; i>= 0; i--) { serverResolution = this.serverResolutions[i]; if(serverResolution > resolution) { resolution = serverResolution; @@ -352,14 +407,13 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { var size = this.map.getSize(); 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); + var x = (lcX - (size.w / 2.0)) * (scale - 1); + var y = (lcY - (size.h / 2.0)) * (scale - 1); this.div.style.left = x + '%'; this.div.style.top = y + '%'; }, - /** * Method: getResolutionScale * Return the value by which the layer is currently scaled. @@ -371,6 +425,96 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { return parseInt(this.div.style.width, 10) / 100; }, + /** + * Method: applyBackBuffer + * Create, insert, scale and position a back buffer for the layer. + * + * Parameters: + * resolution - {Number} The resolution to transition to. + */ + applyBackBuffer: function(resolution) { + var backBuffer = this.backBuffer; + if(!backBuffer) { + backBuffer = this.createBackBuffer(); + if(!backBuffer) { + return; + } + this.div.insertBefore(backBuffer, this.div.firstChild); + this.backBuffer = backBuffer; + + // set some information in the instance for subsequent + // calls to applyBackBuffer where the same back buffer + // is reused + var topLeftTileBounds = this.grid[0][0].bounds; + this.backBufferLonLat = { + lon: topLeftTileBounds.left, + lat: topLeftTileBounds.top + }; + this.backBufferResolution = this.gridResolution; + } + + var style = backBuffer.style; + + // scale the back buffer + var ratio = this.backBufferResolution / resolution; + style.width = 100 * ratio + '%'; + style.height = 100 * ratio + '%'; + + // and position it (based on the grid's top-left corner) + var position = this.getViewPortPxFromLonLat( + this.backBufferLonLat, resolution); + var leftOffset = parseInt(this.map.layerContainerDiv.style.left, 10); + var topOffset = parseInt(this.map.layerContainerDiv.style.top, 10); + backBuffer.style.left = (position.x - leftOffset) + '%'; + backBuffer.style.top = (position.y - topOffset) + '%'; + }, + + /** + * Method: createBackBuffer + * Create a back buffer. + * + * Returns: + * {DOMElement} The DOM element for the back buffer, undefined if the + * grid isn't initialized yet. + */ + createBackBuffer: function() { + var backBuffer; + if(this.grid.length > 0) { + backBuffer = document.createElement('div'); + backBuffer.id = this.div.id + '_bb'; + backBuffer.className = 'olBackBuffer'; + backBuffer.style.position = 'absolute'; + backBuffer.style.width = '100%'; + backBuffer.style.height = '100%'; + for(var i=0, lenI=this.grid.length; i} The bounds of the tile to back. - * resolution - {Number} The resolution of the tile to back. - */ - backBufferData: null, - - /** - * Method: initialize - * Determines the backBuffer mode and registers events - */ - initialize: function() { - OpenLayers.Tile.prototype.initialize.apply(this, arguments); - - var transitionSupported = OpenLayers.Util.indexOf( - this.layer.SUPPORTED_TRANSITIONS, - this.layer.transitionEffect) != -1; - this.backBufferMode = (this.layer.singleTile && 1) | - (transitionSupported && 2); - - this.backBufferData = {}; - if (!this.size) { - this.size = new OpenLayers.Size(256, 256); - } - }, - - /** - * Method: draw - * Check that a tile should be drawn, and draw it. - * - * Returns: - * {Boolean} Was a tile drawn? - */ - draw: function() { - var draw = OpenLayers.Tile.prototype.shouldDraw.apply(this, arguments); - if (draw) { - this.updateBackBuffer(); - } - this.clear(); - if (!draw) { - this.resetBackBuffer(); - }; - return draw; - }, - - /** - * Method: getTile - * Get the tile's markup. To be implemented by subclasses. - * - * Returns: - * {DOMElement} The tile's markup - */ - - /** - * Method: createBackBuffer - * Create a copy of this tile's markup for the back buffer. To be - * implemented by subclasses. - * - * 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 - * Stores the current bounds and resolution, for offset and ratio - * calculations - */ - setBackBufferData: function() { - this.backBufferData = OpenLayers.Util.extend(this.backBufferData, { - bounds: this.bounds, - resolution: this.getTileResolution() - }); - }, - - /** - * Method: updateBackBuffer - * Update the , and return a new or reposition the - * backBuffer. When a backbuffer is returned, the tile's markup is not - * available any more. - * - * Returns: - * {HTMLDivElement} the tile's markup in a cloned element, or undefined if - * no backbuffer is currently available or needed - */ - updateBackBuffer: function() { - var layer = this.layer, map = layer.map, - backBufferMode = this.backBufferMode, - data = this.backBufferData, - tile = this.getTile(), - backBuffer = data.tile, - 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: - // (1) current ratio and backBufferMode dont't require a backbuffer - notNeeded = !(ratio == 1 ? backBufferMode & 1 : backBufferMode & 2), - // (2) the tile is not appended to the layer's div - noParent = tile && tile.parentNode !== layer.div, - // (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; - - if (notNeeded || noParent || noTile || noBackBuffer) { - this.setBackBufferData(); - return; - } - - // Create a back buffer tile and add it to the DOM - if (!backBuffer) { - 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; - } - - // Position the back buffer now that we have one - var lonLat = {lon: data.bounds.left, lat: data.bounds.top}, - 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) + "%"; - style.top = (position.y - topOffset) + "%"; - style.width = (this.size.w * ratio) + "%"; - style.height = (this.size.h * ratio) + "%"; - - return backBuffer; - }, - - /** - * Method: resetBackBuffer - * Handler for the layer's loadend event. - */ - resetBackBuffer: function() { - this.layer.events.unregister("loadend", this, this.resetBackBuffer); - this.removeBackBuffer(); - this.setBackBufferData(); - }, - - /** - * Method: removeBackBuffer - * Removes the backBuffer for this tile. - */ - removeBackBuffer: function() { - var backBufferData = this.backBufferData; - var backBuffer = backBufferData.tile; - delete backBufferData.tile; - var parent = backBuffer && backBuffer.parentNode; - if (backBuffer) { - parent.removeChild(backBuffer); - } - }, - - /** - * APIMethod: destroy - * nullify references to prevent circular references and memory leaks - */ - destroy: function() { - this.removeBackBuffer(); - this.layer.events.unregister("loadend", this, this.resetBackBuffer); - this.backBufferData = null; - OpenLayers.Tile.prototype.destroy.apply(this, arguments); - } - -}); diff --git a/lib/OpenLayers/Tile/Image.js b/lib/OpenLayers/Tile/Image.js index 087693d79f..ee35ed76bb 100644 --- a/lib/OpenLayers/Tile/Image.js +++ b/lib/OpenLayers/Tile/Image.js @@ -5,7 +5,7 @@ /** - * @requires OpenLayers/Tile/BackBufferable.js + * @requires OpenLayers/Tile.js */ /** @@ -15,9 +15,9 @@ * constructor. * * Inherits from: - * - + * - */ -OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, { +OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, { /** * Property: url @@ -96,7 +96,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, { * options - {Object} */ initialize: function(layer, position, bounds, url, size, options) { - OpenLayers.Tile.BackBufferable.prototype.initialize.apply(this, arguments); + OpenLayers.Tile.prototype.initialize.apply(this, arguments); this.url = url; //deprecated remove me @@ -123,7 +123,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, { } // don't handle async requests any more this.asyncRequestId = null; - OpenLayers.Tile.BackBufferable.prototype.destroy.apply(this, arguments); + OpenLayers.Tile.prototype.destroy.apply(this, arguments); }, /** @@ -134,7 +134,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, { * {Boolean} Was a tile drawn? */ draw: function() { - var drawn = OpenLayers.Tile.BackBufferable.prototype.draw.apply(this, arguments); + var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments); if (drawn) { if (this.layer != this.layer.map.baseLayer && this.layer.reproject) { this.bounds = this.getBoundsFromBaseLayer(this.position); @@ -320,19 +320,23 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, { }, /** - * Method: insertBackBuffer - * Create a copy of this tile's markup and insert it to the layer - * div. + * Method: createBackBuffer + * Create a backbuffer for this tile. A backbuffer isn't exactly a clone + * of the tile's markup, because we want to avoid the reloading of the + * image. So we clone the frame, and steal the image from the tile. * * Returns: - * {DOMElement} The back buffer. + * {DOMElement} The markup, or undefined if the tile has no image + * or if it's currently loading. */ - insertBackBuffer: function() { - var frame = this.frame.cloneNode(false); - this.layer.div.insertBefore(frame, this.frame); - frame.appendChild(this.imgDiv); + createBackBuffer: function() { + if(!this.imgDiv || this.isLoading) { + return; + } + var backBuffer = this.frame.cloneNode(false); + backBuffer.appendChild(this.imgDiv); this.imgDiv = null; - return frame; + return backBuffer; }, /** diff --git a/lib/OpenLayers/Tile/Image/IFrame.js b/lib/OpenLayers/Tile/Image/IFrame.js index 9616036815..d67db5f70e 100644 --- a/lib/OpenLayers/Tile/Image/IFrame.js +++ b/lib/OpenLayers/Tile/Image/IFrame.js @@ -15,9 +15,6 @@ * * This mixin will be applied to instances * configured with set. - * - * Inherits from: - * - */ OpenLayers.Tile.Image.IFrame = { @@ -29,40 +26,52 @@ OpenLayers.Tile.Image.IFrame = { useIFrame: null, /** - * Method: updateBackBuffer - * Update the , and return a new or reposition the - * backBuffer. When a backbuffer is returned, the tile's markup is not - * available any more. - * - * Returns: - * {HTMLDivElement} the tile's markup in a cloned element, or undefined if - * no backbuffer is currently available or needed + * Method: draw + * Set useIFrame in the instance, and operate the image/iframe switch. + * Then call Tile.Image.draw. + * + * Returns: + * {Boolean} */ - updateBackBuffer: function() { - this.url = this.layer.getURL(this.bounds); - var usedIFrame = this.useIFrame; - this.useIFrame = this.maxGetUrlLength !== null && !this.layer.async && - this.url.length > this.maxGetUrlLength; - var fromIFrame = usedIFrame && !this.useIFrame; - var toIFrame = !usedIFrame && this.useIFrame; - if (fromIFrame || toIFrame) { - // switch between get (image) and post (iframe) - this.clear(); - if (this.imgDiv && this.imgDiv.parentNode === this.frame) { - this.frame.removeChild(this.imgDiv); - } - this.imgDiv = null; - if (fromIFrame) { - // remove eventPane - this.frame.removeChild(this.frame.firstChild); - this.resetBackBuffer(); + draw: function() { + var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this); + if(draw) { + + // this.url isn't set to the currect value yet, so we call getURL + // on the layer and store the result in a local variable + var url = this.layer.getURL(this.bounds); + + var usedIFrame = this.useIFrame; + this.useIFrame = this.maxGetUrlLength !== null && + !this.layer.async && + url.length > this.maxGetUrlLength; + + var fromIFrame = usedIFrame && !this.useIFrame; + var toIFrame = !usedIFrame && this.useIFrame; + + if(fromIFrame || toIFrame) { + + // Switching between GET (image) and POST (iframe). + + // We remove the imgDiv (really either an image or an iframe) + // from the frame and set it to null to make sure initImage + // will call createImage. + + if(this.imgDiv && this.imgDiv.parentNode === this.frame) { + this.frame.removeChild(this.imgDiv); + } + this.imgDiv = null; + + // And if we had an iframe we also remove the event pane. + + if(fromIFrame) { + this.frame.removeChild(this.frame.firstChild); + } } } - if (!this.useIFrame) { - OpenLayers.Tile.Image.prototype.updateBackBuffer.apply(this, arguments); - } + return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments); }, - + /** * Method: createImage * Creates the content for the frame on the tile. @@ -183,6 +192,22 @@ OpenLayers.Tile.Image.IFrame = { } else { OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments); } - } + }, + /** + * Method: createBackBuffer + * Override createBackBuffer to do nothing when we use an iframe. Moving an + * iframe from one element to another makes it necessary to reload the iframe + * because its content is lost. So we just give up. + * + * Returns: + * {DOMElement} + */ + createBackBuffer: function() { + var backBuffer; + if(!this.useIFrame) { + backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this); + } + return backBuffer; + } }; diff --git a/tests/Layer/Grid.html b/tests/Layer/Grid.html index fd8afb6e9d..afeb1ec850 100644 --- a/tests/Layer/Grid.html +++ b/tests/Layer/Grid.html @@ -49,7 +49,7 @@ } function test_Layer_Grid_clearTiles (t) { - t.plan(3); + t.plan(4); var map = new OpenLayers.Map('map'); layer = new OpenLayers.Layer.WMS(name, url, params); @@ -85,6 +85,7 @@ t.ok( layer.grid != null, "layer.grid does not get nullified" ); t.eq(tilesDeleted, numTiles, "all tiles destroy()ed properly"); t.ok(allTilesUnhooked, "all tiles unhooked before being destroyed"); + t.eq(layer.gridResolution, null, "gridResolution set to null"); OpenLayers.Tile.Image.prototype.destroy = OpenLayers.Tile.Image.prototype._destroy; @@ -167,6 +168,7 @@ var map = new OpenLayers.Map('map'); layer = new OpenLayers.Layer.WMS(name, url, params); layer.destroy = function() {}; //we're going to do funky things with the grid + layer.applyBackBuffer = function() {}; // backbuffering isn't under test here map.addLayer(layer); //make sure null bounds doesnt cause script error. @@ -390,6 +392,8 @@ getLayerPxFromLonLat: function(ul) { t.ok(ul.equals(desiredUL), "correct ul passed to translation"); return translatedPX; + }, + getResolution: function() { } } @@ -482,6 +486,7 @@ g_events = []; + layer.grid = [[{}]]; // to prevent error in updateBackBuffer tile.onLoadEnd.apply(layer); t.eq(g_events[0], "tileloaded", "tileloaded triggered when numLoadingTiles is 0"); t.eq(g_events[1], "loadend", "loadend event triggered when numLoadingTiles is 0"); @@ -586,7 +591,7 @@ function test_Layer_Grid_destroy (t) { - t.plan( 8 ); + t.plan( 9 ); var map = new OpenLayers.Map('map'); layer = new OpenLayers.Layer.Grid(name, url, params); @@ -603,16 +608,20 @@ map.setCenter(new OpenLayers.LonLat(0,0), 10); map.setCenter(new OpenLayers.LonLat(1,1)); - //grab a reference to one of the tiles var tile = layer.grid[1][1]; t.eq( tile.imgDiv.className, "olTileImage", "Tile has an image" ); + var removeBackBufferCalled = false; + layer.removeBackBuffer = function() { + removeBackBufferCalled = true; + }; + layer.destroy(); t.eq( tile.imgDiv, null, "Tile destroyed" ); t.eq( layer.timerId, null, "Tile loading timeout cleared"); - t.ok( layer.grid == null, "tiles appropriately destroyed") + t.ok( removeBackBufferCalled, "destroy calls removeBackBuffer"); // destroy after remove from map layer = new OpenLayers.Layer.WMS(name, url, params); @@ -759,6 +768,92 @@ map.destroy(); } + function test_moveTo_backbuffer_singletile(t) { + t.plan(4); + + var map = new OpenLayers.Map('map', { + resolutions: [1, 0.5, 0.025] + }); + var resolution; + var layer = new OpenLayers.Layer.WMS('', '', {}, { + singleTile: true, + isBaseLayer: true, + transitionEffect: 'resize', + applyBackBuffer: function(res) { + resolution = res; + } + }); + map.addLayer(layer); + + // initial resolution is 0.025 + resolution = undefined; + map.setCenter(new OpenLayers.LonLat(0, 0), 2); + t.eq(resolution, 0.025, + 'applyBackBuffer not called on first moveTo'); + + // move to (-90, 45) + resolution = undefined; + map.setCenter(new OpenLayers.LonLat(-90, 45)); + t.eq(resolution, 0.025, + 'applyBackBuffer called when map is moved'); + + // change to resolution 1 + resolution = undefined; + map.zoomTo(0); + t.eq(resolution, 1, + 'applyBackBuffer called when map is zoomed out'); + + // change to resolution 0.5 + resolution = undefined; + map.zoomTo(1); + t.eq(resolution, 0.5, + 'applyBackBuffer called when map is zoomed out'); + + map.destroy(); + } + + function test_moveTo_backbuffer(t) { + t.plan(4); + + var map = new OpenLayers.Map('map', { + resolutions: [1, 0.5, 0.025] + }); + var resolution; + var layer = new OpenLayers.Layer.WMS('', '', {}, { + isBaseLayer: true, + transitionEffect: 'resize', + applyBackBuffer: function(res) { + resolution = res; + } + }); + map.addLayer(layer); + + // initial resolution is 0.025 + resolution = undefined; + map.setCenter(new OpenLayers.LonLat(0, 0), 2); + t.eq(resolution, 0.025, + 'applyBackBuffer not called on first moveTo'); + + // move to (-90, 45) + resolution = undefined; + map.setCenter(new OpenLayers.LonLat(-90, 45)); + t.eq(resolution, undefined, + 'applyBackBuffer not called when map is moved'); + + // change to resolution 1 + resolution = undefined; + map.zoomTo(0); + t.eq(resolution, 1, + 'applyBackBuffer called when map is zoomed out'); + + // change to resolution 0.5 + map.zoomTo(1); + t.eq(resolution, 0.5, + 'applyBackBuffer called when map is zoomed out'); + + map.destroy(); + } + function test_transformDiv(t) { t.plan(8); @@ -814,6 +909,273 @@ map.destroy(); } + function test_applyBackBuffer(t) { + t.plan(13); + + var map = new OpenLayers.Map('map2'); + var layer = new OpenLayers.Layer.WMS('', '', {}, { + isBaseLayer: true + }); + map.addLayer(layer); + map.zoomToMaxExtent(); + + var backBuffer; + + // test #1 + layer.createBackBuffer = function() { + return; + }; + layer.applyBackBuffer(2); + t.eq(layer.backBuffer, undefined, + 'back buffer not created if createBackBuffer returns undefined'); + + // test #2 + layer.createBackBuffer = function() { + backBuffer = document.createElement('div'); + return backBuffer; + }; + layer.gridResolution = 32; + layer.grid[0][0].bounds = new OpenLayers.Bounds(0, 1, 1, 0); + layer.applyBackBuffer(2); + t.ok(layer.backBuffer === backBuffer, + 'back buffer set in layer'); + t.ok(layer.div.firstChild === backBuffer, + 'back buffer inserted as first child'); + t.eq(layer.backBuffer.style.width, '1600%', + 'back buffer has correct width'); + t.eq(layer.backBuffer.style.height, '1600%', + 'back buffer has correct height'); + t.eq(layer.backBuffer.style.left, '250%', + 'back buffer has correct left'); + t.eq(layer.backBuffer.style.top, '275%', + 'back buffer has correct top'); + + // test #3 + layer.createBackBuffer = function() { + backBuffer = document.createElement('div'); + return backBuffer; + }; + layer.gridResolution = 32; + layer.grid[0][0].bounds = new OpenLayers.Bounds(0, 1, 1, 0); + map.layerContainerDiv.style.left = '20px'; + map.layerContainerDiv.style.top = '-20px'; + layer.applyBackBuffer(2); + t.ok(layer.backBuffer === backBuffer, + 'back buffer set in layer'); + t.ok(layer.div.firstChild === backBuffer, + 'back buffer inserted as first child'); + t.eq(layer.backBuffer.style.width, '1600%', + 'back buffer has correct width'); + t.eq(layer.backBuffer.style.height, '1600%', + 'back buffer has correct height'); + t.eq(layer.backBuffer.style.left, '230%', + 'back buffer has correct left'); + t.eq(layer.backBuffer.style.top, '295%', + 'back buffer has correct top'); + + map.destroy(); + } + + function test_createBackBuffer(t) { + t.plan(7); + + var map = new OpenLayers.Map('map'); + var layer = new OpenLayers.Layer.WMS('', '', {}, { + isBaseLayer: true + }); + map.addLayer(layer); + map.zoomToMaxExtent(); + + var createBackBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer; + + var backBuffer; + + OpenLayers.Tile.Image.prototype.createBackBuffer = function() { + return; + }; + backBuffer = layer.createBackBuffer(); + t.ok(backBuffer != undefined, + 'createBackBuffer returns a back buffer'); + t.eq(backBuffer.childNodes.length, 0, + 'returned back buffer has no child nodes'); + + OpenLayers.Tile.Image.prototype.createBackBuffer = function() { + return document.createElement('div'); + }; + backBuffer = layer.createBackBuffer(); + t.ok(backBuffer != undefined, + 'createBackBuffer returns a back buffer'); + t.eq(backBuffer.childNodes[0].style.left, '0%', + 'first tile has correct left'); + t.eq(backBuffer.childNodes[0].style.top, '0%', + 'first tile has correct top'); + t.eq(backBuffer.childNodes[1].style.left, '256%', + 'second tile has correct left'); + t.eq(backBuffer.childNodes[1].style.top, '0%', + 'second tile has correct top'); + + map.destroy(); + OpenLayers.Tile.Image.prototype.createBackBuffer = createBackBuffer; + } + + function test_removeBackBuffer(t) { + t.plan(3); + + var map = new OpenLayers.Map('map'); + var layer = new OpenLayers.Layer.WMS('', '', {}, {isBaseLayer: true}); + map.addLayer(layer); + + // add a fake back buffer + var backBuffer = document.createElement('div'); + layer.backBuffer = backBuffer; + layer.div.appendChild(backBuffer); + layer.backBufferResolution = 32; + + layer.removeBackBuffer(); + t.eq(layer.backBuffer, null, 'backBuffer set to null in layer'); + t.eq(layer.backBufferResolution, null, + 'backBufferResolution set to null in layer'); + t.ok(backBuffer.parentNode !== layer.div, + 'back buffer removed from layer'); + + map.destroy(); + } + + function test_singleTile_move_and_zoom(t) { + + // + // In single tile mode with no transition effect, we insert a non-scaled + // backbuffer when the layer is moved. But if a zoom occurs right after + // a move, i.e. before the new image is received, we need to remove the + // backbuffer, or an ill-positioned image will be visible during the + // zoom transition. + // + + t.plan(2); + + var map = new OpenLayers.Map('map'); + var layer = new OpenLayers.Layer.WMS('', '', {}, { + isBaseLayer: true, + singleTile: true, + ratio: 1 + }); + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0, 0), 0); + + // move + map.setCenter(new OpenLayers.LonLat(10, 10)); + t.ok(layer.backBuffer && layer.backBuffer.parentNode === layer.div, + 'backbuffer inserted after map move'); + + // zoom + map.zoomTo(1); + t.eq(layer.backBuffer, null, + 'back buffer removed when zooming'); + + map.destroy(); + } + + function test_backbuffer_scaled_layer(t) { + t.plan(12); + + // + // set up + // + + var map = new OpenLayers.Map('map', { + resolutions: [32, 16, 8, 4, 2, 1] + }); + var layer = new OpenLayers.Layer.WMS( + "WMS", + window.location.href + "#", + null, + {transitionEffect: "resize"} + ); + + layer.serverResolutions = [32, 16, 8]; + + map.addLayer(layer); + map.setCenter(new OpenLayers.LonLat(0, 0), 2); + + layer.createBackBuffer = function() { + return document.createElement('div'); + }; + + // we want to control when the back buffer is removed + var removeBackBuffer = OpenLayers.Function.bind( + layer.removeBackBuffer, layer); + layer.removeBackBuffer = function() {}; + + // + // test + // + + // change resolution from 8 to 4 + map.zoomTo(3); + t.eq(layer.backBuffer.style.width, '100%', + '[8->4] back buffer not scaled'); + removeBackBuffer(); + + // change resolution from 8 to 2 + map.zoomTo(2); removeBackBuffer(); map.zoomTo(4); + t.eq(layer.backBuffer.style.width, '100%', + '[8->2] back buffer not scaled'); + removeBackBuffer(); + + // change resolution from 16 to 4 + map.zoomTo(1); removeBackBuffer(); map.zoomTo(3); + t.eq(layer.backBuffer.style.width, '200%', + '[16->4] back buffer width is as expected'); + t.eq(layer.backBuffer.style.width, '200%', + '[16->4] back buffer height is as expected'); + removeBackBuffer(); + + // change resolution from 32 to 1 + map.zoomTo(0); removeBackBuffer(); map.zoomTo(5); + t.eq(layer.backBuffer.style.width, '400%', + '[32->1] back buffer width is as expected'); + t.eq(layer.backBuffer.style.width, '400%', + '[32->1] back buffer height is as expected'); + removeBackBuffer(); + + // change resolution from 4 to 2 + map.zoomTo(3); removeBackBuffer(); map.zoomTo(4); + t.eq(layer.backBuffer.style.width, '100%', + '[4->2] back buffer not scaled'); + removeBackBuffer(); + + // change resolution from 4 to 1 + map.zoomTo(3); removeBackBuffer(); map.zoomTo(5); + t.eq(layer.backBuffer.style.width, '100%', + '[4->1] back buffer not scaled'); + removeBackBuffer(); + + // change resolution from 1 to 4 + map.zoomTo(5); removeBackBuffer(); map.zoomTo(3); + t.eq(layer.backBuffer.style.width, '100%', + '[1->4] back buffer not scaled'); + removeBackBuffer(); + + // change resolution from 4 to 8 + map.zoomTo(3); removeBackBuffer(); map.zoomTo(2); + t.eq(layer.backBuffer.style.width, '100%', + '[4->8] back buffer not scaled'); + removeBackBuffer(); + + // change resolution from 4 to 16 + map.zoomTo(3); removeBackBuffer(); map.zoomTo(1); + t.eq(layer.backBuffer.style.width, '50%', + '[4->16] back buffer width is as expected'); + t.eq(layer.backBuffer.style.width, '50%', + '[4->16] back buffer height is as expected'); + removeBackBuffer(); + + // + // tear down + // + + map.destroy(); + } diff --git a/tests/Tile/BackBufferable.html b/tests/Tile/BackBufferable.html deleted file mode 100644 index 97275891e7..0000000000 --- a/tests/Tile/BackBufferable.html +++ /dev/null @@ -1,260 +0,0 @@ - - - - - - -
- - diff --git a/tests/Tile/Image.html b/tests/Tile/Image.html index 0bfa4ae2ec..5b84f14897 100644 --- a/tests/Tile/Image.html +++ b/tests/Tile/Image.html @@ -317,8 +317,8 @@ map.destroy(); } - function test_insertBackBuffer(t) { - t.plan(4); + function test_createBackBuffer(t) { + t.plan(3); var map = new OpenLayers.Map('map'); var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS", @@ -326,11 +326,12 @@ map.addLayer(layer); map.setCenter(new OpenLayers.LonLat(0,0), 5); var tile = layer.grid[0][0]; + tile.isLoading = false; var img = tile.imgDiv; - 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"); + var bb = tile.createBackBuffer(); + t.eq(bb.style.left, tile.frame.style.left, + "backbuffer has same left style as frame"); + t.ok(bb.firstChild === img, "image appended to bb"); t.ok(tile.imgDiv == null, "image reference removed from tile"); map.destroy(); } diff --git a/tests/Tile/Image/IFrame.html b/tests/Tile/Image/IFrame.html index ce57a31c6a..ab119ade38 100644 --- a/tests/Tile/Image/IFrame.html +++ b/tests/Tile/Image/IFrame.html @@ -15,7 +15,7 @@ var wmsUrl = "http://labs.metacarta.com/wms/vmap0?"; function test_Tile_Image_IFrame_create (t) { - t.plan( 5 ); + t.plan( 3 ); map = new OpenLayers.Map('map'); var bar = new Array(205).join("1234567890"); layer = new OpenLayers.Layer.WMS(name, wmsUrl, @@ -25,7 +25,6 @@ map.addLayer(layer); var tile = layer.addTile(bounds, position); - t.eq(tile.backBufferMode, 2, "backBufferMode is 2 after tile creation"); tile.draw(); t.eq(tile.imgDiv.nodeName.toLowerCase(), "iframe", "IFrame used for long URL"); @@ -33,7 +32,6 @@ layer.mergeNewParams({foo: null}); tile.draw(); t.eq(tile.imgDiv.nodeName.toLowerCase(), "img", "IMG used for short URL"); - t.eq(tile.backBufferMode, 2, "backBufferMode reset to 2"); tile.maxGetUrlLength = 0; tile.draw(); diff --git a/tests/list-tests.html b/tests/list-tests.html index b5d052093f..4983e0b4f8 100644 --- a/tests/list-tests.html +++ b/tests/list-tests.html @@ -224,7 +224,6 @@
  • Symbolizer/Text.html
  • Tile.html
  • Tile/Image.html
  • -
  • Tile/BackBufferable.html
  • Tile/Image/IFrame.html
  • Tile/WFS.html
  • Tween.html