Merge pull request #423 from twpayne/preemptive-webgl-texture-uploads

White flash occurs when zooming out from initially zoomed in view with WebGL renderer
This commit is contained in:
Tom Payne
2013-03-27 16:00:56 -07:00
20 changed files with 774 additions and 284 deletions

View File

@@ -23,6 +23,7 @@ goog.require('ol.layer.LayerState');
* backgroundColor: ol.Color,
* coordinateToPixelMatrix: goog.vec.Mat4.Number,
* extent: (null|ol.Extent),
* focus: ol.Coordinate,
* layersArray: Array.<ol.layer.Layer>,
* layerStates: Object.<number, ol.layer.LayerState>,
* pixelToCoordinateMatrix: goog.vec.Mat4.Number,

View File

@@ -1 +1 @@
@exportClass ol.layer.TileLayer ol.layer.LayerOptions
@exportClass ol.layer.TileLayer ol.layer.TileLayerOptions

View File

@@ -4,21 +4,58 @@ goog.require('ol.layer.Layer');
goog.require('ol.source.TileSource');
/**
* @enum {string}
*/
ol.layer.TileLayerProperty = {
PRELOAD: 'preload'
};
/**
* @constructor
* @extends {ol.layer.Layer}
* @param {ol.layer.LayerOptions} layerOptions Layer options.
* @param {ol.layer.TileLayerOptions} options Options.
*/
ol.layer.TileLayer = function(layerOptions) {
goog.base(this, layerOptions);
ol.layer.TileLayer = function(options) {
goog.base(this, options);
this.setPreload(
goog.isDef(options.preload) ? options.preload : 0);
};
goog.inherits(ol.layer.TileLayer, ol.layer.Layer);
/**
* @return {number} Preload.
*/
ol.layer.TileLayer.prototype.getPreload = function() {
return /** @type {number} */ (this.get(ol.layer.TileLayerProperty.PRELOAD));
};
goog.exportProperty(
ol.layer.TileLayer.prototype,
'getPreload',
ol.layer.TileLayer.prototype.getPreload);
/**
* @return {ol.source.TileSource} Source.
*/
ol.layer.TileLayer.prototype.getTileSource = function() {
return /** @type {ol.source.TileSource} */ (this.getSource());
};
/**
* @param {number} preload Preload.
*/
ol.layer.TileLayer.prototype.setPreload = function(preload) {
this.set(ol.layer.TileLayerProperty.PRELOAD, preload);
};
goog.exportProperty(
ol.layer.TileLayer.prototype,
'setPreload',
ol.layer.TileLayer.prototype.setPreload);

View File

@@ -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,20 +494,22 @@ 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,
// 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 tileResolution * (deltaX * deltaX + deltaY * deltaY + 1);
// Prioritize the highest zoom level tiles closest to the focus.
// Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
// Within a zoom level, tiles are prioritized by the distance in pixels
// between the center of the tile and the focus. The factor of 65536 means
// that the prioritization should behave as desired for tiles up to
// 65536 * Math.log(2) = 45426 pixels from the focus.
var deltaX = tileCenter.x - frameState.focus.x;
var deltaY = tileCenter.y - frameState.focus.y;
return 65536 * Math.log(tileResolution) +
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
};
@@ -713,6 +716,7 @@ ol.Map.prototype.renderFrame_ = function(time) {
backgroundColor : new ol.Color(255, 255, 255, 1),
coordinateToPixelMatrix: this.coordinateToPixelMatrix_,
extent: null,
focus: goog.isNull(this.focus_) ? view2DState.center : this.focus_,
layersArray: layersArray,
layerStates: layerStates,
pixelToCoordinateMatrix: this.pixelToCoordinateMatrix_,

View File

@@ -285,8 +285,8 @@ ol.renderer.canvas.TileLayer.prototype.renderFrame =
}
this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.manageTilePyramid(
frameState, tileSource, tileGrid, projection, extent, z);
this.manageTilePyramid(frameState, tileSource, tileGrid, projection, extent,
z, tileLayer.getPreload());
this.scheduleExpireCache(frameState, tileSource);
var transform = this.transform_;

View File

@@ -218,8 +218,8 @@ ol.renderer.dom.TileLayer.prototype.renderFrame =
}
this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
this.manageTilePyramid(
frameState, tileSource, tileGrid, projection, extent, z);
this.manageTilePyramid(frameState, tileSource, tileGrid, projection, extent,
z, tileLayer.getPreload());
this.scheduleExpireCache(frameState, tileSource);
};

View File

@@ -17,12 +17,6 @@ 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
@@ -297,29 +291,39 @@ ol.renderer.Layer.prototype.snapCenterToPixel =
* @param {ol.Projection} projection Projection.
* @param {ol.Extent} extent Extent.
* @param {number} currentZ Current Z.
* @param {number} preload Load low resolution tiles up to 'preload' levels.
* @param {function(this: T, ol.Tile)=} opt_tileCallback Tile callback.
* @param {T=} opt_obj Object.
* @protected
* @template T
*/
ol.renderer.Layer.prototype.manageTilePyramid =
function(frameState, tileSource, tileGrid, projection, extent, currentZ) {
ol.renderer.Layer.prototype.manageTilePyramid = function(
frameState, tileSource, tileGrid, projection, extent, currentZ, preload,
opt_tileCallback, opt_obj) {
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, 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);
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) {
if (currentZ - z <= preload) {
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]);
}
}
if (goog.isDef(opt_tileCallback)) {
opt_tileCallback.call(opt_obj, tile);
}
} else {
tileSource.useTile(z, x, y);

View File

@@ -23,6 +23,7 @@ goog.require('ol.renderer.webgl.map.shader');
goog.require('ol.structs.Buffer');
goog.require('ol.structs.IntegerSet');
goog.require('ol.structs.LRUCache');
goog.require('ol.structs.PriorityQueue');
goog.require('ol.webgl');
goog.require('ol.webgl.WebGLContextEventType');
goog.require('ol.webgl.shader');
@@ -154,6 +155,52 @@ ol.renderer.webgl.Map = function(container, map) {
*/
this.textureCache_ = new ol.structs.LRUCache();
/**
* @private
* @type {ol.Coordinate}
*/
this.focus_ = null;
/**
* @private
* @type {ol.structs.PriorityQueue}
*/
this.tileTextureQueue_ = new ol.structs.PriorityQueue(
/**
* @param {Array} element Element.
* @return {number} Priority.
*/
goog.bind(function(element) {
var tile = /** @type {ol.Tile} */ (element[0]);
var tileCenter = /** @type {ol.Coordinate} */ (element[1]);
var tileResolution = /** @type {number} */ (element[2]);
var deltaX = tileCenter.x - this.focus_.x;
var deltaY = tileCenter.y - this.focus_.y;
return 65536 * Math.log(tileResolution) +
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
}, this),
/**
* @param {Array} element Element.
* @return {string} Key.
*/
function(element) {
return /** @type {ol.Tile} */ (element[0]).getKey();
});
/**
* @private
* @type {ol.PostRenderFunction}
*/
this.loadNextTileTexture_ = goog.bind(
function(map, frameState) {
if (!this.tileTextureQueue_.isEmpty()) {
this.tileTextureQueue_.reprioritize();
var tile =
/** @type {ol.Tile} */ (this.tileTextureQueue_.dequeue()[0]);
this.bindTileTexture(tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
}
}, this);
/**
* @private
* @type {number}
@@ -424,6 +471,14 @@ ol.renderer.webgl.Map.prototype.getShader = function(shaderObject) {
};
/**
* @return {ol.structs.PriorityQueue} Tile texture queue.
*/
ol.renderer.webgl.Map.prototype.getTileTextureQueue = function() {
return this.tileTextureQueue_;
};
/**
* @param {goog.events.Event} event Event.
* @protected
@@ -494,6 +549,8 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
return false;
}
this.focus_ = frameState.focus;
this.textureCache_.set(frameState.time.toString(), null);
++this.textureCacheFrameMarkerCount_;
@@ -584,4 +641,9 @@ ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) {
frameState.postRenderFunctions.push(goog.bind(this.expireCache_, this));
}
if (!this.tileTextureQueue_.isEmpty()) {
frameState.postRenderFunctions.push(this.loadNextTileTexture_);
frameState.animate = true;
}
};

View File

@@ -5,7 +5,6 @@ goog.provide('ol.renderer.webgl.TileLayer');
goog.require('goog.array');
goog.require('goog.object');
goog.require('goog.structs.PriorityQueue');
goog.require('goog.vec.Mat4');
goog.require('goog.vec.Vec4');
goog.require('goog.webgl');
@@ -210,10 +209,8 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
var findLoadedTiles = goog.bind(tileSource.findLoadedTiles, tileSource,
tilesToDrawByZ, getTileIfLoaded);
var tilesToLoad = new goog.structs.PriorityQueue();
var allTilesLoaded = true;
var deltaX, deltaY, priority, tile, tileCenter, tileState, x, y;
var tile, tileState, x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
@@ -223,12 +220,6 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
if (mapRenderer.isTileTextureLoaded(tile)) {
tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
continue;
} else {
tileCenter = tileGrid.getTileCoordCenter(tile.tileCoord);
deltaX = tileCenter.x - center.x;
deltaY = tileCenter.y - center.y;
priority = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
tilesToLoad.enqueue(priority, tile);
}
} else if (tileState == ol.TileState.ERROR ||
tileState == ol.TileState.EMPTY) {
@@ -263,19 +254,6 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
}, this);
}, this);
if (!tilesToLoad.isEmpty()) {
frameState.postRenderFunctions.push(
goog.partial(function(mapRenderer, tilesToLoad) {
var i, tile;
// FIXME determine a suitable number of textures to upload per frame
for (i = 0; !tilesToLoad.isEmpty() && i < 4; ++i) {
tile = /** @type {ol.Tile} */ (tilesToLoad.remove());
mapRenderer.bindTileTexture(
tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
}
}, mapRenderer, tilesToLoad));
}
if (allTilesLoaded) {
this.renderedTileRange_ = tileRange;
this.renderedFramebufferExtent_ = framebufferExtent;
@@ -288,8 +266,21 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame =
}
this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange);
var tileTextureQueue = mapRenderer.getTileTextureQueue();
this.manageTilePyramid(
frameState, tileSource, tileGrid, projection, extent, z);
frameState, tileSource, tileGrid, projection, extent, z,
tileLayer.getPreload(),
function(tile) {
if (tile.getState() == ol.TileState.LOADED &&
!mapRenderer.isTileTextureLoaded(tile) &&
!tileTextureQueue.isKeyQueued(tile.getKey())) {
tileTextureQueue.enqueue([
tile,
tileGrid.getTileCoordCenter(tile.tileCoord),
tileGrid.getResolution(tile.tileCoord.z)
]);
}
}, this);
this.scheduleExpireCache(frameState, tileSource);
var texCoordMatrix = this.texCoordMatrix;

View File

@@ -0,0 +1,288 @@
goog.provide('ol.structs.PriorityQueue');
goog.require('goog.asserts');
goog.require('goog.object');
/**
* Priority queue.
*
* The implementation is inspired from the Closure Library's Heap class and
* Python's heapq module.
*
* @see http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html
* @see http://hg.python.org/cpython/file/2.7/Lib/heapq.py
*
* @constructor
* @param {function(?): number} priorityFunction Priority function.
* @param {function(?): string} keyFunction Key function.
*/
ol.structs.PriorityQueue = function(priorityFunction, keyFunction) {
/**
* @type {function(?): number}
* @private
*/
this.priorityFunction_ = priorityFunction;
/**
* @type {function(?): string}
* @private
*/
this.keyFunction_ = keyFunction;
/**
* @type {Array}
* @private
*/
this.elements_ = [];
/**
* @type {Array.<number>}
* @private
*/
this.priorities_ = [];
/**
* @type {Object.<string, boolean>}
* @private
*/
this.queuedElements_ = {};
};
/**
* @const {number}
*/
ol.structs.PriorityQueue.DROP = Infinity;
/**
* FIXME empty desciption for jsdoc
*/
ol.structs.PriorityQueue.prototype.assertValid = function() {
var elements = this.elements_;
var priorities = this.priorities_;
var n = elements.length;
goog.asserts.assert(priorities.length == n);
var i, priority;
for (i = 0; i < (n >> 1) - 1; ++i) {
priority = priorities[i];
goog.asserts.assert(priority <= priorities[this.getLeftChildIndex_(i)]);
goog.asserts.assert(priority <= priorities[this.getRightChildIndex_(i)]);
}
};
/**
* FIXME empty description for jsdoc
*/
ol.structs.PriorityQueue.prototype.clear = function() {
this.elements_.length = 0;
this.priorities_.length = 0;
goog.object.clear(this.queuedElements_);
};
/**
* Remove and return the highest-priority element. O(log N).
* @return {*} Element.
*/
ol.structs.PriorityQueue.prototype.dequeue = function() {
var elements = this.elements_;
goog.asserts.assert(elements.length > 0);
var priorities = this.priorities_;
var element = elements[0];
if (elements.length == 1) {
elements.length = 0;
priorities.length = 0;
} else {
elements[0] = elements.pop();
priorities[0] = priorities.pop();
this.siftUp_(0);
}
var elementKey = this.keyFunction_(element);
goog.asserts.assert(elementKey in this.queuedElements_);
delete this.queuedElements_[elementKey];
return element;
};
/**
* Enqueue an element. O(log N).
* @param {*} element Element.
*/
ol.structs.PriorityQueue.prototype.enqueue = function(element) {
goog.asserts.assert(!(this.keyFunction_(element) in this.queuedElements_));
var priority = this.priorityFunction_(element);
if (priority != ol.structs.PriorityQueue.DROP) {
this.elements_.push(element);
this.priorities_.push(priority);
this.queuedElements_[this.keyFunction_(element)] = true;
this.siftDown_(0, this.elements_.length - 1);
}
};
/**
* @return {number} Count.
*/
ol.structs.PriorityQueue.prototype.getCount = function() {
return this.elements_.length;
};
/**
* 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.structs.PriorityQueue.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.structs.PriorityQueue.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.structs.PriorityQueue.prototype.getParentIndex_ = function(index) {
return (index - 1) >> 1;
};
/**
* Make this a heap. O(N).
* @private
*/
ol.structs.PriorityQueue.prototype.heapify_ = function() {
var i;
for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) {
this.siftUp_(i);
}
};
/**
* @return {boolean} Is empty.
*/
ol.structs.PriorityQueue.prototype.isEmpty = function() {
return this.elements_.length === 0;
};
/**
* @param {string} key Key.
* @return {boolean} Is key queued.
*/
ol.structs.PriorityQueue.prototype.isKeyQueued = function(key) {
return key in this.queuedElements_;
};
/**
* @param {*} element Element.
* @return {boolean} Is queued.
*/
ol.structs.PriorityQueue.prototype.isQueued = function(element) {
return this.isKeyQueued(this.keyFunction_(element));
};
/**
* @param {number} index The index of the node to move down.
* @private
*/
ol.structs.PriorityQueue.prototype.siftUp_ = function(index) {
var elements = this.elements_;
var priorities = this.priorities_;
var count = elements.length;
var element = elements[index];
var priority = priorities[index];
var startIndex = index;
while (index < (count >> 1)) {
var lIndex = this.getLeftChildIndex_(index);
var rIndex = this.getRightChildIndex_(index);
var smallerChildIndex = rIndex < count &&
priorities[rIndex] < priorities[lIndex] ?
rIndex : lIndex;
elements[index] = elements[smallerChildIndex];
priorities[index] = priorities[smallerChildIndex];
index = smallerChildIndex;
}
elements[index] = element;
priorities[index] = priority;
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.structs.PriorityQueue.prototype.siftDown_ = function(startIndex, index) {
var elements = this.elements_;
var priorities = this.priorities_;
var element = elements[index];
var priority = priorities[index];
while (index > startIndex) {
var parentIndex = this.getParentIndex_(index);
if (priorities[parentIndex] > priority) {
elements[index] = elements[parentIndex];
priorities[index] = priorities[parentIndex];
index = parentIndex;
} else {
break;
}
}
elements[index] = element;
priorities[index] = priority;
};
/**
* FIXME empty description for jsdoc
*/
ol.structs.PriorityQueue.prototype.reprioritize = function() {
var priorityFunction = this.priorityFunction_;
var elements = this.elements_;
var priorities = this.priorities_;
var index = 0;
var n = elements.length;
var element, i, priority;
for (i = 0; i < n; ++i) {
element = elements[i];
priority = priorityFunction(element);
if (priority == ol.structs.PriorityQueue.DROP) {
delete this.queuedElements_[this.keyFunction_(element)];
} else {
priorities[index] = priority;
elements[index++] = element;
}
}
elements.length = index;
priorities.length = index;
this.heapify_();
};

View File

@@ -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.<Array.<*>>}
*/
this.heap_ = [];
/**
* @private
* @type {Object.<string, boolean>}
*/
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_();
};