New tile image cache and tile queue improvements
We now reuse tile images by maintaining a cache of image elements with a simplified LRU expiry policy (by order, not by timestamp). The tile queue is bypassed for images that are available in the cache, so they can be rendered immediately. And the tile queue itself loads more than just one image at a time now (2 per layer url).
This commit is contained in:
@@ -201,13 +201,13 @@ OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: drawTileFromQueue
|
||||
* Draws the first tile from the tileQueue, and unqueues that tile
|
||||
* Method: drawTilesFromQueue
|
||||
* Draws tiles from the tileQueue, and unqueues the tiles
|
||||
*/
|
||||
drawTileFromQueue: function() {
|
||||
drawTilesFromQueue: function() {
|
||||
// don't start working on the queue before we have a url from initLayer
|
||||
if (this.url) {
|
||||
OpenLayers.Layer.XYZ.prototype.drawTileFromQueue.apply(this, arguments);
|
||||
OpenLayers.Layer.XYZ.prototype.drawTilesFromQueue.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
|
||||
|
||||
/**
|
||||
* Property: tileQueueId
|
||||
* {Number} The id of the <drawTileFromQueue> animation.
|
||||
* {Number} The id of the <drawTilesFromQueue> animation.
|
||||
*/
|
||||
tileQueueId: null,
|
||||
|
||||
@@ -240,6 +240,26 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
|
||||
* should not be zero.
|
||||
*/
|
||||
className: null,
|
||||
|
||||
/**
|
||||
* Property: tileCache
|
||||
* {Object} Cached image elements, keyed by URL.
|
||||
*/
|
||||
tileCache: null,
|
||||
|
||||
/**
|
||||
* Property: tileCacheIndex
|
||||
* {Array<String>} URLs of cached tiles; first entry is least recently
|
||||
* used.
|
||||
*/
|
||||
tileCacheIndex: null,
|
||||
|
||||
/**
|
||||
* APIProperty: tileCacheSize
|
||||
* {Number} Number of image elements to keep referenced for fast reuse.
|
||||
* Default is 128 per layer.
|
||||
*/
|
||||
tileCacheSize: 128,
|
||||
|
||||
/**
|
||||
* Register a listener for a particular event with the following syntax:
|
||||
@@ -309,6 +329,8 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
|
||||
arguments);
|
||||
this.grid = [];
|
||||
this.tileQueue = [];
|
||||
this.tileCache = {};
|
||||
this.tileCacheIndex = [];
|
||||
this._removeBackBuffer = OpenLayers.Function.bind(this.removeBackBuffer, this);
|
||||
|
||||
if (this.removeBackBufferDelay === null) {
|
||||
@@ -370,6 +392,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
|
||||
|
||||
this.grid = null;
|
||||
this.tileSize = null;
|
||||
this.tileCache = null;
|
||||
OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
|
||||
},
|
||||
|
||||
@@ -585,37 +608,79 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
|
||||
*/
|
||||
queueTileDraw: function(evt) {
|
||||
var tile = evt.object;
|
||||
if (!~OpenLayers.Util.indexOf(this.tileQueue, tile)) {
|
||||
// queue only if not in queue already
|
||||
this.tileQueue.push(tile);
|
||||
var queued = false;
|
||||
if (this.async || !this.url ||
|
||||
!this.tileCache[this.getURL(tile.bounds)]) {
|
||||
// queue only if not in tileCache already
|
||||
if (!~OpenLayers.Util.indexOf(this.tileQueue, tile)) {
|
||||
// add to queue only if not in queue already
|
||||
this.tileQueue.push(tile);
|
||||
}
|
||||
queued = true;
|
||||
if (!this.tileQueueId) {
|
||||
this.tileQueueId = OpenLayers.Animation.start(
|
||||
OpenLayers.Function.bind(this.drawTilesFromQueue, this),
|
||||
null, this.div
|
||||
);
|
||||
}
|
||||
}
|
||||
if (!this.tileQueueId) {
|
||||
this.tileQueueId = OpenLayers.Animation.start(
|
||||
OpenLayers.Function.bind(this.drawTileFromQueue, this),
|
||||
null, this.div
|
||||
);
|
||||
}
|
||||
return false;
|
||||
return !queued;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: drawTileFromQueue
|
||||
* Draws the first tile from the tileQueue, and unqueues that tile
|
||||
* Method: drawTilesFromQueue
|
||||
* Draws tiles from the tileQueue, and unqueues the tiles
|
||||
*/
|
||||
drawTileFromQueue: function() {
|
||||
if (this.tileQueue.length === 0) {
|
||||
this.clearTileQueue();
|
||||
} else {
|
||||
drawTilesFromQueue: function() {
|
||||
var numUrls = OpenLayers.Util.isArray(this.url) ? this.url.length : 1;
|
||||
//TODO instead of using 2 * urls, we could keep track of the hosts used
|
||||
// by all grid layers, and use a number that just saturates the number
|
||||
// of parallel requests the browser can send
|
||||
while (this.numLoadingTiles < 2 * numUrls) {
|
||||
if (this.tileQueue.length === 0) {
|
||||
this.clearTileQueue();
|
||||
break;
|
||||
}
|
||||
this.tileQueue.shift().draw(true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: manageTileCache
|
||||
* Adds, updates, removes and fetches cache entries.
|
||||
*
|
||||
* Parameters:
|
||||
* evt - {Object} Listener argument of the tile's loadstart event
|
||||
*/
|
||||
manageTileCache: function(evt) {
|
||||
var tile = evt.object;
|
||||
if (this.tileCache[tile.url]) {
|
||||
tile.imgDiv = this.tileCache[tile.url];
|
||||
OpenLayers.Util.removeItem(this.tileCacheIndex, tile.url);
|
||||
this.tileCacheIndex.push(tile.url);
|
||||
tile.positionTile();
|
||||
this.div.appendChild(tile.imgDiv);
|
||||
} else {
|
||||
tile.events.register('loadend', this, function loadend() {
|
||||
tile.events.unregister('loadend', this, loadend);
|
||||
if (!this.tileCache[tile.url]) {
|
||||
if (this.tileCacheIndex.length >= this.tileCacheSize) {
|
||||
delete this.tileCache[this.tileCacheIndex[0]];
|
||||
this.tileCacheIndex.shift();
|
||||
}
|
||||
this.tileCache[tile.url] = tile.imgDiv;
|
||||
this.tileCacheIndex.push(tile.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Method: clearTileQueue
|
||||
* Clears the animation queue
|
||||
*/
|
||||
clearTileQueue: function() {
|
||||
OpenLayers.Animation.stop(this.tileQueueId);
|
||||
window.clearInterval(this.tileQueueId);
|
||||
this.tileQueueId = null;
|
||||
this.tileQueue = [];
|
||||
},
|
||||
@@ -1102,7 +1167,11 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
|
||||
var tile = new this.tileClass(
|
||||
this, position, bounds, null, this.tileSize, this.tileOptions
|
||||
);
|
||||
tile.events.register("beforedraw", this, this.queueTileDraw);
|
||||
tile.events.on({
|
||||
beforedraw: this.queueTileDraw,
|
||||
loadstart: this.manageTileCache,
|
||||
scope: this
|
||||
});
|
||||
return tile;
|
||||
},
|
||||
|
||||
|
||||
@@ -100,6 +100,39 @@
|
||||
map.destroy();
|
||||
}
|
||||
|
||||
function test_manageTileCache(t) {
|
||||
t.plan(9);
|
||||
|
||||
var map = new OpenLayers.Map('map');
|
||||
layer = new OpenLayers.Layer.WMS(name, "../../img/blank.gif", params, {
|
||||
tileCacheSize: 12
|
||||
});
|
||||
map.addLayer(layer);
|
||||
map.setCenter([16, 48], 9);
|
||||
|
||||
var firstInCache, sharedTile;
|
||||
t.delay_call(2, function() {
|
||||
t.eq(layer.tileCacheIndex.length, 12, "tiles cached");
|
||||
t.ok(~OpenLayers.Util.indexOf(layer.tileCacheIndex, layer.grid[1][2].url), "tile found in cache");
|
||||
t.ok(layer.tileCache[layer.grid[1][2].url] === layer.grid[1][2].imgDiv, "correct object cached");
|
||||
firstInCache = layer.tileCache[layer.tileCacheIndex[0]];
|
||||
sharedTile = layer.tileCache[layer.tileCacheIndex[11]];
|
||||
map.setCenter([17, 47]);
|
||||
});
|
||||
t.delay_call(4, function() {
|
||||
t.eq(layer.tileCacheIndex.length, 12, "tiles cached");
|
||||
t.ok(layer.tileCache[layer.grid[1][2].url] === layer.grid[1][2].imgDiv, "correct object cached");
|
||||
t.ok(!(firstInCache.getAttribute("src") in layer.tileCache), "old tile discarded");
|
||||
t.ok(sharedTile.getAttribute("src") in layer.tileCache, "shared tile still in cache");
|
||||
firstInCache = layer.tileCache[layer.tileCacheIndex[0]];
|
||||
map.setCenter([16, 48]);
|
||||
});
|
||||
t.delay_call(6, function() {
|
||||
t.ok(!(firstInCache.getAttribute("src") in layer.tileCache), "old tile discarded");
|
||||
t.ok(sharedTile.getAttribute("src") in layer.tileCache, "shared tile still in cache");
|
||||
});
|
||||
}
|
||||
|
||||
function test_queueTileDraw(t) {
|
||||
t.plan(3);
|
||||
OpenLayers.Layer.Grid.prototype.queueTileDraw = origQueueTileDraw;
|
||||
|
||||
Reference in New Issue
Block a user