Render older loaded tiles while waiting for new tiles
This commit is contained in:
@@ -152,3 +152,66 @@ ol.ImageTile.prototype.unlistenImage_ = function() {
|
|||||||
this.imageListenerKeys_.forEach(ol.events.unlistenByKey);
|
this.imageListenerKeys_.forEach(ol.events.unlistenByKey);
|
||||||
this.imageListenerKeys_ = null;
|
this.imageListenerKeys_ = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the interim tile most suitable for rendering using the chain of interim tiles.
|
||||||
|
* This corresponds to the most recent tile that has been loaded, if no such
|
||||||
|
* tile exists, the original tile is returned.
|
||||||
|
* @return {!ol.Tile} Best tile for rendering.
|
||||||
|
*/
|
||||||
|
ol.ImageTile.prototype.getInterimTile = function() {
|
||||||
|
if (!this.interimTile) {
|
||||||
|
//empty chain
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
var tile = this.interimTile;
|
||||||
|
|
||||||
|
//find the first loaded tile and return it. Since the chain is sorted in decreasing
|
||||||
|
//order of creation time, there is no need to search the remaineder of the list (all those
|
||||||
|
//tiles correspond to older requests and will be cleaned up by refreshInterimChain)
|
||||||
|
do {
|
||||||
|
if (tile.getState() == ol.Tile.State.LOADED) {
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
tile = tile.interimTile;
|
||||||
|
} while (tile);
|
||||||
|
|
||||||
|
//we can not find a better tile
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goes through the chain of interim tiles starting and discards
|
||||||
|
* sections of the chain that are no longer relevant.
|
||||||
|
* @return {void}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.ImageTile.prototype.refreshInterimChain = function() {
|
||||||
|
if (!this.interimTile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tile = this.interimTile;
|
||||||
|
var prev = this;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (tile.getState() == ol.Tile.State.LOADED) {
|
||||||
|
//we have a loaded tile, we can discard the rest of the list
|
||||||
|
//we would could abort any LOADING tile request
|
||||||
|
//older than this tile (i.e. any LOADING tile following this entry in the chain)
|
||||||
|
tile.interimTile = null;
|
||||||
|
break;
|
||||||
|
} else if (tile.getState() == ol.Tile.State.LOADING) {
|
||||||
|
//keep this LOADING tile any loaded tiles later in the chain are
|
||||||
|
//older than this tile, so we're still interested in the request
|
||||||
|
prev = tile;
|
||||||
|
} else if (tile.getState() == ol.Tile.State.IDLE) {
|
||||||
|
//the head of the list is the most current tile, we don't need
|
||||||
|
//to start any other requests for this chain
|
||||||
|
prev.interimTile = tile.interimTile;
|
||||||
|
} else {
|
||||||
|
prev = tile;
|
||||||
|
}
|
||||||
|
tile = prev.interimTile;
|
||||||
|
} while (tile);
|
||||||
|
};
|
||||||
|
|||||||
@@ -139,8 +139,8 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(
|
|||||||
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||||
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||||
tile = tileSource.getTile(z, x, y, pixelRatio, projection);
|
tile = tileSource.getTile(z, x, y, pixelRatio, projection);
|
||||||
if (!drawableTile(tile) && tile.interimTile) {
|
if (!drawableTile(tile)) {
|
||||||
tile = tile.interimTile;
|
tile = tile.getInterimTile();
|
||||||
}
|
}
|
||||||
if (drawableTile(tile)) {
|
if (drawableTile(tile)) {
|
||||||
tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
|
tilesToDrawByZ[z][tile.tileCoord.toString()] = tile;
|
||||||
|
|||||||
@@ -252,8 +252,8 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame = function(frameState, layerS
|
|||||||
drawable = tileState == ol.Tile.State.LOADED ||
|
drawable = tileState == ol.Tile.State.LOADED ||
|
||||||
tileState == ol.Tile.State.EMPTY ||
|
tileState == ol.Tile.State.EMPTY ||
|
||||||
tileState == ol.Tile.State.ERROR && !useInterimTilesOnError;
|
tileState == ol.Tile.State.ERROR && !useInterimTilesOnError;
|
||||||
if (!drawable && tile.interimTile) {
|
if (!drawable) {
|
||||||
tile = tile.interimTile;
|
tile = tile.getInterimTile();
|
||||||
}
|
}
|
||||||
tileState = tile.getState();
|
tileState = tile.getState();
|
||||||
if (tileState == ol.Tile.State.LOADED) {
|
if (tileState == ol.Tile.State.LOADED) {
|
||||||
|
|||||||
@@ -302,26 +302,16 @@ ol.source.TileImage.prototype.getTileInternal = function(z, x, y, pixelRatio, pr
|
|||||||
// can use it then we use it. Otherwise we create a new tile. In both
|
// can use it then we use it. Otherwise we create a new tile. In both
|
||||||
// cases we attempt to assign an interim tile to the new tile.
|
// cases we attempt to assign an interim tile to the new tile.
|
||||||
var /** @type {ol.Tile} */ interimTile = tile;
|
var /** @type {ol.Tile} */ interimTile = tile;
|
||||||
if (tile.interimTile && tile.interimTile.key == key) {
|
|
||||||
ol.DEBUG && console.assert(tile.interimTile.getState() == ol.Tile.State.LOADED);
|
|
||||||
ol.DEBUG && console.assert(tile.interimTile.interimTile === null);
|
|
||||||
tile = tile.interimTile;
|
|
||||||
if (interimTile.getState() == ol.Tile.State.LOADED) {
|
|
||||||
tile.interimTile = interimTile;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tile = this.createTile_(z, x, y, pixelRatio, projection, key);
|
tile = this.createTile_(z, x, y, pixelRatio, projection, key);
|
||||||
if (interimTile.getState() == ol.Tile.State.LOADED) {
|
|
||||||
tile.interimTile = interimTile;
|
//make the new tile the head of the list,
|
||||||
} else if (interimTile.interimTile &&
|
if (interimTile.getState() == ol.Tile.State.IDLE) {
|
||||||
interimTile.interimTile.getState() == ol.Tile.State.LOADED) {
|
//the old tile hasn't begun loading yet, and is now outdated, so we can simply discard it
|
||||||
tile.interimTile = interimTile.interimTile;
|
tile.interimTile = interimTile.interimTile;
|
||||||
interimTile.interimTile = null;
|
} else {
|
||||||
}
|
tile.interimTile = interimTile;
|
||||||
}
|
|
||||||
if (tile.interimTile) {
|
|
||||||
tile.interimTile.interimTile = null;
|
|
||||||
}
|
}
|
||||||
|
tile.refreshInterimChain();
|
||||||
this.tileCache.replace(tileCoordKey, tile);
|
this.tileCache.replace(tileCoordKey, tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,17 @@ ol.Tile.prototype.getKey = function() {
|
|||||||
return this.key + '/' + this.tileCoord;
|
return this.key + '/' + this.tileCoord;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the interim tile if it exists, otherwise returns the tile itself.
|
||||||
|
* @return {!ol.Tile} The interim tile, or the tile itself
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ol.Tile.prototype.getInterimTile = function() {
|
||||||
|
if (this.interimTile) {
|
||||||
|
return this.interimTile;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tile coordinate for this tile.
|
* Get the tile coordinate for this tile.
|
||||||
|
|||||||
@@ -60,7 +60,73 @@ describe('ol.ImageTile', function() {
|
|||||||
|
|
||||||
tile.load();
|
tile.load();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('interimChain', function() {
|
||||||
|
var head, renderTile;
|
||||||
|
beforeEach(function() {
|
||||||
|
var tileCoord = [0, 0, 0];
|
||||||
|
var src = 'spec/ol/data/osm-0-0-0.png';
|
||||||
|
var tileLoadFunction = ol.source.Image.defaultImageLoadFunction;
|
||||||
|
head = new ol.ImageTile(tileCoord, ol.Tile.State.IDLE, src, null, tileLoadFunction);
|
||||||
|
ol.getUid(head);
|
||||||
|
|
||||||
|
var addToChain = function(tile, state) {
|
||||||
|
var next = new ol.ImageTile(tileCoord, state, src, null, tileLoadFunction);
|
||||||
|
ol.getUid(next);
|
||||||
|
tile.interimTile = next;
|
||||||
|
return next;
|
||||||
|
};
|
||||||
|
var tail = addToChain(head,ol.Tile.State.IDLE); //discard, deprecated by head
|
||||||
|
tail = addToChain(tail,ol.Tile.State.LOADING); //keep, request already going
|
||||||
|
tail = addToChain(tail,ol.Tile.State.IDLE); //discard, deprecated by head
|
||||||
|
tail = addToChain(tail,ol.Tile.State.LOADED); //keep, use for rendering
|
||||||
|
renderTile = tail; //store this tile for later tests
|
||||||
|
tail = addToChain(tail,ol.Tile.State.IDLE); //rest of list outdated by tile above
|
||||||
|
tail = addToChain(tail,ol.Tile.State.LOADED);
|
||||||
|
tail = addToChain(tail,ol.Tile.State.LOADING);
|
||||||
|
tail = addToChain(tail,ol.Tile.State.LOADED);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('shrinks tile chain correctly', function(done) {
|
||||||
|
var chainLength = function(tile) {
|
||||||
|
var c = 0;
|
||||||
|
while (tile) {
|
||||||
|
++c;
|
||||||
|
tile = tile.interimTile;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(chainLength(head)).to.be(9);
|
||||||
|
head.refreshInterimChain();
|
||||||
|
expect(chainLength(head)).to.be(3);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gives the right tile to render', function(done) {
|
||||||
|
expect(head.getInterimTile()).to.be(renderTile);
|
||||||
|
head.refreshInterimChain();
|
||||||
|
expect(head.getInterimTile()).to.be(renderTile);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('discards everything after the render tile', function(done) {
|
||||||
|
head.refreshInterimChain();
|
||||||
|
expect(renderTile.interimTile).to.be(null);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves order of tiles', function(done) {
|
||||||
|
head.refreshInterimChain();
|
||||||
|
while (head.interimTile !== null) {
|
||||||
|
//use property of ol.getUid returning increasing id's.
|
||||||
|
expect(ol.getUid(head) < ol.getUid(head.interimTile));
|
||||||
|
head = head.interimTile;
|
||||||
|
}
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user