diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 02e5e904ff..3917e7f3e0 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -129,30 +129,8 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = var tilesToDrawByZ = {}; tilesToDrawByZ[z] = {}; - var findInterimTiles = function(z, tileRange) { - // FIXME this could be more efficient about filling partial holes - var fullyCovered = true; - var tile, tileCoord, tileCoordKey, x, y; - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoord = new ol.TileCoord(z, x, y); - tileCoordKey = tileCoord.toString(); - if (tilesToDrawByZ[z] && tilesToDrawByZ[z][tileCoordKey]) { - return; - } - tile = tileSource.getTile(tileCoord); - if (!goog.isNull(tile) && tile.getState() == ol.TileState.LOADED) { - if (!tilesToDrawByZ[z]) { - tilesToDrawByZ[z] = {}; - } - tilesToDrawByZ[z][tileCoordKey] = tile; - } else { - fullyCovered = false; - } - } - } - return fullyCovered; - }; + var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource, + tilesToDrawByZ); var allTilesLoaded = true; var tile, tileCenter, tileCoord, tileState, x, y; @@ -180,7 +158,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = } allTilesLoaded = false; - tileGrid.forEachTileCoordParentTileRange(tileCoord, findInterimTiles); + tileGrid.forEachTileCoordParentTileRange(tileCoord, findLoadedTiles); } } diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 094d6a3009..d6521d9386 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -93,30 +93,8 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = var tilesToDrawByZ = {}; tilesToDrawByZ[z] = {}; - var findInterimTiles = function(z, tileRange) { - // FIXME this could be more efficient about filling partial holes - var fullyCovered = true; - var tile, tileCoord, tileCoordKey, x, y; - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoord = new ol.TileCoord(z, x, y); - tileCoordKey = tileCoord.toString(); - if (tilesToDrawByZ[z] && tilesToDrawByZ[z][tileCoordKey]) { - return; - } - tile = tileSource.getTile(tileCoord); - if (!goog.isNull(tile) && tile.getState() == ol.TileState.LOADED) { - if (!tilesToDrawByZ[z]) { - tilesToDrawByZ[z] = {}; - } - tilesToDrawByZ[z][tileCoordKey] = tile; - } else { - fullyCovered = false; - } - } - } - return fullyCovered; - }; + var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource, + tilesToDrawByZ); var allTilesLoaded = true; var tile, tileCenter, tileCoord, tileState, x, y; @@ -144,7 +122,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = } allTilesLoaded = false; - tileGrid.forEachTileCoordParentTileRange(tileCoord, findInterimTiles); + tileGrid.forEachTileCoordParentTileRange(tileCoord, findLoadedTiles); } diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 360f8f8d82..fe29c6cf00 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -365,32 +365,8 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = var tilesToDrawByZ = {}; tilesToDrawByZ[z] = {}; - var findInterimTiles = function(z, tileRange) { - // FIXME this could be more efficient about filling partial holes - var fullyCovered = true; - var tile, tileCoord, tileCoordKey, x, y; - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - tileCoord = new ol.TileCoord(z, x, y); - tileCoordKey = tileCoord.toString(); - if (tilesToDrawByZ[z] && tilesToDrawByZ[z][tileCoordKey]) { - return; - } - tile = tileSource.getTile(tileCoord); - if (!goog.isNull(tile) && - tile.getState() == ol.TileState.LOADED && - mapRenderer.isTileTextureLoaded(tile)) { - if (!tilesToDrawByZ[z]) { - tilesToDrawByZ[z] = {}; - } - tilesToDrawByZ[z][tileCoordKey] = tile; - } else { - fullyCovered = false; - } - } - } - return fullyCovered; - }; + var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource, + tilesToDrawByZ); var tilesToLoad = new goog.structs.PriorityQueue(); @@ -428,7 +404,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } allTilesLoaded = false; - tileGrid.forEachTileCoordParentTileRange(tileCoord, findInterimTiles); + tileGrid.forEachTileCoordParentTileRange(tileCoord, findLoadedTiles); } diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index 93e083eef8..bb7d3b0d96 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -8,6 +8,7 @@ goog.require('ol.Projection'); goog.require('ol.Tile'); goog.require('ol.TileCoord'); goog.require('ol.TileRange'); +goog.require('ol.TileState'); goog.require('ol.source.Source'); goog.require('ol.tilegrid.TileGrid'); @@ -58,6 +59,44 @@ ol.source.TileSource.prototype.canExpireCache = goog.functions.FALSE; ol.source.TileSource.prototype.expireCache = goog.abstractMethod; +/** + * Look for loaded tiles over a given tile range and zoom level. Adds + * properties to the provided lookup representing key/tile pairs for already + * loaded tiles. + * + * @param {Object.>} loadedTilesByZ A lookup of + * loaded tiles by zoom level. + * @param {number} z Zoom level. + * @param {ol.TileRange} tileRange Tile range. + * @return {boolean} The tile range is fully covered with loaded tiles. + */ +ol.source.TileSource.prototype.findLoadedTiles = function(loadedTilesByZ, z, + tileRange) { + // FIXME this could be more efficient about filling partial holes + var fullyCovered = true; + var tile, tileCoord, tileCoordKey, x, y; + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + tileCoord = new ol.TileCoord(z, x, y); + tileCoordKey = tileCoord.toString(); + if (loadedTilesByZ[z] && loadedTilesByZ[z][tileCoordKey]) { + return false; // TODO: fix this + } + tile = this.getTile(tileCoord); + if (!goog.isNull(tile) && tile.getState() == ol.TileState.LOADED) { + if (!loadedTilesByZ[z]) { + loadedTilesByZ[z] = {}; + } + loadedTilesByZ[z][tileCoordKey] = tile; + } else { + fullyCovered = false; + } + } + } + return fullyCovered; +}; + + /** * @inheritDoc */ diff --git a/test/spec/ol/source/tilesource.test.js b/test/spec/ol/source/tilesource.test.js new file mode 100644 index 0000000000..9d51c59b04 --- /dev/null +++ b/test/spec/ol/source/tilesource.test.js @@ -0,0 +1,163 @@ +goog.provide('ol.test.source.TileSource'); + +describe('ol.source.TileSource', function() { + + describe('constructor', function() { + it('returns a tile source', function() { + var source = new ol.source.TileSource({ + projection: ol.Projection.getFromCode('EPSG:4326') + }); + expect(source).toBeA(ol.source.Source); + expect(source).toBeA(ol.source.TileSource); + }); + }); + + describe('#findLoadedTiles()', function() { + + it('adds no tiles if none are already loaded', function() { + // a source with no loaded tiles + var source = new ol.test.source.MockTileSource({}); + + var loadedTilesByZ = {}; + var grid = source.getTileGrid(); + var range = grid.getTileRangeForExtentAndZ(source.getExtent(), 3); + source.findLoadedTiles(loadedTilesByZ, 3, range); + + var keys = goog.object.getKeys(loadedTilesByZ); + expect(keys.length).toBe(0); + }); + + it('adds loaded tiles to the lookup (z: 0)', function() { + // a source with no loaded tiles + var source = new ol.test.source.MockTileSource({ + '0/0/-1': true, + '1/0/-1': true + }); + + var loadedTilesByZ = {}; + var grid = source.getTileGrid(); + var range = grid.getTileRangeForExtentAndZ(source.getExtent(), 0); + source.findLoadedTiles(loadedTilesByZ, 0, range); + var keys = goog.object.getKeys(loadedTilesByZ); + expect(keys.length).toBe(1); + var tile = loadedTilesByZ['0']['0/0/-1']; + expect(tile).toBeA(ol.Tile); + expect(tile.state).toBe(ol.TileState.LOADED); + }); + + it('adds loaded tiles to the lookup (z: 1)', function() { + // a source with no loaded tiles + var source = new ol.test.source.MockTileSource({ + '0/0/-1': true, + '1/0/-1': true + }); + + var loadedTilesByZ = {}; + var grid = source.getTileGrid(); + var range = grid.getTileRangeForExtentAndZ(source.getExtent(), 1); + source.findLoadedTiles(loadedTilesByZ, 1, range); + var keys = goog.object.getKeys(loadedTilesByZ); + expect(keys.length).toBe(1); + var tile = loadedTilesByZ['1']['1/0/-1']; + expect(tile).toBeA(ol.Tile); + expect(tile.state).toBe(ol.TileState.LOADED); + }); + + }); + +}); + +/** + * Tile source for tests that uses a EPSG:4326 based grid with 4 resolutions and + * 256x256 tiles. + * + * @constructor + * @extends {ol.source.TileSource} + * @param {Object.} loaded Lookup of already loaded tiles. + */ +ol.test.source.MockTileSource = function(loaded) { + var extent = new ol.Extent(-180, -180, 180, 180); + var tileGrid = new ol.tilegrid.TileGrid({ + resolutions: [360 / 256, 180 / 256, 90 / 256, 45 / 256], + extent: extent, + origin: new ol.Coordinate(-180, 180), + tileSize: new ol.Size(256, 256) + }); + + goog.base(this, { + extent: extent, + projection: ol.Projection.getFromCode('EPSG:4326'), + tileGrid: tileGrid + }); + + /** + * @type {Object.} + * @private + */ + this.loaded_ = loaded; + +}; +goog.inherits(ol.test.source.MockTileSource, ol.source.TileSource); + + +/** + * @inheritDoc + */ +ol.test.source.MockTileSource.prototype.getTile = function(tileCoord) { + var tile = new ol.Tile(tileCoord); + var key = tileCoord.toString(); + if (this.loaded_[key]) { + tile.state = ol.TileState.LOADED; + } + return tile; +}; + + +describe('ol.test.source.MockTileSource', function() { + + describe('constructor', function() { + it('creates a tile source', function() { + var source = new ol.test.source.MockTileSource({}); + expect(source).toBeA(ol.source.TileSource); + expect(source).toBeA(ol.test.source.MockTileSource); + }); + }); + + describe('#getTile()', function() { + it('returns a tile with state based on constructor arg', function() { + var source = new ol.test.source.MockTileSource({ + '0/0/0': true, + '1/0/-1': true + }); + var tile; + + // check a loaded tile + tile = source.getTile(new ol.TileCoord(0, 0, 0)); + expect(tile).toBeA(ol.Tile); + expect(tile.state).toBe(ol.TileState.LOADED); + + // check a tile that is not loaded + tile = source.getTile(new ol.TileCoord(1, 0, 0)); + expect(tile).toBeA(ol.Tile); + expect(tile.state).toBe(ol.TileState.IDLE); + + // check another loaded tile + tile = source.getTile(new ol.TileCoord(1, 0, -1)); + expect(tile).toBeA(ol.Tile); + expect(tile.state).toBe(ol.TileState.LOADED); + + }); + }); + +}); + +goog.require('goog.object'); + +goog.require('ol.Coordinate'); +goog.require('ol.Extent'); +goog.require('ol.Projection'); +goog.require('ol.Tile'); +goog.require('ol.TileCoord'); +goog.require('ol.TileState'); +goog.require('ol.source.TileSource'); +goog.require('ol.tilegrid.TileGrid');