From 52df441cf8a51804d948e8e002d9708f9a85bab1 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 11 Jan 2013 01:10:21 +0100 Subject: [PATCH] Add tile queue --- src/ol/framestate.js | 2 + src/ol/imagetile.js | 1 + src/ol/map.js | 28 +++++ src/ol/renderer/dom/domtilelayerrenderer.js | 4 +- .../renderer/webgl/webgltilelayerrenderer.js | 3 +- src/ol/tilequeue.js | 115 ++++++++++++++++++ 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/ol/tilequeue.js diff --git a/src/ol/framestate.js b/src/ol/framestate.js index 032dac1ed7..32a7edb31c 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -8,6 +8,7 @@ goog.require('ol.Color'); goog.require('ol.Coordinate'); goog.require('ol.Extent'); goog.require('ol.Size'); +goog.require('ol.TileQueue'); goog.require('ol.View2DState'); goog.require('ol.layer.LayerState'); @@ -20,6 +21,7 @@ goog.require('ol.layer.LayerState'); * layerStates: Object., * postRenderFunctions: Array., * size: ol.Size, + * tileQueue: ol.TileQueue, * time: number, * view2DState: ol.View2DState}} */ diff --git a/src/ol/imagetile.js b/src/ol/imagetile.js index c75c11ea23..12255034e3 100644 --- a/src/ol/imagetile.js +++ b/src/ol/imagetile.js @@ -92,6 +92,7 @@ ol.ImageTile.prototype.getKey = function() { ol.ImageTile.prototype.handleImageError_ = function() { this.state = ol.TileState.ERROR; this.unlistenImage_(); + this.dispatchChangeEvent(); }; diff --git a/src/ol/map.js b/src/ol/map.js index 01a180637c..a617727b2e 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -36,6 +36,7 @@ goog.require('ol.Pixel'); goog.require('ol.ResolutionConstraint'); goog.require('ol.RotationConstraint'); goog.require('ol.Size'); +goog.require('ol.TileQueue'); goog.require('ol.TransformFunction'); goog.require('ol.View'); goog.require('ol.View2D'); @@ -249,6 +250,12 @@ ol.Map = function(mapOptions) { */ this.handlePostRender_ = goog.bind(this.handlePostRender, this); + /** + * @private + * @type {ol.TileQueue} + */ + this.tileQueue_ = new ol.TileQueue(goog.bind(this.getTilePriority, this)); + this.setValues(mapOptionsInternal.values); this.handleBrowserWindowResize(); @@ -420,6 +427,24 @@ ol.Map.prototype.getOverlayContainer = function() { }; +/** + * @param {ol.Tile} tile Tile. + * @param {ol.Coordinate} tileCenter Tile center. + * @param {number} tileResolution Tile resolution. + * @return {number|undefined} Tile priority. + */ +ol.Map.prototype.getTilePriority = function(tile, tileCenter, tileResolution) { + if (goog.isNull(this.frameState_)) { + return undefined; + } else { + var center = this.frameState_.view2DState.center; + var deltaX = tileCenter.x - center.x; + var deltaY = tileCenter.y - center.y; + return Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; + } +}; + + /** * @param {goog.events.BrowserEvent} browserEvent Browser event. * @param {string=} opt_type Type. @@ -474,6 +499,8 @@ ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) { * @protected */ ol.Map.prototype.handlePostRender = function() { + this.tileQueue_.reprioritize(); // FIXME only call if needed + this.tileQueue_.loadMoreTiles(); goog.array.forEach( this.postRenderFunctions_, function(postRenderFunction) { @@ -572,6 +599,7 @@ ol.Map.prototype.renderFrame_ = function(time) { layerStates: layerStates, postRenderFunctions: [], size: size, + tileQueue: this.tileQueue_, view2DState: view2DState, time: time }; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 88969c284f..3cf96b1331 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -91,6 +91,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = var tileGrid = tileSource.getTileGrid(); var z = tileGrid.getZForResolution(view2DState.resolution); + var tileResolution = tileGrid.getResolution(z); /** @type {Object.>} */ var tilesToDrawByZ = {}; @@ -111,7 +112,8 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = var tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { - tile.load(); + var tileCenter = tileGrid.getTileCoordCenter(tileCoord); + frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); } else if (tileState == ol.TileState.LOADED) { tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; return; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index fa4160dfe1..7fef2e09c4 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -391,7 +391,8 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = var tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { - tile.load(); + var tileCenter = tileGrid.getTileCoordCenter(tileCoord); + frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); } else if (tileState == ol.TileState.LOADED) { if (mapRenderer.isTileTextureLoaded(tile)) { tilesToDrawByZ[z][tileCoord.toString()] = tile; diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js new file mode 100644 index 0000000000..5d489af079 --- /dev/null +++ b/src/ol/tilequeue.js @@ -0,0 +1,115 @@ +goog.provide('ol.TilePriorityFunction'); +goog.provide('ol.TileQueue'); + +goog.require('goog.events'); +goog.require('goog.events.EventType'); +goog.require('goog.structs.PriorityQueue'); +goog.require('ol.Coordinate'); +goog.require('ol.Tile'); +goog.require('ol.TileState'); + + +/** + * @typedef {function(ol.Tile, ol.Coordinate, number): (number|undefined)} + */ +ol.TilePriorityFunction; + + + +/** + * @constructor + * @param {ol.TilePriorityFunction} tilePriorityFunction + * Tile priority function. + */ +ol.TileQueue = function(tilePriorityFunction) { + + /** + * @private + * @type {ol.TilePriorityFunction} + */ + this.tilePriorityFunction_ = tilePriorityFunction; + + /** + * @private + * @type {number} + */ + this.maxTilesLoading_ = 8; + + /** + * @private + * @type {number} + */ + this.tilesLoading_ = 0; + + /** + * @private + * @type {goog.structs.PriorityQueue} + */ + this.queue_ = new goog.structs.PriorityQueue(); + + /** + * @private + * @type {Object.} + */ + this.queuedTileKeys_ = {}; + +}; + + +/** + * @param {ol.Tile} tile Tile. + * @param {ol.Coordinate} tileCenter Tile center. + * @param {number} tileResolution Tile resolution. + */ +ol.TileQueue.prototype.enqueue = + function(tile, tileCenter, tileResolution) { + goog.asserts.assert(tile.getState() == ol.TileState.IDLE); + var tileKey = tile.getKey(); + if (!(tileKey in this.queuedTileKeys_)) { + var priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); + if (goog.isDef(priority)) { + this.queue_.enqueue(priority, arguments); + this.queuedTileKeys_[tileKey] = true; + } else { + // FIXME fire drop event? + } + } +}; + + +/** + * @protected + */ +ol.TileQueue.prototype.handleTileChange = function() { + --this.tilesLoading_; +}; + + +/** + */ +ol.TileQueue.prototype.loadMoreTiles = function() { + var tile, tileKey; + while (!this.queue_.isEmpty() && this.tilesLoading_ < this.maxTilesLoading_) { + tile = (/** @type {Array} */ (this.queue_.remove()))[0]; + tileKey = tile.getKey(); + delete this.queuedTileKeys_[tileKey]; + goog.events.listen(tile, goog.events.EventType.CHANGE, + this.handleTileChange, false, this); + tile.load(); + ++this.tilesLoading_; + } +}; + + +/** + */ +ol.TileQueue.prototype.reprioritize = function() { + if (!this.queue_.isEmpty()) { + var queue = this.queue_; + this.queue_ = new goog.structs.PriorityQueue(); + this.queuedTileKeys_ = {}; + while (!queue.isEmpty()) { + this.enqueue.apply(this, /** @type {Array} */ (queue.remove())); + } + } +};