From c0c9cdef15045ef4f9a741ed758f9d57066af858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Thu, 17 Jan 2013 16:15:03 +0100 Subject: [PATCH 01/15] New tile queue implementation (heapify-powered) --- src/ol/tilequeue.js | 178 +++++++++++++++++++++++++++++---- test/jasmine-extensions.js | 6 ++ test/ol.html | 1 + test/spec/ol/tilequeue.test.js | 68 +++++++++++++ 4 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 test/spec/ol/tilequeue.test.js diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index d73f7c0901..9fea413f33 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -1,14 +1,25 @@ goog.provide('ol.TilePriorityFunction'); goog.provide('ol.TileQueue'); +goog.require('goog.array'); 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'); +/** + * 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 + */ + + /** * @typedef {function(ol.Tile, ol.Coordinate, number): (number|undefined)} */ @@ -43,9 +54,9 @@ ol.TileQueue = function(tilePriorityFunction) { /** * @private - * @type {goog.structs.PriorityQueue} + * @type {Array.>} */ - this.queue_ = new goog.structs.PriorityQueue(); + this.heap_ = []; /** * @private @@ -57,12 +68,44 @@ ol.TileQueue = function(tilePriorityFunction) { /** + * FIXME empty description for jsdoc + */ +ol.TileQueue.prototype.clear = function() { + goog.array.clear(this.heap_); +}; + + +/** + * Remove and return the highest-priority tile. O(logn). + * @private + * @return {ol.Tile|undefined} Tile. + */ +ol.TileQueue.prototype.dequeue_ = function() { + var heap = this.heap_; + var count = heap.length; + if (count <= 0) { + return undefined; + } + var tile = /** @type {ol.Tile} */ (heap[0][1]); + if (count == 1) { + goog.array.clear(heap); + } 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 {ol.Coordinate} tileCenter Tile center. * @param {number} tileResolution Tile resolution. */ -ol.TileQueue.prototype.enqueue = - function(tile, tileCenter, tileResolution) { +ol.TileQueue.prototype.enqueue = function(tile, tileCenter, tileResolution) { if (tile.getState() != ol.TileState.IDLE) { return; } @@ -70,8 +113,9 @@ ol.TileQueue.prototype.enqueue = if (!(tileKey in this.queuedTileKeys_)) { var priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); if (goog.isDef(priority)) { - this.queue_.enqueue(priority, arguments); + this.heap_.push([priority, tile, tileCenter, tileResolution]); this.queuedTileKeys_[tileKey] = true; + this.siftDown_(0, this.heap_.length - 1); } else { // FIXME fire drop event? } @@ -87,15 +131,57 @@ 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, tileKey; - while (!this.queue_.isEmpty() && this.tilesLoading_ < this.maxTilesLoading_) { - tile = (/** @type {Array} */ (this.queue_.dequeue()))[0]; - tileKey = tile.getKey(); - delete this.queuedTileKeys_[tileKey]; + var tile; + while (this.heap_.length > 0 && this.tilesLoading_ < this.maxTilesLoading_) { + tile = /** @type {ol.Tile} */ (this.dequeue_()); goog.events.listen(tile, goog.events.EventType.CHANGE, this.handleTileChange, false, this); tile.load(); @@ -104,17 +190,73 @@ ol.TileQueue.prototype.loadMoreTiles = function() { }; +/** + * @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() { - if (!this.queue_.isEmpty()) { - var values = /** @type {Array.} */ (this.queue_.getValues()); - this.queue_.clear(); - this.queuedTileKeys_ = {}; - var i; - for (i = 0; i < values.length; ++i) { - this.enqueue.apply(this, values[i]); + var heap = this.heap_; + var count = heap.length; + var i, priority, node, tile, tileCenter, tileResolution; + for (i = count - 1; i >= 0; i--) { + node = heap[i]; + tile = /** @type {ol.Tile} */ (node[1]); + tileCenter = /** @type {ol.Coordinate} */ (node[2]); + tileResolution = /** @type {number} */ (node[3]); + priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); + if (goog.isDef(priority)) { + node[0] = priority; + } else { + goog.array.removeAt(heap, i); } } + this.heapify_(); }; diff --git a/test/jasmine-extensions.js b/test/jasmine-extensions.js index 4649130908..46e24cb7ae 100644 --- a/test/jasmine-extensions.js +++ b/test/jasmine-extensions.js @@ -4,6 +4,12 @@ beforeEach(function() { toBeA: function(type) { return this.actual instanceof type; }, + toBeGreaterThanOrEqualTo: function(other) { + return this.actual >= other; + }, + toBeLessThanOrEqualTo: function(other) { + return this.actual <= other; + }, toRoughlyEqual: function(other, tol) { return Math.abs(this.actual - other) <= tol; } diff --git a/test/ol.html b/test/ol.html index b0e05e2f6d..c490d01fd1 100644 --- a/test/ol.html +++ b/test/ol.html @@ -84,6 +84,7 @@ + diff --git a/test/spec/ol/tilequeue.test.js b/test/spec/ol/tilequeue.test.js new file mode 100644 index 0000000000..3c5bc8a055 --- /dev/null +++ b/test/spec/ol/tilequeue.test.js @@ -0,0 +1,68 @@ +describe('ol.TileQueue', function() { + + // is the tile queue's array a heap? + function isHeap(tq) { + var heap = tq.heap_; + 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]; + if (leftKey < key || rightKey < key) { + return false; + } + } + return true; + } + + function addRandomPriorityTiles(tq, num) { + var i, tile, priority; + 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), 1]); + tq.queuedTileKeys_[tile.getKey()] = true; + } + } + + describe('heapify', function() { + it('does convert an arbitrary array into a heap', function() { + + var tq = new ol.TileQueue(function() {}); + addRandomPriorityTiles(tq, 100); + + tq.heapify_(); + expect(isHeap(tq)).toBeTruthy(); + }); + }); + + describe('reprioritize', function() { + it('does reprioritize the array', function() { + + var tq = new ol.TileQueue(function() {}); + addRandomPriorityTiles(tq, 100); + + tq.heapify_(); + + // now reprioritize, changing the priority of 50 tiles and removing the + // rest + + var i = 0; + tq.tilePriorityFunction_ = function() { + if ((i++) % 2 === 0) { + return undefined; + } + return Math.floor(Math.random() * 100); + }; + + tq.reprioritize(); + expect(tq.heap_.length).toEqual(50); + expect(isHeap(tq)).toBeTruthy(); + + }); + }); +}); + From 9cf314fd64af34e15a0f31840988b62f4b05da12 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:04:16 +0100 Subject: [PATCH 02/15] Sort object properties --- src/ol/map.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index a0400327e3..6d5caa091f 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -628,9 +628,9 @@ ol.Map.prototype.renderFrame_ = function(time) { size: size, tileQueue: this.tileQueue_, tileUsage: {}, + time: time, view2DState: view2DState, - viewHints: viewHints, - time: time + viewHints: viewHints }; } From 7babfebef96fcc2ae9dab4eedcbf21334c2c1b53 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:35:23 +0100 Subject: [PATCH 03/15] Set frame state immediately after rendering --- src/ol/map.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index 6d5caa091f..2609269db2 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -664,6 +664,8 @@ ol.Map.prototype.renderFrame_ = function(time) { } this.renderer_.renderFrame(frameState); + this.frameState_ = frameState; + this.dirty_ = false; if (!goog.isNull(frameState)) { if (frameState.animate) { @@ -672,8 +674,6 @@ ol.Map.prototype.renderFrame_ = function(time) { Array.prototype.push.apply( this.postRenderFunctions_, frameState.postRenderFunctions); } - this.frameState_ = frameState; - this.dirty_ = false; this.dispatchEvent( new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState)); From 111b4585a8f8cc400fcbc4cc6d523114ae5d9390 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:36:41 +0100 Subject: [PATCH 04/15] Fix key usage --- src/ol/renderer/layerrenderer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index b2003467ab..6e1a4fd729 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -141,7 +141,7 @@ ol.renderer.Layer.prototype.updateTileUsage = var sourceKey = goog.getUid(source).toString(); var zKey = z.toString(); if (sourceKey in tileUsage) { - if (z in tileUsage[sourceKey]) { + if (zKey in tileUsage[sourceKey]) { tileUsage[sourceKey][zKey].extend(tileRange); } else { tileUsage[sourceKey][zKey] = tileRange; From 1c524caae610554a112b1295d2560f121edea51a Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:36:26 +0100 Subject: [PATCH 05/15] Discard unwanted tiles from tile queue --- src/ol/framestate.js | 5 +++- src/ol/map.js | 24 +++++++++++------- .../canvas/canvastilelayerrenderer.js | 4 ++- src/ol/renderer/dom/domtilelayerrenderer.js | 4 ++- src/ol/renderer/layerrenderer.js | 25 +++++++++++++++++++ .../renderer/webgl/webgltilelayerrenderer.js | 4 ++- src/ol/tilequeue.js | 18 ++++++------- test/spec/ol/tilequeue.test.js | 2 +- 8 files changed, 63 insertions(+), 23 deletions(-) diff --git a/src/ol/framestate.js b/src/ol/framestate.js index 574fefe20f..d0b7ec4570 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -1,4 +1,6 @@ // FIXME add view3DState +// FIXME rename tileUsage to usedTiles +// FIXME factor out common code between tileUsage and wantedTiles goog.provide('ol.FrameState'); goog.provide('ol.PostRenderFunction'); @@ -29,7 +31,8 @@ goog.require('ol.layer.LayerState'); * tileUsage: Object.>, * time: number, * view2DState: ol.View2DState, - * viewHints: Array.}} + * viewHints: Array., + * wantedTiles: Object.>}} */ ol.FrameState; diff --git a/src/ol/map.js b/src/ol/map.js index 2609269db2..33fdaed885 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -453,19 +453,24 @@ 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|undefined} Tile priority. */ -ol.Map.prototype.getTilePriority = function(tile, tileCenter, tileResolution) { - if (goog.isNull(this.frameState_)) { +ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) { + var frameState = this.frameState_; + if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) { 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; } + var zKey = tile.tileCoord.z.toString(); + if (!(zKey in frameState.wantedTiles[tileSourceKey]) || + !frameState.wantedTiles[tileSourceKey][zKey].contains(tile.tileCoord)) { + return undefined; + } + var center = frameState.view2DState.center; + var deltaX = tileCenter.x - center.x; + var deltaY = tileCenter.y - center.y; + return deltaX * deltaX + deltaY * deltaY; }; @@ -630,7 +635,8 @@ ol.Map.prototype.renderFrame_ = function(time) { tileUsage: {}, time: time, view2DState: view2DState, - viewHints: viewHints + viewHints: viewHints, + wantedTiles: {} }; } diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 4c17795648..66c9cf6e25 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -87,6 +87,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); + var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); var tileSize = tileGrid.getTileSize(); var z = tileGrid.getZForResolution(view2DState.resolution); @@ -165,7 +166,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); + frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { tilesToDrawByZ[z][tileCoord.toString()] = tile; continue; @@ -216,6 +217,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = } this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); var transform = this.transform_; goog.vec.Mat4.makeIdentity(transform); diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index 58a55295f7..fa9a86b1ae 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -83,6 +83,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); + var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); var z = tileGrid.getZForResolution(view2DState.resolution); var tileResolution = tileGrid.getResolution(z); @@ -132,7 +133,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); + frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { tilesToDrawByZ[z][tileCoord.toString()] = tile; continue; @@ -237,6 +238,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = } this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index 6e1a4fd729..c019c12276 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -151,3 +151,28 @@ ol.renderer.Layer.prototype.updateTileUsage = tileUsage[sourceKey][zKey] = tileRange; } }; + + +/** + * @protected + * @param {Object.>} wantedTiles Wanted + * tile ranges. + * @param {ol.source.Source} source Source. + * @param {number} z Z. + * @param {ol.TileRange} tileRange Tile range. + */ +ol.renderer.Layer.prototype.updateWantedTiles = + function(wantedTiles, source, z, tileRange) { + var sourceKey = goog.getUid(source).toString(); + var zKey = z.toString(); + if (sourceKey in wantedTiles) { + if (zKey in wantedTiles[sourceKey]) { + wantedTiles[sourceKey][zKey].extend(tileRange); + } else { + wantedTiles[sourceKey][zKey] = tileRange; + } + } else { + wantedTiles[sourceKey] = {}; + wantedTiles[sourceKey][zKey] = tileRange; + } +}; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index de1f18e1e2..93654d0a98 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -270,6 +270,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = var tileLayer = this.getTileLayer(); var tileSource = tileLayer.getTileSource(); + var tileSourceKey = goog.getUid(tileSource).toString(); var tileGrid = tileSource.getTileGrid(); var z = tileGrid.getZForResolution(view2DState.resolution); var tileResolution = tileGrid.getResolution(z); @@ -392,7 +393,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = tileState = tile.getState(); if (tileState == ol.TileState.IDLE) { tileCenter = tileGrid.getTileCoordCenter(tileCoord); - frameState.tileQueue.enqueue(tile, tileCenter, tileResolution); + frameState.tileQueue.enqueue(tile, tileSourceKey, tileCenter); } else if (tileState == ol.TileState.LOADED) { if (mapRenderer.isTileTextureLoaded(tile)) { tilesToDrawByZ[z][tileCoord.toString()] = tile; @@ -460,6 +461,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); goog.vec.Mat4.makeIdentity(this.matrix_); goog.vec.Mat4.translate(this.matrix_, diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 9fea413f33..d914257beb 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -21,7 +21,7 @@ goog.require('ol.TileState'); /** - * @typedef {function(ol.Tile, ol.Coordinate, number): (number|undefined)} + * @typedef {function(ol.Tile, string, ol.Coordinate): (number|undefined)} */ ol.TilePriorityFunction; @@ -102,18 +102,18 @@ ol.TileQueue.prototype.dequeue_ = function() { /** * 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, tileCenter, tileResolution) { +ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) { if (tile.getState() != ol.TileState.IDLE) { return; } var tileKey = tile.getKey(); if (!(tileKey in this.queuedTileKeys_)) { - var priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); + var priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); if (goog.isDef(priority)) { - this.heap_.push([priority, tile, tileCenter, tileResolution]); + this.heap_.push([priority, tile, tileSourceKey, tileCenter]); this.queuedTileKeys_[tileKey] = true; this.siftDown_(0, this.heap_.length - 1); } else { @@ -245,13 +245,13 @@ ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { ol.TileQueue.prototype.reprioritize = function() { var heap = this.heap_; var count = heap.length; - var i, priority, node, tile, tileCenter, tileResolution; + var i, priority, node, tile, tileCenter, tileSourceKey; for (i = count - 1; i >= 0; i--) { node = heap[i]; tile = /** @type {ol.Tile} */ (node[1]); - tileCenter = /** @type {ol.Coordinate} */ (node[2]); - tileResolution = /** @type {number} */ (node[3]); - priority = this.tilePriorityFunction_(tile, tileCenter, tileResolution); + tileSourceKey = /** @type {string} */ (node[2]); + tileCenter = /** @type {ol.Coordinate} */ (node[3]); + priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); if (goog.isDef(priority)) { node[0] = priority; } else { diff --git a/test/spec/ol/tilequeue.test.js b/test/spec/ol/tilequeue.test.js index 3c5bc8a055..f4fa134a1c 100644 --- a/test/spec/ol/tilequeue.test.js +++ b/test/spec/ol/tilequeue.test.js @@ -23,7 +23,7 @@ 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), 1]); + tq.heap_.push([priority, tile, '', new ol.Coordinate(0, 0)]); tq.queuedTileKeys_[tile.getKey()] = true; } } From 3ec2014ad1d4e97df330d9e7110a9e650c4886ff Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:46:32 +0100 Subject: [PATCH 06/15] Rename tileUsage to usedTiles --- src/ol/control/attributioncontrol.js | 14 +++++++------- src/ol/framestate.js | 5 ++--- src/ol/map.js | 2 +- .../renderer/canvas/canvastilelayerrenderer.js | 2 +- src/ol/renderer/dom/domtilelayerrenderer.js | 2 +- src/ol/renderer/layerrenderer.js | 18 +++++++++--------- .../renderer/webgl/webgltilelayerrenderer.js | 2 +- 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/ol/control/attributioncontrol.js b/src/ol/control/attributioncontrol.js index f7148722e8..52e33effba 100644 --- a/src/ol/control/attributioncontrol.js +++ b/src/ol/control/attributioncontrol.js @@ -69,7 +69,7 @@ ol.control.Attribution.prototype.handleMapPostrender = function(mapEvent) { if (goog.isNull(frameState)) { this.updateElement_(null); } else { - this.updateElement_(frameState.tileUsage); + this.updateElement_(frameState.usedTiles); } }; @@ -94,12 +94,12 @@ ol.control.Attribution.prototype.setMap = function(map) { /** * @private - * @param {?Object.>} tileUsage Tile - * usage. + * @param {?Object.>} usedTiles Used + * tiles. */ -ol.control.Attribution.prototype.updateElement_ = function(tileUsage) { +ol.control.Attribution.prototype.updateElement_ = function(usedTiles) { - if (goog.isNull(tileUsage)) { + if (goog.isNull(usedTiles)) { if (this.renderedVisible_) { goog.style.showElement(this.element, false); this.renderedVisible_ = false; @@ -136,14 +136,14 @@ ol.control.Attribution.prototype.updateElement_ = function(tileUsage) { var attributions = {}; var i, tileRanges, tileSource, tileSourceAttribution, tileSourceAttributionKey, tileSourceAttributions, tileSourceKey, z; - for (tileSourceKey in tileUsage) { + for (tileSourceKey in usedTiles) { goog.asserts.assert(tileSourceKey in tileSources); tileSource = tileSources[tileSourceKey]; tileSourceAttributions = tileSource.getAttributions(); if (goog.isNull(tileSourceAttributions)) { continue; } - tileRanges = tileUsage[tileSourceKey]; + tileRanges = usedTiles[tileSourceKey]; for (i = 0; i < tileSourceAttributions.length; ++i) { tileSourceAttribution = tileSourceAttributions[i]; tileSourceAttributionKey = goog.getUid(tileSourceAttribution).toString(); diff --git a/src/ol/framestate.js b/src/ol/framestate.js index d0b7ec4570..6f06b5fff6 100644 --- a/src/ol/framestate.js +++ b/src/ol/framestate.js @@ -1,6 +1,5 @@ // FIXME add view3DState -// FIXME rename tileUsage to usedTiles -// FIXME factor out common code between tileUsage and wantedTiles +// FIXME factor out common code between usedTiles and wantedTiles goog.provide('ol.FrameState'); goog.provide('ol.PostRenderFunction'); @@ -28,8 +27,8 @@ goog.require('ol.layer.LayerState'); * postRenderFunctions: Array., * size: ol.Size, * tileQueue: ol.TileQueue, - * tileUsage: Object.>, * time: number, + * usedTiles: Object.>, * view2DState: ol.View2DState, * viewHints: Array., * wantedTiles: Object.>}} diff --git a/src/ol/map.js b/src/ol/map.js index 33fdaed885..c31e7d88b0 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -632,8 +632,8 @@ ol.Map.prototype.renderFrame_ = function(time) { postRenderFunctions: [], size: size, tileQueue: this.tileQueue_, - tileUsage: {}, time: time, + usedTiles: {}, view2DState: view2DState, viewHints: viewHints, wantedTiles: {} diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 66c9cf6e25..8b6a3d0a1d 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -216,7 +216,7 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = frameState.animate = true; } - this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); var transform = this.transform_; diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index fa9a86b1ae..afabed997d 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -237,7 +237,7 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = frameState.animate = true; } - this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); }; diff --git a/src/ol/renderer/layerrenderer.js b/src/ol/renderer/layerrenderer.js index c019c12276..c460fafb3b 100644 --- a/src/ol/renderer/layerrenderer.js +++ b/src/ol/renderer/layerrenderer.js @@ -130,25 +130,25 @@ ol.renderer.Layer.prototype.handleLayerVisibleChange = goog.nullFunction; /** * @protected - * @param {Object.>} tileUsage Tile usage. + * @param {Object.>} usedTiles Used tiles. * @param {ol.source.Source} source Source. * @param {number} z Z. * @param {ol.TileRange} tileRange Tile range. */ -ol.renderer.Layer.prototype.updateTileUsage = - function(tileUsage, source, z, tileRange) { +ol.renderer.Layer.prototype.updateUsedTiles = + function(usedTiles, source, z, tileRange) { // FIXME should we use tilesToDrawByZ instead? var sourceKey = goog.getUid(source).toString(); var zKey = z.toString(); - if (sourceKey in tileUsage) { - if (zKey in tileUsage[sourceKey]) { - tileUsage[sourceKey][zKey].extend(tileRange); + if (sourceKey in usedTiles) { + if (zKey in usedTiles[sourceKey]) { + usedTiles[sourceKey][zKey].extend(tileRange); } else { - tileUsage[sourceKey][zKey] = tileRange; + usedTiles[sourceKey][zKey] = tileRange; } } else { - tileUsage[sourceKey] = {}; - tileUsage[sourceKey][zKey] = tileRange; + usedTiles[sourceKey] = {}; + usedTiles[sourceKey][zKey] = tileRange; } }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 93654d0a98..63e3e86238 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -460,7 +460,7 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = } - this.updateTileUsage(frameState.tileUsage, tileSource, z, tileRange); + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); goog.vec.Mat4.makeIdentity(this.matrix_); From 4846a6a7b3003b60351d4e5e183da3cba0dddf07 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Fri, 18 Jan 2013 16:51:56 +0100 Subject: [PATCH 07/15] Only update wanted tiles if not all tiles are loaded --- src/ol/renderer/canvas/canvastilelayerrenderer.js | 2 +- src/ol/renderer/dom/domtilelayerrenderer.js | 2 +- src/ol/renderer/webgl/webgltilelayerrenderer.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ol/renderer/canvas/canvastilelayerrenderer.js b/src/ol/renderer/canvas/canvastilelayerrenderer.js index 8b6a3d0a1d..c79251e6f9 100644 --- a/src/ol/renderer/canvas/canvastilelayerrenderer.js +++ b/src/ol/renderer/canvas/canvastilelayerrenderer.js @@ -214,10 +214,10 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame = if (!allTilesLoaded) { frameState.animate = true; + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); var transform = this.transform_; goog.vec.Mat4.makeIdentity(transform); diff --git a/src/ol/renderer/dom/domtilelayerrenderer.js b/src/ol/renderer/dom/domtilelayerrenderer.js index afabed997d..9cf215a98e 100644 --- a/src/ol/renderer/dom/domtilelayerrenderer.js +++ b/src/ol/renderer/dom/domtilelayerrenderer.js @@ -235,10 +235,10 @@ ol.renderer.dom.TileLayer.prototype.renderFrame = if (!allTilesLoaded) { frameState.animate = true; + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); }; diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js index 63e3e86238..621883f635 100644 --- a/src/ol/renderer/webgl/webgltilelayerrenderer.js +++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js @@ -456,12 +456,12 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = this.renderedTileRange_ = null; this.renderedFramebufferExtent_ = null; frameState.animate = true; + this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); } } this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); - this.updateWantedTiles(frameState.wantedTiles, tileSource, z, tileRange); goog.vec.Mat4.makeIdentity(this.matrix_); goog.vec.Mat4.translate(this.matrix_, From a8dc8106960ec3598dcf6360e0be3fbb076b22bf Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:10:22 +0100 Subject: [PATCH 08/15] Use Infinity rather than undefined to drop tiles --- src/ol/map.js | 6 +++--- src/ol/tilequeue.js | 12 +++++++++--- test/spec/ol/tilequeue.test.js | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/ol/map.js b/src/ol/map.js index c31e7d88b0..a580e5b315 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -455,17 +455,17 @@ ol.Map.prototype.getOverlayContainer = function() { * @param {ol.Tile} tile Tile. * @param {string} tileSourceKey Tile source key. * @param {ol.Coordinate} tileCenter Tile center. - * @return {number|undefined} Tile priority. + * @return {number} Tile priority. */ ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter) { var frameState = this.frameState_; if (goog.isNull(frameState) || !(tileSourceKey in frameState.wantedTiles)) { - return undefined; + return ol.TileQueue.DROP; } var zKey = tile.tileCoord.z.toString(); if (!(zKey in frameState.wantedTiles[tileSourceKey]) || !frameState.wantedTiles[tileSourceKey][zKey].contains(tile.tileCoord)) { - return undefined; + return ol.TileQueue.DROP; } var center = frameState.view2DState.center; var deltaX = tileCenter.x - center.x; diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index d914257beb..1df40e0f01 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -21,7 +21,7 @@ goog.require('ol.TileState'); /** - * @typedef {function(ol.Tile, string, ol.Coordinate): (number|undefined)} + * @typedef {function(ol.Tile, string, ol.Coordinate): number} */ ol.TilePriorityFunction; @@ -67,6 +67,12 @@ ol.TileQueue = function(tilePriorityFunction) { }; +/** + * @const {number} + */ +ol.TileQueue.DROP = Infinity; + + /** * FIXME empty description for jsdoc */ @@ -112,7 +118,7 @@ ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) { var tileKey = tile.getKey(); if (!(tileKey in this.queuedTileKeys_)) { var priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); - if (goog.isDef(priority)) { + if (priority != ol.TileQueue.DROP) { this.heap_.push([priority, tile, tileSourceKey, tileCenter]); this.queuedTileKeys_[tileKey] = true; this.siftDown_(0, this.heap_.length - 1); @@ -252,7 +258,7 @@ ol.TileQueue.prototype.reprioritize = function() { tileSourceKey = /** @type {string} */ (node[2]); tileCenter = /** @type {ol.Coordinate} */ (node[3]); priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); - if (goog.isDef(priority)) { + if (priority != ol.TileQueue.DROP) { node[0] = priority; } else { goog.array.removeAt(heap, i); diff --git a/test/spec/ol/tilequeue.test.js b/test/spec/ol/tilequeue.test.js index f4fa134a1c..37b8d609e0 100644 --- a/test/spec/ol/tilequeue.test.js +++ b/test/spec/ol/tilequeue.test.js @@ -53,7 +53,7 @@ describe('ol.TileQueue', function() { var i = 0; tq.tilePriorityFunction_ = function() { if ((i++) % 2 === 0) { - return undefined; + return ol.TileQueue.DROP; } return Math.floor(Math.random() * 100); }; From 4b971b5f32d85af40b75039e8285ffd19d7242f8 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:24:50 +0100 Subject: [PATCH 09/15] Optimize tile dropping in reprioritize --- src/ol/tilequeue.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 1df40e0f01..8a599d5439 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -250,9 +250,8 @@ ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { */ ol.TileQueue.prototype.reprioritize = function() { var heap = this.heap_; - var count = heap.length; - var i, priority, node, tile, tileCenter, tileSourceKey; - for (i = count - 1; i >= 0; i--) { + var i, n = 0, node, priority, tile, tileCenter, tileSourceKey; + for (i = 0; i < heap.length; ++i) { node = heap[i]; tile = /** @type {ol.Tile} */ (node[1]); tileSourceKey = /** @type {string} */ (node[2]); @@ -260,9 +259,9 @@ ol.TileQueue.prototype.reprioritize = function() { priority = this.tilePriorityFunction_(tile, tileSourceKey, tileCenter); if (priority != ol.TileQueue.DROP) { node[0] = priority; - } else { - goog.array.removeAt(heap, i); + heap[n++] = node; } } + heap.length = n; this.heapify_(); }; From 2bdfd3765ba10309d3f0852672edadc439a3bfb5 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:38:15 +0100 Subject: [PATCH 10/15] Remove stale FIXME --- src/ol/tilequeue.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 8a599d5439..caedf4b1a2 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -122,8 +122,6 @@ ol.TileQueue.prototype.enqueue = function(tile, tileSourceKey, tileCenter) { this.heap_.push([priority, tile, tileSourceKey, tileCenter]); this.queuedTileKeys_[tileKey] = true; this.siftDown_(0, this.heap_.length - 1); - } else { - // FIXME fire drop event? } } }; From 6507e71dd785e661dc3d549666c56e8b9e3db713 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:44:01 +0100 Subject: [PATCH 11/15] Clear queued tile keys when queue is cleared --- src/ol/tilequeue.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index caedf4b1a2..18cf82e453 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -78,6 +78,7 @@ ol.TileQueue.DROP = Infinity; */ ol.TileQueue.prototype.clear = function() { goog.array.clear(this.heap_); + this.queuedTileKeys_ = {}; }; From d43abba154776adda130f79c35a0e204eb7e8c44 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 15:44:58 +0100 Subject: [PATCH 12/15] Use stricter types in private methods, operate on array directly --- src/ol/tilequeue.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index 18cf82e453..baf61b1e06 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -1,7 +1,6 @@ goog.provide('ol.TilePriorityFunction'); goog.provide('ol.TileQueue'); -goog.require('goog.array'); goog.require('goog.events'); goog.require('goog.events.EventType'); goog.require('ol.Coordinate'); @@ -77,7 +76,7 @@ ol.TileQueue.DROP = Infinity; * FIXME empty description for jsdoc */ ol.TileQueue.prototype.clear = function() { - goog.array.clear(this.heap_); + this.heap_.length = 0; this.queuedTileKeys_ = {}; }; @@ -85,17 +84,14 @@ ol.TileQueue.prototype.clear = function() { /** * Remove and return the highest-priority tile. O(logn). * @private - * @return {ol.Tile|undefined} Tile. + * @return {ol.Tile} Tile. */ ol.TileQueue.prototype.dequeue_ = function() { var heap = this.heap_; - var count = heap.length; - if (count <= 0) { - return undefined; - } + goog.asserts.assert(heap.length > 0); var tile = /** @type {ol.Tile} */ (heap[0][1]); - if (count == 1) { - goog.array.clear(heap); + if (heap.length == 1) { + heap.length = 0; } else { heap[0] = heap.pop(); this.siftUp_(0); From 9ef5f0b060bf233bb2e10915103401a3098b750f Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sat, 19 Jan 2013 21:58:01 +0100 Subject: [PATCH 13/15] Ensure dropped tiles are properly removed from tile queue --- src/ol/tilequeue.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index baf61b1e06..c1f3554f14 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -245,14 +245,17 @@ ol.TileQueue.prototype.siftDown_ = function(startIndex, index) { */ ol.TileQueue.prototype.reprioritize = function() { var heap = this.heap_; - var i, n = 0, node, priority, tile, tileCenter, tileSourceKey; + var i, n = 0, node, priority, tile, tileCenter, tileKey, 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); - if (priority != ol.TileQueue.DROP) { + if (priority == ol.TileQueue.DROP) { + tileKey = tile.getKey(); + delete this.queuedTileKeys_[tileKey]; + } else { node[0] = priority; heap[n++] = node; } From 7f38c1683ed16c0adfb8475ab488bcee0d228178 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Sun, 20 Jan 2013 15:15:13 +0100 Subject: [PATCH 14/15] Remove unused TileQueue clear method --- src/ol/tilequeue.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/ol/tilequeue.js b/src/ol/tilequeue.js index c1f3554f14..64984ba1a8 100644 --- a/src/ol/tilequeue.js +++ b/src/ol/tilequeue.js @@ -72,15 +72,6 @@ ol.TileQueue = function(tilePriorityFunction) { ol.TileQueue.DROP = Infinity; -/** - * FIXME empty description for jsdoc - */ -ol.TileQueue.prototype.clear = function() { - this.heap_.length = 0; - this.queuedTileKeys_ = {}; -}; - - /** * Remove and return the highest-priority tile. O(logn). * @private From c6a8e5ef7202777bdbb2baa8e07f9757906732b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 21 Jan 2013 12:29:31 +0100 Subject: [PATCH 15/15] Remove unused Jasmine matchers --- test/jasmine-extensions.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/jasmine-extensions.js b/test/jasmine-extensions.js index 46e24cb7ae..4649130908 100644 --- a/test/jasmine-extensions.js +++ b/test/jasmine-extensions.js @@ -4,12 +4,6 @@ beforeEach(function() { toBeA: function(type) { return this.actual instanceof type; }, - toBeGreaterThanOrEqualTo: function(other) { - return this.actual >= other; - }, - toBeLessThanOrEqualTo: function(other) { - return this.actual <= other; - }, toRoughlyEqual: function(other, tol) { return Math.abs(this.actual - other) <= tol; }