diff --git a/src/ol/map.js b/src/ol/map.js index 45a6294b63..34b92731bc 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -55,6 +55,7 @@ goog.require('ol.renderer.dom.Map'); goog.require('ol.renderer.dom.SUPPORTED'); goog.require('ol.renderer.webgl.Map'); goog.require('ol.renderer.webgl.SUPPORTED'); +goog.require('ol.structs.PriorityQueue'); /** @@ -493,11 +494,11 @@ ol.Map.prototype.getTilePriority = // are outside the visible extent. var frameState = this.frameState_; if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) { - return ol.TileQueue.DROP; + return ol.structs.PriorityQueue.DROP; } var coordKey = tile.tileCoord.toString(); if (!frameState.wantedTiles[tileSourceKey][coordKey]) { - return ol.TileQueue.DROP; + return ol.structs.PriorityQueue.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, diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 6484c01a3e..005e8a03a6 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -307,7 +307,7 @@ ol.renderer.Layer.prototype.manageTilePyramid = } var wantedTiles = frameState.wantedTiles[tileSourceKey]; var tileQueue = frameState.tileQueue; - var tile, tileCenter, tileRange, tileResolution, x, y, z; + var tile, 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); @@ -317,9 +317,11 @@ ol.renderer.Layer.prototype.manageTilePyramid = if (ol.PREEMPTIVELY_LOAD_LOW_RESOLUTION_TILES || z == currentZ) { tile = tileSource.getTile(z, x, y, tileGrid, projection); if (tile.getState() == ol.TileState.IDLE) { - tileCenter = tileGrid.getTileCoordCenter(tile.tileCoord); wantedTiles[tile.tileCoord.toString()] = true; - tileQueue.enqueue(tile, tileSourceKey, tileCenter, tileResolution); + if (!tileQueue.isKeyQueued(tile.getKey())) { + tileQueue.enqueue([tile, tileSourceKey, + tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]); + } } } else { tileSource.useTile(z, x, y); diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 43aaf920d3..4d21f2ef44 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -5,18 +5,7 @@ goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('ol.Coordinate'); goog.require('ol.Tile'); -goog.require('ol.TileState'); - - -/** - * Tile Queue. - * - * The implementation is inspired from the Closure Library's Heap - * class and Python's heapq module. - * - * http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html - * http://hg.python.org/cpython/file/2.7/Lib/heapq.py - */ +goog.require('ol.structs.PriorityQueue'); /** @@ -28,6 +17,7 @@ ol.TilePriorityFunction; /** * @constructor + * @extends {ol.structs.PriorityQueue} * @param {ol.TilePriorityFunction} tilePriorityFunction * Tile priority function. * @param {Function} tileChangeCallback @@ -35,11 +25,22 @@ ol.TilePriorityFunction; */ ol.TileQueue = function(tilePriorityFunction, tileChangeCallback) { - /** - * @private - * @type {ol.TilePriorityFunction} - */ - this.tilePriorityFunction_ = tilePriorityFunction; + goog.base( + this, + /** + * @param {Array} element Element. + * @return {number} Priority. + */ + function(element) { + return tilePriorityFunction.apply(null, element); + }, + /** + * @param {Array} element Element. + * @return {string} Key. + */ + function(element) { + return /** @type {ol.Tile} */ (element[0]).getKey(); + }); /** * @private @@ -59,72 +60,8 @@ ol.TileQueue = function(tilePriorityFunction, tileChangeCallback) { */ this.tilesLoading_ = 0; - /** - * @private - * @type {Array.>} - */ - this.heap_ = []; - - /** - * @private - * @type {Object.} - */ - this.queuedTileKeys_ = {}; - -}; - - -/** - * @const {number} - */ -ol.TileQueue.DROP = Infinity; - - -/** - * Remove and return the highest-priority tile. O(logn). - * @private - * @return {ol.Tile} Tile. - */ -ol.TileQueue.prototype.dequeue_ = function() { - var heap = this.heap_; - goog.asserts.assert(heap.length > 0); - var tile = /** @type {ol.Tile} */ (heap[0][1]); - if (heap.length == 1) { - heap.length = 0; - } else { - heap[0] = heap.pop(); - this.siftUp_(0); - } - var tileKey = tile.getKey(); - delete this.queuedTileKeys_[tileKey]; - return tile; -}; - - -/** - * Enqueue a tile. O(logn). - * @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, tileResolution) { - if (tile.getState() != ol.TileState.IDLE) { - return; - } - var tileKey = tile.getKey(); - if (!(tileKey in this.queuedTileKeys_)) { - var priority = this.tilePriorityFunction_( - tile, tileSourceKey, tileCenter, tileResolution); - if (priority != ol.TileQueue.DROP) { - this.heap_.push( - [priority, tile, tileSourceKey, tileCenter, tileResolution]); - this.queuedTileKeys_[tileKey] = true; - this.siftDown_(0, this.heap_.length - 1); - } - } }; +goog.inherits(ol.TileQueue, ol.structs.PriorityQueue); /** @@ -136,137 +73,16 @@ ol.TileQueue.prototype.handleTileChange = function() { }; -/** - * Gets the index of the left child of the node at the given index. - * @param {number} index The index of the node to get the left child for. - * @return {number} The index of the left child. - * @private - */ -ol.TileQueue.prototype.getLeftChildIndex_ = function(index) { - return index * 2 + 1; -}; - - -/** - * Gets the index of the right child of the node at the given index. - * @param {number} index The index of the node to get the right child for. - * @return {number} The index of the right child. - * @private - */ -ol.TileQueue.prototype.getRightChildIndex_ = function(index) { - return index * 2 + 2; -}; - - -/** - * Gets the index of the parent of the node at the given index. - * @param {number} index The index of the node to get the parent for. - * @return {number} The index of the parent. - * @private - */ -ol.TileQueue.prototype.getParentIndex_ = function(index) { - return (index - 1) >> 1; -}; - - -/** - * Make _heap a heap. O(n). - * @private - */ -ol.TileQueue.prototype.heapify_ = function() { - for (var i = (this.heap_.length >> 1) - 1; i >= 0; i--) { - this.siftUp_(i); - } -}; - - /** * FIXME empty description for jsdoc */ ol.TileQueue.prototype.loadMoreTiles = function() { var tile; - while (this.heap_.length > 0 && this.tilesLoading_ < this.maxTilesLoading_) { - tile = /** @type {ol.Tile} */ (this.dequeue_()); + while (!this.isEmpty() && this.tilesLoading_ < this.maxTilesLoading_) { + tile = /** @type {ol.Tile} */ (this.dequeue()[0]); goog.events.listenOnce(tile, goog.events.EventType.CHANGE, this.handleTileChange, false, this); tile.load(); ++this.tilesLoading_; } }; - - -/** - * @param {number} index The index of the node to move down. - * @private - */ -ol.TileQueue.prototype.siftUp_ = function(index) { - var heap = this.heap_; - var count = heap.length; - var node = heap[index]; - var startIndex = index; - - while (index < (count >> 1)) { - var lIndex = this.getLeftChildIndex_(index); - var rIndex = this.getRightChildIndex_(index); - - var smallerChildIndex = rIndex < count && - heap[rIndex][0] < heap[lIndex][0] ? - rIndex : lIndex; - - heap[index] = heap[smallerChildIndex]; - index = smallerChildIndex; - } - - heap[index] = node; - this.siftDown_(startIndex, index); -}; - - -/** - * @param {number} startIndex The index of the root. - * @param {number} index The index of the node to move up. - * @private - */ -ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { - var heap = this.heap_; - var node = heap[index]; - - while (index > startIndex) { - var parentIndex = this.getParentIndex_(index); - if (heap[parentIndex][0] > node[0]) { - heap[index] = heap[parentIndex]; - index = parentIndex; - } else { - break; - } - } - heap[index] = node; -}; - - -/** - * FIXME empty description for jsdoc - */ -ol.TileQueue.prototype.reprioritize = function() { - var heap = this.heap_; - 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]); - tileResolution = /** @type {number} */ (node[4]); - priority = this.tilePriorityFunction_( - tile, tileSourceKey, tileCenter, tileResolution); - if (priority == ol.TileQueue.DROP) { - tileKey = tile.getKey(); - delete this.queuedTileKeys_[tileKey]; - } else { - node[0] = priority; - heap[n++] = node; - } - } - heap.length = n; - this.heapify_(); -}; diff --git a/test/spec/ol/tilequeue.test.js b/test/spec/ol/tilequeue.test.js index ab4b261e87..1e30024b92 100644 --- a/test/spec/ol/tilequeue.test.js +++ b/test/spec/ol/tilequeue.test.js @@ -4,15 +4,15 @@ describe('ol.TileQueue', function() { // is the tile queue's array a heap? function isHeap(tq) { - var heap = tq.heap_; + var priorities = tq.priorities_; var i; var key; var leftKey; var rightKey; - for (i = 0; i < (heap.length >> 1) - 1; i++) { - key = heap[i][0]; - leftKey = heap[tq.getLeftChildIndex_(i)][0]; - rightKey = heap[tq.getRightChildIndex_(i)][0]; + for (i = 0; i < (priorities.length >> 1) - 1; i++) { + key = priorities[i]; + leftKey = priorities[tq.getLeftChildIndex_(i)]; + rightKey = priorities[tq.getRightChildIndex_(i)]; if (leftKey < key || rightKey < key) { return false; } @@ -25,8 +25,9 @@ describe('ol.TileQueue', function() { for (i = 0; i < num; i++) { tile = new ol.Tile(); priority = Math.floor(Math.random() * 100); - tq.heap_.push([priority, tile, '', new ol.Coordinate(0, 0)]); - tq.queuedTileKeys_[tile.getKey()] = true; + tq.elements_.push([tile, '', new ol.Coordinate(0, 0)]); + tq.priorities_.push(priority); + tq.queuedElements_[tile.getKey()] = true; } } @@ -53,15 +54,16 @@ describe('ol.TileQueue', function() { // rest var i = 0; - tq.tilePriorityFunction_ = function() { + tq.priorityFunction_ = function() { if ((i++) % 2 === 0) { - return ol.TileQueue.DROP; + return ol.structs.PriorityQueue.DROP; } return Math.floor(Math.random() * 100); }; tq.reprioritize(); - expect(tq.heap_.length).to.eql(50); + expect(tq.elements_.length).to.eql(50); + expect(tq.priorities_.length).to.eql(50); expect(isHeap(tq)).to.be.ok(); }); @@ -71,3 +73,4 @@ describe('ol.TileQueue', function() { goog.require('ol.Coordinate'); goog.require('ol.Tile'); goog.require('ol.TileQueue'); +goog.require('ol.structs.PriorityQueue');