diff --git a/src/ol/map.js b/src/ol/map.js index c15999df45..45a6294b63 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -484,9 +484,13 @@ ol.Map.prototype.getOverlayContainer = function() { * @param {ol.Tile} tile Tile. * @param {string} tileSourceKey Tile source key. * @param {ol.Coordinate} tileCenter Tile center. + * @param {number} tileResolution Tile resolution. * @return {number} Tile priority. */ -ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) { +ol.Map.prototype.getTilePriority = + function(tile, tileSourceKey, tileCenter, tileResolution) { + // Filter out tiles at higher zoom levels than the current zoom level, or that + // are outside the visible extent. var frameState = this.frameState_; if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) { return ol.TileQueue.DROP; @@ -495,11 +499,14 @@ ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) { if (!frameState.wantedTiles[tileSourceKey][coordKey]) { return ol.TileQueue.DROP; } + // Prioritize tiles closest to the focus or center. The + 1 helps to + // prioritize tiles at higher zoom levels over tiles at lower zoom levels, + // even if the tile's center is close to the focus. var focus = goog.isNull(this.focus_) ? frameState.view2DState.center : this.focus_; var deltaX = tileCenter.x - focus.x; var deltaY = tileCenter.y - focus.y; - return deltaX * deltaX + deltaY * deltaY; + return tileResolution * (deltaX * deltaX + deltaY * deltaY + 1); }; diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index d6cdf09329..c2fbb13e9b 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -96,7 +96,6 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); - var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); if (goog.isNull(tileGrid)) { tileGrid = ol.tilegrid.getForProjection(projection); @@ -156,19 +155,14 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = tilesToDrawByZ, getTileIfLoaded); var allTilesLoaded = true; - var tile, tileCenter, tileCoord, tileState, x, y; + var tile, tileCoord, tileState, 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); tile = tileSource.getTile(tileCoord, tileGrid, projection); tileState = tile.getState(); - if (tileState == ol.TileState.IDLE) { - this.updateWantedTiles(frameState.wantedTiles, tileSource, tileCoord); - tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); - } else if (tileState == ol.TileState.LOADED || - tileState == ol.TileState.EMPTY) { + if (tileState == ol.TileState.LOADED || tileState == ol.TileState.EMPTY) { tilesToDrawByZ[z][tileCoord.toString()] = tile; continue; } else if (tileState == ol.TileState.ERROR) { @@ -246,7 +240,8 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - tileSource.useLowResolutionTiles(z, extent, tileGrid); + this.manageTilePyramid( + frameState, tileSource, tileGrid, projection, extent, z); this.scheduleExpireCache(frameState, tileSource); var transform = this.transform_; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 6c828573b3..ba3d1e3be5 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -83,7 +83,6 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); - var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); if (goog.isNull(tileGrid)) { tileGrid = ol.tilegrid.getForProjection(projection); @@ -113,18 +112,14 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = tilesToDrawByZ, getTileIfLoaded); var allTilesLoaded = true; - var tile, tileCenter, tileCoord, tileState, x, y; + var tile, tileCoord, tileState, 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); tile = tileSource.getTile(tileCoord, tileGrid, projection); tileState = tile.getState(); - if (tileState == ol.TileState.IDLE) { - this.updateWantedTiles(frameState.wantedTiles, tileSource, tileCoord); - tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); - } else if (tileState == ol.TileState.LOADED) { + if (tileState == ol.TileState.LOADED) { tilesToDrawByZ[z][tileCoord.toString()] = tile; continue; } else if (tileState == ol.TileState.ERROR || @@ -224,7 +219,8 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - tileSource.useLowResolutionTiles(z, extent, tileGrid); + this.manageTilePyramid( + frameState, tileSource, tileGrid, projection, extent, z); this.scheduleExpireCache(frameState, tileSource); }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index ed89f1071f..7e08147cf8 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -18,6 +18,12 @@ goog.require('ol.layer.LayerState'); goog.require('ol.source.TileSource'); +/** + * @define {boolean} Preemptively load low resolution tiles. + */ +ol.PREEMPTIVELY_LOAD_LOW_RESOLUTION_TILES = true; + + /** * @constructor @@ -246,23 +252,6 @@ ol.renderer.Layer.prototype.updateUsedTiles = }; -/** - * @protected - * @param {Object.>} wantedTiles Wanted tiles. - * @param {ol.source.TileSource} tileSource Tile source. - * @param {ol.TileCoord} tileCoord Tile coordinate. - */ -ol.renderer.Layer.prototype.updateWantedTiles = - function(wantedTiles, tileSource, tileCoord) { - var tileSourceKey = goog.getUid(tileSource).toString(); - var coordKey = tileCoord.toString(); - if (!(tileSourceKey in wantedTiles)) { - wantedTiles[tileSourceKey] = {}; - } - wantedTiles[tileSourceKey][coordKey] = true; -}; - - /** * @param {function(ol.Tile): boolean} isLoadedFunction Function to * determine if the tile is loaded. @@ -293,3 +282,50 @@ ol.renderer.Layer.prototype.snapCenterToPixel = resolution * (Math.round(center.x / resolution) + (size.width % 2) / 2), resolution * (Math.round(center.y / resolution) + (size.height % 2) / 2)); }; + + +/** + * Manage tile pyramid. + * This function performs a number of functions related to the tiles at the + * current zoom and lower zoom levels: + * - registers idle tiles in frameState.wantedTiles so that they are not + * discarded by the tile queue + * - enqueues missing tiles + * @param {ol.FrameState} frameState Frame state. + * @param {ol.source.TileSource} tileSource Tile source. + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @param {ol.Projection} projection Projection. + * @param {ol.Extent} extent Extent. + * @param {number} currentZ Current Z. + * @protected + */ +ol.renderer.Layer.prototype.manageTilePyramid = + function(frameState, tileSource, tileGrid, projection, extent, currentZ) { + var tileSourceKey = goog.getUid(tileSource).toString(); + if (!(tileSourceKey in frameState.wantedTiles)) { + frameState.wantedTiles[tileSourceKey] = {}; + } + var wantedTiles = frameState.wantedTiles[tileSourceKey]; + var tileQueue = frameState.tileQueue; + var tile, tileCenter, tileCoord, tileRange, tileResolution, x, y, z; + // FIXME this should loop up to tileGrid's minZ when implemented + for (z = currentZ; z >= 0; --z) { + tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + tileResolution = tileGrid.getResolution(z); + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + if (ol.PREEMPTIVELY_LOAD_LOW_RESOLUTION_TILES || z == currentZ) { + tileCoord = new ol.TileCoord(z, x, y); + tile = tileSource.getTile(tileCoord, tileGrid, projection); + if (tile.getState() == ol.TileState.IDLE) { + tileCenter = tileGrid.getTileCoordCenter(tileCoord); + wantedTiles[tileCoord.toString()] = true; + tileQueue.enqueue(tile, tileSourceKey, tileCenter, tileResolution); + } + } else { + tileSource.useTile(z + '/' + x + '/' + y); + } + } + } + } +}; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 7b7a848d1a..c9b0f69bfb 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -177,7 +177,6 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); - var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); if (goog.isNull(tileGrid)) { tileGrid = ol.tilegrid.getForProjection(projection); @@ -272,11 +271,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = tileCoord = new ol.TileCoord(z, x, y); tile = tileSource.getTile(tileCoord, tileGrid, projection); tileState = tile.getState(); - if (tileState == ol.TileState.IDLE) { - this.updateWantedTiles(frameState.wantedTiles, tileSource, tileCoord); - tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); - } else if (tileState == ol.TileState.LOADED) { + if (tileState == ol.TileState.LOADED) { if (mapRenderer.isTileTextureLoaded(tile)) { tilesToDrawByZ[z][tileCoord.toString()] = tile; continue; @@ -344,7 +339,8 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - tileSource.useLowResolutionTiles(z, extent, tileGrid); + this.manageTilePyramid( + frameState, tileSource, tileGrid, projection, extent, z); this.scheduleExpireCache(frameState, tileSource); var texCoordMatrix = this.texCoordMatrix; diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js index 01d8fe7d3c..d9dc144248 100644 --- a/src/ol/source/tilesource.js +++ b/src/ol/source/tilesource.js @@ -138,26 +138,6 @@ ol.source.TileSource.prototype.getTileGrid = function() { }; -/** - * @param {number} z Z. - * @param {ol.Extent} extent Extent. - * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. - */ -ol.source.TileSource.prototype.useLowResolutionTiles = - function(z, extent, tileGrid) { - var tileRange, x, y, zKey; - // FIXME this should loop up to tileGrid's minZ when implemented - for (; z >= 0; --z) { - tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); - for (x = tileRange.minX; x <= tileRange.maxX; ++x) { - for (y = tileRange.minY; y <= tileRange.maxY; ++y) { - this.useTile(z + '/' + x + '/' + y); - } - } - } -}; - - /** * Marks a tile coord as being used, without triggering a load. * @param {string} tileCoordKey Tile coordinate key. diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 3232686929..43aaf920d3 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -20,7 +20,7 @@ goog.require('ol.TileState'); /** - * @typedef {function(ol.Tile, string, ol.Coordinate): number} + * @typedef {function(ol.Tile, string, ol.Coordinate, number): number} */ ol.TilePriorityFunction; @@ -106,16 +106,20 @@ ol.TileQueue.prototype.dequeue_ = function() { * @param {ol.Tile} tile Tile. * @param {string} tileSourceKey Tile source key. * @param {ol.Coordinate} tileCenter Tile center. + * @param {number} tileResolution Tile resolution. */ -ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) { +ol.TileQueue.prototype.enqueue = function( + tile, tileSourceKey, tileCenter, tileResolution) { if (tile.getState() != ol.TileState.IDLE) { return; } var tileKey = tile.getKey(); if (!(tileKey in this.queuedTileKeys_)) { - var priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); + var priority = this.tilePriorityFunction_( + tile, tileSourceKey, tileCenter, tileResolution); if (priority != ol.TileQueue.DROP) { - this.heap_.push([priority, tile, tileSourceKey, tileCenter]); + this.heap_.push( + [priority, tile, tileSourceKey, tileCenter, tileResolution]); this.queuedTileKeys_[tileKey] = true; this.siftDown_(0, this.heap_.length - 1); } @@ -245,13 +249,16 @@ ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { */ ol.TileQueue.prototype.reprioritize = function() { var heap = this.heap_; - var i, n = 0, node, priority, tile, tileCenter, tileKey, tileSourceKey; + var i, n = 0, node, priority; + var tile, tileCenter, tileKey, tileResolution, tileSourceKey; for (i = 0; i < heap.length; ++i) { node = heap[i]; tile = /** @type {ol.Tile} */ (node[1]); tileSourceKey = /** @type {string} */ (node[2]); tileCenter = /** @type {ol.Coordinate} */ (node[3]); - priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); + tileResolution = /** @type {number} */ (node[4]); + priority = this.tilePriorityFunction_( + tile, tileSourceKey, tileCenter, tileResolution); if (priority == ol.TileQueue.DROP) { tileKey = tile.getKey(); delete this.queuedTileKeys_[tileKey];