From 921347a81abbd7e6bd7240cfea7c075b0b7cfbdc Mon Sep 17 00:00:00 2001 From: euzuro Date: Thu, 12 Jul 2007 22:42:34 +0000 Subject: [PATCH] patch for #831 - simplify the notion of untiled (now an option on grid layers called 'singleTile') and implementing loading events for gridded/untiled layers. thanks tim and chris for reviewing this beast. git-svn-id: http://svn.openlayers.org/trunk/openlayers@3725 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf --- examples/layerLoadMonitoring.html | 248 ++++++++++++++++++ examples/notile.html | 28 +- lib/OpenLayers/Layer/Grid.js | 361 +++++++++++++++++++------- lib/OpenLayers/Layer/KaMap.js | 4 +- lib/OpenLayers/Layer/WMS/Untiled.js | 279 ++------------------ lib/OpenLayers/Tile.js | 24 ++ lib/OpenLayers/Tile/Image.js | 26 ++ tests/Layer/test_Grid.html | 385 ++++++++++++++++++++++++++-- tests/Layer/test_KaMap.html | 2 +- tests/Layer/test_TMS.html | 2 +- tests/Tile/test_Image.html | 16 +- tests/test_Tile.html | 29 ++- 12 files changed, 1021 insertions(+), 383 deletions(-) create mode 100644 examples/layerLoadMonitoring.html diff --git a/examples/layerLoadMonitoring.html b/examples/layerLoadMonitoring.html new file mode 100644 index 0000000000..bcf10b63b3 --- /dev/null +++ b/examples/layerLoadMonitoring.html @@ -0,0 +1,248 @@ + + + + + + + + +
+
+
+ Events Log: +
+
+
+ +
+ + + + + + + + + + +
+
+
+ Events Log: +
+
+
+ +
+ + diff --git a/examples/notile.html b/examples/notile.html index 3bbaa44c92..ff34b52e7e 100644 --- a/examples/notile.html +++ b/examples/notile.html @@ -1,9 +1,9 @@ @@ -11,20 +11,30 @@

OpenLayers Example

-
+
+

The first layer is an old OpenLayers.Layer.WMS.Untiled layer, using + a default ratio value of 1.5. +

The second layer is an OpenLayers.Layer.WMS layer with singleTile set + to true, and with a ratio of 1. diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js index b1905ad73c..36aca8328f 100644 --- a/lib/OpenLayers/Layer/Grid.js +++ b/lib/OpenLayers/Layer/Grid.js @@ -29,12 +29,26 @@ OpenLayers.Layer.Grid.prototype = */ grid: null, + /** APIProperty: ratio + * {Float} Used only when in single-tile mode, this specifies the + * ratio of the size of the single tile to the size of the map. + */ + ratio: 1.5, + /** * APIProperty: buffer - * {Integer} + * {Integer} Used only when in gridded mode, this specifies the number of + * extra rows and colums of tiles which will surround the minimum + * grid tiles to cover the map. */ buffer: 2, + /** + * APIProperty: numLoadingTiles + * {Integer} How many tiles are still loading? + */ + numLoadingTiles: 0, + /** * Constructor: OpenLayers.Layer.Grid * Create a new grid layer @@ -44,10 +58,18 @@ OpenLayers.Layer.Grid.prototype = * url - {String} * params - {Object} * options - {Object} Hashtable of extra options to tag onto the layer - */ + */ initialize: function(name, url, params, options) { OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, arguments); + + //grid layers will trigger 'tileloaded' when each new tile is + // loaded, as a means of progress update to listeners. + // listeners can access 'numLoadingTiles' if they wish to keep track + // of the loading progress + // + this.events.addEventType("tileloaded"); + this.grid = new Array(); }, @@ -72,7 +94,9 @@ OpenLayers.Layer.Grid.prototype = for(var iRow=0; iRow < this.grid.length; iRow++) { var row = this.grid[iRow]; for(var iCol=0; iCol < row.length; iCol++) { - row[iCol].destroy(); + var tile = row[iCol]; + this.removeTileMonitoringHooks(tile); + tile.destroy(); } } this.grid = []; @@ -112,21 +136,6 @@ OpenLayers.Layer.Grid.prototype = return obj; }, - /** - * Method: setMap - * When the layer is added to a map, then we can ask the map for - * its default tile size - * - * Parameters: - * map - {} - */ - setMap: function(map) { - OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this, arguments); - if (this.tileSize == null) { - this.tileSize = this.map.getTileSize(); - } - }, - /** * Method: moveTo * This function is called whenever the map is moved. All the moving @@ -141,48 +150,78 @@ OpenLayers.Layer.Grid.prototype = moveTo:function(bounds, zoomChanged, dragging) { OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments); - if (bounds == null) { - bounds = this.map.getExtent(); - } + bounds = bounds || this.map.getExtent(); + if (bounds != null) { - if (!this.grid.length || zoomChanged - || !this.getGridBounds().containsBounds(bounds, true)) { - this._initTiles(); + + // if grid is empty or zoom has changed, we *must* re-tile + var forceReTile = !this.grid.length || zoomChanged; + + // total bounds of the tiles + var tilesBounds = this.getTilesBounds(); + + 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))) { + this.initSingleTile(bounds); + } } else { - var buffer = (this.buffer) ? this.buffer*1.5 : 1; - while (true) { - var tlLayer = this.grid[0][0].position; - var tlViewPort = - this.map.getViewPortPxFromLayerPx(tlLayer); - if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) { - this.shiftColumn(true); - } else if (tlViewPort.x < -this.tileSize.w * buffer) { - this.shiftColumn(false); - } else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) { - this.shiftRow(true); - } else if (tlViewPort.y < -this.tileSize.h * buffer) { - this.shiftRow(false); - } else { - break; - } - }; - if (this.buffer == 0) { - for (var r=0, rl=this.grid.length; r} + */ + setTileSize: function(size) { + if (this.singleTile) { + var size = this.map.getSize().clone(); + size.h = size.h * this.ratio; + size.w = size.w * this.ratio; + } + OpenLayers.Layer.HTTPRequest.prototype.setTileSize.apply(this, [size]); + }, + /** * Method: getGridBounds + * Deprecated. This function will be removed in 3.0. Please use + * getTilesBounds() instead. + * + * Return: + * {} A Bounds object representing the bounds of all the + * currently loaded tiles (including those partially or not at all seen + * onscreen) + */ + getGridBounds: function() { + var msg = "The getGridBounds() function is deprecated. It will be " + + "removed in 3.0. Please use getTilesBounds() instead."; + OpenLayers.Console.warn(msg); + return getTilesBounds(); + }, + + /** + * Method: getTilesBounds * Get the bounds of the grid * * Return: @@ -190,33 +229,80 @@ OpenLayers.Layer.Grid.prototype = * currently loaded tiles (including those partially or not at all seen * onscreen) */ - getGridBounds:function() { + getTilesBounds: function() { + var bounds = null; - var bottom = this.grid.length - 1; - var bottomLeftTile = this.grid[bottom][0]; - - var right = this.grid[0].length - 1; - var topRightTile = this.grid[0][right]; - - return new OpenLayers.Bounds(bottomLeftTile.bounds.left, - bottomLeftTile.bounds.bottom, - topRightTile.bounds.right, - topRightTile.bounds.top); + if (this.grid.length) { + var bottom = this.grid.length - 1; + var bottomLeftTile = this.grid[bottom][0]; + + var right = this.grid[0].length - 1; + var topRightTile = this.grid[0][right]; + + bounds = new OpenLayers.Bounds(bottomLeftTile.bounds.left, + bottomLeftTile.bounds.bottom, + topRightTile.bounds.right, + topRightTile.bounds.top); + + } + return bounds; }, /** - * Method: _initTiles - * Initialize the tiles + * Method: initSingleTile + * + * Parameters: + * bounds - {} */ - _initTiles:function() { + initSingleTile: function(bounds) { + + //determine new tile bounds + var center = bounds.getCenterLonLat(); + var tileWidth = bounds.getWidth() * this.ratio; + var tileHeight = bounds.getHeight() * this.ratio; + + var tileBounds = + new OpenLayers.Bounds(center.lon - (tileWidth/2), + center.lat - (tileHeight/2), + center.lon + (tileWidth/2), + center.lat + (tileHeight/2)); + + var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top); + var px = this.map.getLayerPxFromLonLat(ul); + + if (!this.grid.length) { + this.grid[0] = new Array(); + } + + var tile = this.grid[0][0]; + if (!tile) { + tile = this.addTile(tileBounds, px); + + this.addTileMonitoringHooks(tile); + tile.draw(); + this.grid[0][0] = tile; + } else { + tile.moveTo(tileBounds, px); + } + //remove all but our single tile + this.removeExcessTiles(1,1); + }, + + /** + * Method: initGriddedTiles + * + * Parameters: + * bounds - {} + */ + initGriddedTiles:function(bounds) { + // work out mininum number of rows and columns; this is the number of // tiles required to cover the viewport plus one for panning var viewSize = this.map.getSize(); var minRows = Math.ceil(viewSize.h/this.tileSize.h) + 1; var minCols = Math.ceil(viewSize.w/this.tileSize.w) + 1; - var bounds = this.map.getExtent(); var extent = this.map.getMaxExtent(); var resolution = this.map.getResolution(); var tilelon = resolution * this.tileSize.w; @@ -256,10 +342,11 @@ OpenLayers.Layer.Grid.prototype = var colidx = 0; do { - var tileBounds = new OpenLayers.Bounds(tileoffsetlon, - tileoffsetlat, - tileoffsetlon + tilelon, - tileoffsetlat + tilelat); + var tileBounds = + new OpenLayers.Bounds(tileoffsetlon, + tileoffsetlat, + tileoffsetlon + tilelon, + tileoffsetlat + tilelat); var x = tileoffsetx; x -= parseInt(this.map.layerContainerDiv.style.left); @@ -271,6 +358,7 @@ OpenLayers.Layer.Grid.prototype = var tile = row[colidx++]; if (!tile) { tile = this.addTile(tileBounds, px); + this.addTileMonitoringHooks(tile); row.push(tile); } else { tile.moveTo(tileBounds, px, false); @@ -286,23 +374,9 @@ OpenLayers.Layer.Grid.prototype = } while((tileoffsetlat >= bounds.bottom - tilelat * this.buffer) || rowidx < minRows) - // remove extra rows - while (this.grid.length > rowidx) { - var row = this.grid.pop(); - for (var i=0, l=row.length; i colidx) { - for (var i=0, l=this.grid.length; i} + */ + addTileMonitoringHooks: function(tile) { + + tile.onLoadStart = function() { + //if that was first tile then trigger a 'loadstart' on the layer + if (this.numLoadingTiles == 0) { + this.events.triggerEvent("loadstart"); + } + this.numLoadingTiles++; + }; + tile.events.register("loadstart", this, tile.onLoadStart); + + tile.onLoadEnd = function() { + this.numLoadingTiles--; + this.events.triggerEvent("tileloaded"); + //if that was the last tile, then trigger a 'loadend' on the layer + if (this.numLoadingTiles == 0) { + this.events.triggerEvent("loadend"); + } + }; + tile.events.register("loadend", this, tile.onLoadEnd); + }, + + /** + * Method: removeTileMonitoringHooks + * This function takes a tile as input and removes the tile hooks + * that were added in addTileMonitoringHooks() + * + * Parameters: + * tile - {} + */ + removeTileMonitoringHooks: function(tile) { + tile.events.unregister("loadstart", this, tile.onLoadStart); + tile.events.unregister("loadend", this, tile.onLoadEnd); + }, + + /** + * Method: moveGriddedTiles + * + * Parameters: + * bounds - {} + */ + moveGriddedTiles: function(bounds) { + var buffer = (this.buffer) ? this.buffer*1.5 : 1; + while (true) { + var tlLayer = this.grid[0][0].position; + var tlViewPort = + this.map.getViewPortPxFromLayerPx(tlLayer); + if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) { + this.shiftColumn(true); + } else if (tlViewPort.x < -this.tileSize.w * buffer) { + this.shiftColumn(false); + } else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) { + this.shiftRow(true); + } else if (tlViewPort.y < -this.tileSize.h * buffer) { + this.shiftRow(false); + } else { + break; + } + }; + if (this.buffer == 0) { + for (var r=0, rl=this.grid.length; r rows) { + var row = this.grid.pop(); + for (var i=0, l=row.length; i columns) { + for (var i=0, l=this.grid.length; i + * - */ OpenLayers.Layer.WMS.Untiled = OpenLayers.Class.create(); OpenLayers.Layer.WMS.Untiled.prototype = - OpenLayers.Class.inherit( OpenLayers.Layer.HTTPRequest, { - - /** - * Property: DEFAULT_PARAMS - * Hashtable of default parameter key/value pairs - */ - DEFAULT_PARAMS: { service: "WMS", - version: "1.1.1", - request: "GetMap", - styles: "", - exceptions: "application/vnd.ogc.se_inimage", - format: "image/jpeg" - }, - /** - * APIProperty: reproject - * Whether or not to use the base layer as a basis for 'staking' the corners - * of the image geographically. - */ - reproject: true, - - /** - * APIProperty: ratio - * {Float} the ratio of image/tile size to map size (this is the untiled - * buffer) - */ - ratio: 2, + OpenLayers.Class.inherit( OpenLayers.Layer.WMS, { /** - * Property: tile - * {} + * APIProperty: singleTile + * {singleTile} Always true for untiled. */ - tile: null, + singleTile: true, /** - * Property: doneLoading - * {Boolean} did the image finish loading before a new draw was initiated? + * Constructor: OpenLayers.Layer.WMS.Untiled + * + * Parameters: + * name - {String} + * url - {String} + * params - {Object} + * options - {Object} */ - doneLoading: false, - - /** - * Constructor: OpenLayers.Layer.WMS.Untiled - * - * Parameters: - * name - {String} - * url - {String} - * params - {Object} - * options - {Object} - */ initialize: function(name, url, params, options) { - var newArguments = new Array(); - //uppercase params - params = OpenLayers.Util.upperCaseObject(params); - newArguments.push(name, url, params, options); - OpenLayers.Layer.HTTPRequest.prototype.initialize.apply(this, - newArguments); - OpenLayers.Util.applyDefaults( - this.params, - OpenLayers.Util.upperCaseObject(this.DEFAULT_PARAMS) - ); - - //layer is transparent - if (this.params.TRANSPARENT && - this.params.TRANSPARENT.toString().toLowerCase() == "true") { - - // unless explicitly set in options, make layer an overlay - if ( (options == null) || (!options.isBaseLayer) ) { - this.isBaseLayer = false; - } - - // jpegs can never be transparent, so intelligently switch the - // format, depending on teh browser's capabilities - if (this.params.FORMAT == "image/jpeg") { - this.params.FORMAT = OpenLayers.Util.alphaHack() ? "image/gif" - : "image/png"; - } - } - + OpenLayers.Layer.WMS.prototype.initialize.apply(this, arguments); + + var msg = "The OpenLayers.Layer.WMS.Untiled class is deprecated and " + + "will be removed in 3.0. Instead, you should use the " + + "normal OpenLayers.Layer.WMS class, passing it the option " + + "'singleTile' as true."; + OpenLayers.Console.warn(msg); }, - - /** - * APIMethod: destroy - */ - destroy: function() { - if (this.tile) { - this.tile.destroy(); - this.tile = null; - } - OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments); - }, - - /** - * APIMethod: clone - * Makes an exact clone of this OpenLayers.Layer.WMS.Untiled - * - * Parameters: - * obj - {Object} - * - * Returns: - * {} - */ - clone: function (obj) { - - if (obj == null) { - obj = new OpenLayers.Layer.WMS.Untiled(this.name, - this.url, - this.params, - this.options); - } - - //get all additions from superclasses - obj = OpenLayers.Layer.HTTPRequest.prototype.clone.apply(this, [obj]); - - // copy/set any non-init, non-simple values here - - obj.tile = null; - - return obj; - }, - - - /** - * Method: setMap - * Once HTTPRequest has set the map, we can load the image div - * - * Parameters: - * map - {} - */ - setMap: function(map) { - OpenLayers.Layer.HTTPRequest.prototype.setMap.apply(this, arguments); - }, - - /** - * Method: setTileSize - * Set the tile size based on the map size. - */ - setTileSize: function() { - var tileSize = this.map.getSize(); - tileSize.w = tileSize.w * this.ratio; - tileSize.h = tileSize.h * this.ratio; - this.tileSize = tileSize; - }, - - /** - * Method: moveTo - * When it is not a dragging move (ie when done dragging) - * reload and recenter the div. - * - * Parameters: - * bounds - {} - * zoomChanged - {Boolean} - * dragging - {Boolean} - */ - moveTo:function(bounds, zoomChanged, dragging) { - if (!this.doneLoading) { - this.events.triggerEvent("loadcancel"); - this.doneLoading = true; - } - OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this,arguments); - - if (bounds == null) { - bounds = this.map.getExtent(); - } - - var firstRendering = (this.tile == null); - - //does the new bounds to which we need to move fall outside of the - // current tile's bounds? - var outOfBounds = (!firstRendering && - !this.tile.bounds.containsBounds(bounds)); - - if ( zoomChanged || firstRendering || (!dragging && outOfBounds) ) { - - //clear out the old tile - if (this.tile) { - this.tile.clear(); - } - - //determine new tile bounds - var center = bounds.getCenterLonLat(); - var tileWidth = bounds.getWidth() * this.ratio; - var tileHeight = bounds.getHeight() * this.ratio; - var tileBounds = - new OpenLayers.Bounds(center.lon - (tileWidth / 2), - center.lat - (tileHeight / 2), - center.lon + (tileWidth / 2), - center.lat + (tileHeight / 2)); - - //determine new tile size - this.setTileSize(); - - //formulate request url string - var url = this.getURL(tileBounds); - - //determine new position (upper left corner of new bounds) - var ul = new OpenLayers.LonLat(tileBounds.left, tileBounds.top); - var pos = this.map.getLayerPxFromLonLat(ul); - - if ( this.tile && !this.tile.size.equals(this.tileSize)) { - this.tile.destroy(); - this.tile = null; - } - - this.events.triggerEvent("loadstart"); - this.doneLoading = false; - if (!this.tile) { - this.tile = new OpenLayers.Tile.Image(this, pos, tileBounds, - url, this.tileSize); - this.tile.draw(); - var onload = function() { - this.doneLoading = true; - this.events.triggerEvent("loadend"); - } - OpenLayers.Event.observe(this.tile.imgDiv, 'load', - onload.bindAsEventListener(this)); - } else { - this.tile.moveTo(tileBounds, pos); - } - - } - }, - - /** - * Method: getURL - * - * Parameters: - * bounds - {} - */ - getURL: function(bounds) { - return this.getFullRequestString( {'BBOX': bounds.toBBOX(), - 'WIDTH': this.tileSize.w, - 'HEIGHT': this.tileSize.h} ); - }, - - - /** - * Method: setURL - * Once HTTPRequest has updated the url, reload the image div - * - * Parameters: - * newUrl - {String} - */ - setUrl: function(newUrl) { - OpenLayers.Layer.HTTPRequest.prototype.setUrl.apply(this, arguments); - this.redraw(); - }, - - /** - * APIMethod: getFullRequestString - * combine the layer's url with its params and these newParams. - * - * Add the SRS parameter from 'projection' -- this is probably - * more eloquently done via a setProjection() method, but this - * works for now and always. - * - * Parameters: - * newParams - {Object} - */ - getFullRequestString:function(newParams) { - var projection = this.map.getProjection(); - this.params.SRS = (projection == "none") ? null : projection; - - return OpenLayers.Layer.Grid.prototype.getFullRequestString.apply( - this, arguments); - }, /** @final @type String */ CLASS_NAME: "OpenLayers.Layer.WMS.Untiled" diff --git a/lib/OpenLayers/Tile.js b/lib/OpenLayers/Tile.js index 1dc9041c7f..2c9d1bb0e1 100644 --- a/lib/OpenLayers/Tile.js +++ b/lib/OpenLayers/Tile.js @@ -21,6 +21,19 @@ OpenLayers.Tile = OpenLayers.Class.create(); OpenLayers.Tile.prototype = { /** + * Constant: EVENT_TYPES + * {Array(String)} Supported application event types + */ + EVENT_TYPES: [ "loadstart", "loadend", "reload"], + + /** + * APIProperty: events + * {} An events object that handles all + * events on the tile. + */ + events: null, + + /** * Property: id * {String} null */ @@ -66,6 +79,12 @@ OpenLayers.Tile.prototype = { */ drawn: false, + /** + * Property: isLoading + * {Boolean} Is the tile loading? + */ + isLoading: false, + /** TBD 3.0 -- remove 'url' from the list of parameters to the constructor. * there is no need for the base tile class to have a url. * @@ -88,6 +107,8 @@ OpenLayers.Tile.prototype = { //give the tile a unique id based on its BBOX. this.id = OpenLayers.Util.createUniqueID("Tile_"); + + this.events = new OpenLayers.Events(this, null, this.EVENT_TYPES); }, /** @@ -99,6 +120,9 @@ OpenLayers.Tile.prototype = { this.bounds = null; this.size = null; this.position = null; + + this.events.destroy(); + this.events = null; }, /** diff --git a/lib/OpenLayers/Tile/Image.js b/lib/OpenLayers/Tile/Image.js index 4145c16e5c..fa5adc55a8 100644 --- a/lib/OpenLayers/Tile/Image.js +++ b/lib/OpenLayers/Tile/Image.js @@ -96,6 +96,15 @@ OpenLayers.Tile.Image.prototype = if (!OpenLayers.Tile.prototype.draw.apply(this, arguments)) { return false; } + + if (this.isLoading) { + //if we're already loading, send 'reload' instead of 'loadstart'. + this.events.triggerEvent("reload"); + } else { + this.isLoading = true; + this.events.triggerEvent("loadstart"); + } + if (this.imgDiv == null) { this.initImgDiv(); } @@ -202,6 +211,23 @@ OpenLayers.Tile.Image.prototype = // we need this reference to check back the viewRequestID this.imgDiv.map = this.layer.map; + //bind a listener to the onload of the image div so that we + // can register when a tile has finished loading. + var onload = function() { + + //normally isLoading should always be true here but there are some + // right funky conditions where loading and then reloading a tile + // with the same url *really*fast*. this check prevents sending + // a 'loadend' if the msg has already been sent + // + if (this.isLoading) { + this.isLoading = false; + this.events.triggerEvent("loadend"); + } + } + OpenLayers.Event.observe(this.imgDiv, 'load', + onload.bindAsEventListener(this)); + }, /** diff --git a/tests/Layer/test_Grid.html b/tests/Layer/test_Grid.html index 4856182990..91fcf67494 100644 --- a/tests/Layer/test_Grid.html +++ b/tests/Layer/test_Grid.html @@ -23,10 +23,15 @@ function test_01_Layer_Grid_constructor (t) { - t.plan( 1 ); + t.plan( 5 ); layer = new OpenLayers.Layer.Grid(name, url, params, null); t.ok( layer instanceof OpenLayers.Layer.Grid, "returns OpenLayers.Layer.Grid object" ); + t.eq( layer.buffer, 2, "buffer default is 2"); + t.eq( layer.ratio, 1.5, "ratio default is 1.5"); + t.eq( layer.numLoadingTiles, 0, "numLoadingTiles starts at 0"); + t.ok( layer.events.listeners["tileloaded"] != null, "'tileloaded' event added to layer's event types"); + } @@ -42,40 +47,80 @@ } function test_03_Layer_Grid_clearTiles (t) { - t.plan(1); + t.plan(3); + var map = new OpenLayers.Map('map'); layer = new OpenLayers.Layer.WMS(name, url, params); map.addLayer(layer); map.setCenter(new OpenLayers.LonLat(0,0)); + var numTiles = layer.grid.length * layer.grid[0].length; - //grab a reference to one of the tiles - var tile = layer.grid[0][0]; + //our count of how many times tile.destroy() is called + tilesDeleted = 0; + + //this will get set to false if we try to destroy a tile that has + // not been unhookedv + allTilesUnhooked = true; + + OpenLayers.Tile.Image.prototype._destroy = + OpenLayers.Tile.Image.prototype.destroy; + + OpenLayers.Tile.Image.prototype.destroy = function() { + if (!this.unhooked) { + allTilesUnhooked = false; + } + tilesDeleted++; + } + + layer.removeTileMonitoringHooks = function(tile) { + tile.unhooked = true; + } layer.clearGrid(); 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"); + + OpenLayers.Tile.Image.prototype.destroy = + OpenLayers.Tile.Image.prototype._destroy; + } - function test_04_Layer_Grid_getGridBounds(t) { - t.plan( 1 ); + function test_04_Layer_Grid_getTilesBounds(t) { + t.plan( 3 ); layer = new OpenLayers.Layer.WMS(name, url, params); + + //normal grid var bl = { bounds: new OpenLayers.Bounds(1,2,0,0)}; var tr = { bounds: new OpenLayers.Bounds(0,0,3,4)}; layer.grid = [ [6, tr], [bl, 7]]; - var bounds = layer.getGridBounds(); - + var bounds = layer.getTilesBounds(); var testBounds = new OpenLayers.Bounds(1,2,3,4); - t.ok( bounds.equals(testBounds), "getGridBounds() returns correct bounds") + t.ok( bounds.equals(testBounds), "getTilesBounds() returns correct bounds"); - layer.grid = null; + //no tiles + layer.grid = new Array(); + bounds = layer.getTilesBounds(); + + t.ok(bounds == null, "getTilesBounds() on a tile-less grid returns null"); + + + //singleTile + var singleTile = { bounds: new OpenLayers.Bounds(1,2,3,4)}; + layer.grid = [ [ singleTile ] ]; + bounds = layer.getTilesBounds(); + + t.ok( bounds.equals(testBounds), "getTilesBounds() returns correct bounds"); + } function test_05_Layer_Grid_getResolution(t) { @@ -108,16 +153,155 @@ t.eq( zoom, 2, "getZoomForExtent() returns correct value"); } + + function test_07_Layer_Grid_moveTo(t) { + t.plan(13); + + 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 + map.addLayer(layer); + + //make sure null bounds doesnt cause script error. + // no test necessary, just action + map.getExtent = function() { return null; } + layer.singleTile = false; + layer.moveTo(); //checks to make sure null bounds doesnt break us + + + + //observing globals + layer.initSingleTile = function(bounds) { + g_WhichFunc = "InitSingle"; + g_Bounds = bounds; + }; + layer.initGriddedTiles = function(bounds) { + g_WhichFunc = "InitGridded"; + g_Bounds = bounds; + }; + layer.moveGriddedTiles = function(bounds) { + g_WhichFunc = "MoveGridded"; + g_Bounds = bounds; + }; + var clearTestBounds = function() { + g_WhichFunc = null; + g_Bounds = null; + }; + + //default map extent (tested every time below) + b = new OpenLayers.Bounds(0,0,100,100); + map.getExtent = function() { + return b; + }; + var tilesBounds = null; + layer.getTilesBounds = function() { + return tilesBounds; + } + + +//FORCE + + //empty grid + layer.grid = new Array(); + //grid + clearTestBounds(); + layer.singleTile = false; + layer.moveTo() + t.ok(g_Bounds.equals(b), "if grid is empty, initGridded called"); + + //singletile + clearTestBounds(); + layer.singleTile = true; + layer.moveTo() + t.ok(g_Bounds.equals(b), "if grid is empty, initSingleTile called"); + + //zoomChanged + zoomChanged = true; + layer.grid = [ [ {} ] ]; + + //grid + clearTestBounds(); + layer.singleTile = false; + layer.moveTo(null, zoomChanged); + t.ok(g_Bounds.equals(b), "if layer has grid but zoomChanged is called, initGridded called"); + + //singletile + clearTestBounds(); + layer.singleTile = true; + layer.moveTo(null, zoomChanged); + t.ok(g_Bounds.equals(b), "if layer has grid but zoomChanged is called, initSingleTile called"); + + + layer.getTilesBounds = function() { + return tilesBounds; + } + + + +//NO FORCE + zoomChanged = false; + layer.grid = [ [ {} ] ]; + + //single tile + layer.singleTile = true; + + //DRAGGING + var dragging = true; + + //in bounds + clearTestBounds(); + tilesBounds = new OpenLayers.Bounds(-10,-10,110,110); + layer.moveTo(null, zoomChanged, dragging); + t.ok(g_Bounds == null, "if dragging and tile in bounds, no init()"); + + //out bounds + clearTestBounds(); + tilesBounds = new OpenLayers.Bounds(10,10,120,120); + layer.moveTo(null, zoomChanged, dragging); + t.ok(g_Bounds == null, "if dragging and tile out of bounds, no init()"); + + //NOT DRAGGING + dragging = false; + + //in bounds + clearTestBounds(); + tilesBounds = new OpenLayers.Bounds(-10,-10,110,110); + layer.moveTo(null, zoomChanged, dragging); + t.ok(g_Bounds == null, "if dragging and tile in bounds, no init()"); + + //out bounds + clearTestBounds(); + tilesBounds = new OpenLayers.Bounds(10,10,120,120); + layer.moveTo(null, zoomChanged, dragging); + t.ok(g_WhichFunc == "InitSingle", "if not dragging and tile out of bounds, we call initSingleTile()"); + t.ok(g_Bounds.equals(b), "if not dragging and tile out of bounds, we call initSingleTile() with correct bounds"); + + + //gridded + layer.grid = [ [ {} ] ]; + layer.singleTile = false; + + // drastic pan + clearTestBounds(); + tilesBounds = new OpenLayers.Bounds(-150,-150,-120,-120); + layer.moveTo(null, zoomChanged); + t.ok(g_WhichFunc == "InitGridded", "if tiles drastically out of bounds, we call initGriddedTile()"); + t.ok(g_Bounds.equals(b), "if tiles drastically out of bounds, we call initGriddedTile() with correct bounds"); + + //regular move + clearTestBounds(); + tilesBounds = new OpenLayers.Bounds(10,10,120,120); + layer.moveTo(null, zoomChanged); + t.ok(g_WhichFunc == "MoveGridded", "if tiles not drastically out of bounds, we call moveGriddedTile()"); + t.ok(g_Bounds.equals(b), "if tiles not drastically out of bounds, we call moveGriddedTile() with correct bounds"); + } /** THIS WOULD BE WHERE THE TESTS WOULD GO FOR * - * -moveTo * -insertColumn * -insertRow - - function 07_Layer_Grid_moveTo(t) { - } + * function 08_Layer_Grid_insertColumn(t) { } @@ -155,21 +339,176 @@ layer.grid = null; } - function test_11_Layer_Grid_setMap(t) { + function test_11_Layer_Grid_setTileSize(t) { + t.plan(1); + OpenLayers.Layer.HTTPRequest.prototype._setTileSize = + OpenLayers.Layer.HTTPRequest.prototype.setTileSize; + + OpenLayers.Layer.HTTPRequest.prototype.setTileSize = function(size) { + g_Size = size; + }; + + + layer = new OpenLayers.Layer.Grid(name, url, params, { + singleTile: true + }); + mapSize = new OpenLayers.Size(100,1000); + layer.map = { + getSize: function() { return mapSize; } + } + + g_Size = null; + layer.setTileSize(); + + var idealSize = new OpenLayers.Size(150,1500); + t.ok( g_Size && g_Size.equals(idealSize), "correctly calculated tile size passed to superclass setTileSize() function"); + + OpenLayers.Layer.HTTPRequest.prototype.setTileSize = + OpenLayers.Layer.HTTPRequest.prototype._setTileSize; + } + + function test_12_Layer_Grid_initSingleTile(t) { + t.plan( 11 ); + + layer = new OpenLayers.Layer.Grid(name, url, params, { + singleTile: true, + ratio: 2 + }); + + var bounds = new OpenLayers.Bounds(-10,10,50,100); + + var desiredTileBounds = new OpenLayers.Bounds(-40,-35,80,145); + var desiredUL = new OpenLayers.LonLat(-40,145); + + translatedPX = {}; + layer.map = { + getLayerPxFromLonLat: function(ul) { + t.ok(ul.equals(desiredUL), "correct ul passed to translation"); + return translatedPX; + } + } + + var newTile = { + draw: function() { + t.ok(true, "newly created tile has been drawn"); + } + }; + layer.addTile = function(tileBounds, px) { + t.ok(tileBounds.equals(desiredTileBounds), "correct tile bounds passed to addTile to create new tile"); + t.ok(px == translatedPX, "correct tile px passed to addTile to create new tile"); + return newTile; + }; + layer.addTileMonitoringHooks = function(tile) { + t.ok(tile == newTile, "adding monitoring hooks to the newly added tile"); + }; + layer.removeExcessTiles = function(x,y) { + t.ok(x == 1 && y == 1, "removeExcessTiles called") + }; + + + layer.grid = new Array(); + layer.initSingleTile(bounds); + + t.ok(layer.grid[0][0] == newTile, "grid's 0,0 is set to the newly created tile"); + + var tile = { + moveTo: function(tileBounds, px) { + t.ok(tileBounds.equals(desiredTileBounds), "correct tile bounds passed to tile.moveTo()"); + t.ok(px == translatedPX, "correct tile px passed to tile.moveTo()"); + } + }; + layer.grid = [[ tile ]]; + layer.initSingleTile(bounds); + + } + + function test_14_Layer_Grid_addTileMonitoringHooks(t) { + t.plan(14); + + layer = new OpenLayers.Layer.Grid(); + layer.events = { + 'triggerEvent': function(str) { + g_events.push(str); + } + } + + var tile = { + events: { + register: function(name, obj, func) { + g_registered[name] = [obj, func]; + } + } + } + + g_registered = {}; + g_events = new Array(); + + layer.addTileMonitoringHooks(tile); + + //loadstart + t.ok(tile.onLoadStart != null, "onLoadStart function created and added to tile"); + entry = g_registered["loadstart"]; + t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadStart, "loadstart correctly registered"); + + layer.numLoadingTiles = 0; + g_events = new Array(); + tile.onLoadStart.apply(layer); + + t.eq(g_events[0], "loadstart", "loadstart event triggered when numLoadingTiles is 0"); + t.eq(layer.numLoadingTiles, 1, "numLoadingTiles incremented"); + + g_events = new Array(); + tile.onLoadStart.apply(layer); + t.eq(g_events.length, 0, "loadstart event not triggered when numLoadingTiles is not 0"); + t.eq(layer.numLoadingTiles, 2, "numLoadingTiles incremented"); + + + //loadend + t.ok(tile.onLoadEnd != null, "onLoadEnd function created and added to tile"); + entry = g_registered["loadend"]; + t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadEnd, "loadend correctly registered"); + + layer.numLoadingTiles = 2; + g_events = new Array(); + tile.onLoadEnd.apply(layer); + t.eq(g_events[0], "tileloaded", "tileloaded triggered when numLoadingTiles is > 0"); + t.eq(g_events.length, 1, "loadend event not triggered when numLoadingTiles is > 0"); + t.eq(layer.numLoadingTiles, 1, "numLoadingTiles decremented"); + + + g_events = new Array(); + 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"); + t.eq(layer.numLoadingTiles, 0, "numLoadingTiles decremented"); + } + + function test_15_Layer_Grid_removeTileMonitoringHooks(t) { t.plan(2); - var options = {tileSize: new OpenLayers.Size(500,50)}; - var map = new OpenLayers.Map('map', options); - layer = new OpenLayers.Layer.Grid(name, url, params); + layer = new OpenLayers.Layer.Grid(); + + var tile = { + onLoadStart: {}, + onLoadEnd: {}, + events: { + unregister: function(name, obj, func) { + g_unregistered[name] = [obj, func]; + } + } + } - - layer.setMap(map); + g_unregistered = {}; - t.ok( layer.tileSize != null, "tileSize has been set"); - t.ok( (layer.tileSize.h == 50) && (layer.tileSize.w == 500), "tileSize has been set correctly"); - } + layer.removeTileMonitoringHooks(tile); + + entry = g_unregistered["loadstart"]; + t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadStart, "loadstart correctly unregistered"); + entry = g_unregistered["loadend"]; + t.ok( entry && entry[0] == layer && entry[1] == tile.onLoadEnd, "loadend correctly unregistered"); + } function test_99_Layer_Grid_destroy (t) { diff --git a/tests/Layer/test_KaMap.html b/tests/Layer/test_KaMap.html index de11585e9f..47623df5f3 100644 --- a/tests/Layer/test_KaMap.html +++ b/tests/Layer/test_KaMap.html @@ -62,7 +62,7 @@ layer.grid = [ [6, tr], [bl, 7]]; - var bounds = layer.getGridBounds(); + var bounds = layer.getTilesBounds(); var testBounds = new OpenLayers.Bounds(1,2,3,4); diff --git a/tests/Layer/test_TMS.html b/tests/Layer/test_TMS.html index 134d89e7a2..9e1ae61900 100644 --- a/tests/Layer/test_TMS.html +++ b/tests/Layer/test_TMS.html @@ -46,7 +46,7 @@ layer.grid = [ [6, tr], [bl, 7]]; - var bounds = layer.getGridBounds(); + var bounds = layer.getTilesBounds(); var testBounds = new OpenLayers.Bounds(1,2,3,4); diff --git a/tests/Tile/test_Image.html b/tests/Tile/test_Image.html index 32b0a3b4e3..dd761beae4 100644 --- a/tests/Tile/test_Image.html +++ b/tests/Tile/test_Image.html @@ -25,7 +25,7 @@ t.ok( tile.size.equals(size), "tile.size is set correctly"); } function test_02_Tile_Image_draw (t) { - t.plan( 5 ); + t.plan( 7 ); var map = new OpenLayers.Map('map'); @@ -40,7 +40,16 @@ var url = "http://www.openlayers.org/dev/tests/tileimage"; tile = new OpenLayers.Tile.Image(layer, position, bounds, url, size); + tile.events.register("loadstart", this, function() { + t.ok(true, "loadstart triggered"); + }); + tile.events.register("reload", this, function() { + t.ok(true, "reload triggered"); + }); + + //this should trigger a "loadstart" event tile.draw(); + var img = tile.imgDiv; if (!isMozilla) @@ -63,6 +72,11 @@ "tile.draw creates an image"); t.eq( tile.imgDiv.style.width, "5px", "Image width is correct" ); t.eq( tile.imgDiv.style.height, "6px", "Image height is correct" ); + + // this should trigger a "reload" event (since the image never actually + // loads in tests) + tile.draw(); + } function test_03_Tile_Image_OutsideMaxExtent(t) { t.plan( 11 ); diff --git a/tests/test_Tile.html b/tests/test_Tile.html index 180e5601c9..23778d44db 100644 --- a/tests/test_Tile.html +++ b/tests/test_Tile.html @@ -5,7 +5,7 @@ var tile; function test_01_Tile_constructor (t) { - t.plan( 8 ); + t.plan( 9 ); var layer = new Object(); // bogus layer var position = new OpenLayers.Pixel(10,20); @@ -24,6 +24,33 @@ t.ok( tile.id != null, "tile is given an id"); t.ok( tile.id.startsWith("Tile_"), "tile's id starts correctly"); + t.ok( tile.events != null, "tile's events intitialized"); + } + + function test_99_Tile_destroy(t) { + t.plan( 6 ); + + + var layer = new Object(); // bogus layer + var position = new OpenLayers.Pixel(10,20); + var bounds = new OpenLayers.Bounds(1,2,3,4); + var url = "bobob"; + var size = new OpenLayers.Size(5,6); + + tile = new OpenLayers.Tile(layer, position, bounds, url, size); + tile.events.destroy = function() { + t.ok(true, "tile events destroy() called"); + } + + + tile.destroy(); + + t.ok(tile.layer == null, "tile.layer set to null"); + t.ok(tile.bounds == null, "tile.bounds set to null"); + t.ok(tile.size == null, "tile.size set to null"); + t.ok(tile.position == null, "tile.position set to null"); + + t.ok(tile.events == null, "tile.events set to null"); }