Merge pull request #4389 from elemoine/dynamic-params

Smooth transitions on parameter changes
This commit is contained in:
Éric Lemoine
2015-11-13 08:41:42 +01:00
13 changed files with 332 additions and 38 deletions

View File

@@ -70,6 +70,9 @@ ol.ImageTile.prototype.disposeInternal = function() {
if (this.state == ol.TileState.LOADING) {
this.unlistenImage_();
}
if (this.interimTile) {
goog.dispose(this.interimTile);
}
goog.base(this, 'disposeInternal');
};

View File

@@ -316,19 +316,29 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame =
var tmpExtent = ol.extent.createEmpty();
var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
var childTileRange, fullyLoaded, tile, tileState, x, y;
var childTileRange, fullyLoaded, tile, x, y;
var drawableTile = (
/**
* @param {!ol.Tile} tile Tile.
* @return {boolean} Tile is selected.
*/
function(tile) {
var tileState = tile.getState();
return tileState == ol.TileState.LOADED ||
tileState == ol.TileState.EMPTY ||
tileState == ol.TileState.ERROR && !useInterimTilesOnError;
});
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tile = tileSource.getTile(z, x, y, pixelRatio, projection);
tileState = tile.getState();
if (tileState == ol.TileState.LOADED ||
tileState == ol.TileState.EMPTY ||
(tileState == ol.TileState.ERROR && !useInterimTilesOnError)) {
if (!drawableTile(tile) && tile.interimTile) {
tile = tile.interimTile;
}
goog.asserts.assert(tile);
if (drawableTile(tile)) {
tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;
continue;
}
fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
if (!fullyLoaded) {
@@ -360,7 +370,7 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame =
var origin = ol.extent.getTopLeft(tileGrid.getTileCoordExtent(
[z, canvasTileRange.minX, canvasTileRange.maxY],
tmpExtent));
var currentZ, index, scale, tileCoordKey, tileExtent, tilesToDraw;
var currentZ, index, scale, tileCoordKey, tileExtent, tileState, tilesToDraw;
var ix, iy, interimTileRange, maxX, maxY;
var height, width;
for (i = 0, ii = zs.length; i < ii; ++i) {

View File

@@ -128,12 +128,19 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame =
var tmpExtent = ol.extent.createEmpty();
var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
var childTileRange, fullyLoaded, tile, tileState, x, y;
var childTileRange, drawable, fullyLoaded, tile, tileState, x, y;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
tile = tileSource.getTile(z, x, y, pixelRatio, projection);
tileState = tile.getState();
drawable = tileState == ol.TileState.LOADED ||
tileState == ol.TileState.EMPTY ||
tileState == ol.TileState.ERROR && !useInterimTilesOnError;
if (!drawable && tile.interimTile) {
tile = tile.interimTile;
}
goog.asserts.assert(tile);
tileState = tile.getState();
if (tileState == ol.TileState.LOADED) {
tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;
continue;
@@ -142,7 +149,6 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame =
!useInterimTilesOnError)) {
continue;
}
fullyLoaded = tileGrid.forEachTileCoordParentTileRange(
tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent);
if (!fullyLoaded) {

View File

@@ -247,7 +247,8 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame =
var allTilesLoaded = true;
var tmpExtent = ol.extent.createEmpty();
var tmpTileRange = new ol.TileRange(0, 0, 0, 0);
var childTileRange, fullyLoaded, tile, tileState, x, y, tileExtent;
var childTileRange, drawable, fullyLoaded, tile, tileState;
var x, y, tileExtent;
for (x = tileRange.minX; x <= tileRange.maxX; ++x) {
for (y = tileRange.minY; y <= tileRange.maxY; ++y) {
@@ -260,6 +261,14 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame =
}
}
tileState = tile.getState();
drawable = tileState == ol.TileState.LOADED ||
tileState == ol.TileState.EMPTY ||
tileState == ol.TileState.ERROR && !useInterimTilesOnError;
if (!drawable && tile.interimTile) {
tile = tile.interimTile;
}
goog.asserts.assert(tile);
tileState = tile.getState();
if (tileState == ol.TileState.LOADED) {
if (mapRenderer.isTileTextureLoaded(tile)) {
tilesToDrawByZ[z][ol.tilecoord.toString(tile.tileCoord)] = tile;

View File

@@ -162,6 +162,36 @@ ol.source.TileImage.prototype.getTileCacheForProjection = function(projection) {
};
/**
* @param {number} z Tile coordinate z.
* @param {number} x Tile coordinate x.
* @param {number} y Tile coordinate y.
* @param {number} pixelRatio Pixel ratio.
* @param {ol.proj.Projection} projection Projection.
* @param {string} key The key set on the tile.
* @return {ol.Tile} Tile.
* @private
*/
ol.source.TileImage.prototype.createTile_ =
function(z, x, y, pixelRatio, projection, key) {
var tileCoord = [z, x, y];
var urlTileCoord = this.getTileCoordForTileUrlFunction(
tileCoord, projection);
var tileUrl = urlTileCoord ?
this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined;
var tile = new this.tileClass(
tileCoord,
tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY,
tileUrl !== undefined ? tileUrl : '',
this.crossOrigin,
this.tileLoadFunction);
tile.key = key;
goog.events.listen(tile, goog.events.EventType.CHANGE,
this.handleTileChange, false, this);
return tile;
};
/**
* @inheritDoc
*/
@@ -176,7 +206,7 @@ ol.source.TileImage.prototype.getTile =
var cache = this.getTileCacheForProjection(projection);
var tileCoordKey = this.getKeyZXY(z, x, y);
if (cache.containsKey(tileCoordKey)) {
return /** @type {!ol.Tile} */(cache.get(tileCoordKey));
return /** @type {!ol.Tile} */ (cache.get(tileCoordKey));
} else {
var sourceProjection = this.getProjection();
var sourceTileGrid = this.getTileGridForProjection(sourceProjection);
@@ -208,28 +238,45 @@ ol.source.TileImage.prototype.getTile =
*/
ol.source.TileImage.prototype.getTileInternal =
function(z, x, y, pixelRatio, projection) {
var /** @type {ol.Tile} */ tile = null;
var tileCoordKey = this.getKeyZXY(z, x, y);
if (this.tileCache.containsKey(tileCoordKey)) {
return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
} else {
var paramsKey = this.getKeyParams();
if (!this.tileCache.containsKey(tileCoordKey)) {
goog.asserts.assert(projection, 'argument projection is truthy');
var tileCoord = [z, x, y];
var urlTileCoord = this.getTileCoordForTileUrlFunction(
tileCoord, projection);
var tileUrl = !urlTileCoord ? undefined :
this.tileUrlFunction(urlTileCoord, pixelRatio, projection);
var tile = new this.tileClass(
tileCoord,
tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY,
tileUrl !== undefined ? tileUrl : '',
this.crossOrigin,
this.tileLoadFunction);
goog.events.listen(tile, goog.events.EventType.CHANGE,
this.handleTileChange, false, this);
tile = this.createTile_(z, x, y, pixelRatio, projection, paramsKey);
this.tileCache.set(tileCoordKey, tile);
return tile;
} else {
tile = /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
if (tile.key != paramsKey) {
// The source's params changed. If the tile has an interim tile and if we
// 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.
var /** @type {ol.Tile} */ interimTile = tile;
if (tile.interimTile && tile.interimTile.key == paramsKey) {
goog.asserts.assert(tile.interimTile.getState() == ol.TileState.LOADED);
goog.asserts.assert(tile.interimTile.interimTile === null);
tile = tile.interimTile;
if (interimTile.getState() == ol.TileState.LOADED) {
tile.interimTile = interimTile;
}
} else {
tile = this.createTile_(z, x, y, pixelRatio, projection, paramsKey);
if (interimTile.getState() == ol.TileState.LOADED) {
tile.interimTile = interimTile;
} else if (interimTile.interimTile &&
interimTile.interimTile.getState() == ol.TileState.LOADED) {
tile.interimTile = interimTile.interimTile;
interimTile.interimTile = null;
}
}
if (tile.interimTile) {
tile.interimTile.interimTile = null;
}
this.tileCache.replace(tileCoordKey, tile);
}
}
goog.asserts.assert(tile);
return tile;
};

View File

@@ -156,6 +156,17 @@ ol.source.Tile.prototype.getGutter = function() {
};
/**
* Return the "parameters" key, a string composed of the source's
* parameters/dimensions.
* @return {string} The parameters key.
* @protected
*/
ol.source.Tile.prototype.getKeyParams = function() {
return '';
};
/**
* @param {number} z Z.
* @param {number} x X.

View File

@@ -61,8 +61,8 @@ ol.source.WMTS = function(options) {
* @private
* @type {string}
*/
this.coordKeyPrefix_ = '';
this.resetCoordKeyPrefix_();
this.dimensionsKey_ = '';
this.resetDimensionsKey_();
/**
* @private
@@ -218,8 +218,8 @@ ol.source.WMTS.prototype.getFormat = function() {
/**
* @inheritDoc
*/
ol.source.WMTS.prototype.getKeyZXY = function(z, x, y) {
return this.coordKeyPrefix_ + goog.base(this, 'getKeyZXY', z, x, y);
ol.source.WMTS.prototype.getKeyParams = function() {
return this.dimensionsKey_;
};
@@ -276,13 +276,13 @@ ol.source.WMTS.prototype.getVersion = function() {
/**
* @private
*/
ol.source.WMTS.prototype.resetCoordKeyPrefix_ = function() {
ol.source.WMTS.prototype.resetDimensionsKey_ = function() {
var i = 0;
var res = [];
for (var key in this.dimensions_) {
res[i++] = key + '-' + this.dimensions_[key];
}
this.coordKeyPrefix_ = res.join('/');
this.dimensionsKey_ = res.join('/');
};
@@ -293,7 +293,7 @@ ol.source.WMTS.prototype.resetCoordKeyPrefix_ = function() {
*/
ol.source.WMTS.prototype.updateDimensions = function(dimensions) {
goog.object.extend(this.dimensions_, dimensions);
this.resetCoordKeyPrefix_();
this.resetDimensionsKey_();
this.changed();
};

View File

@@ -226,6 +226,16 @@ ol.structs.LRUCache.prototype.pop = function() {
};
/**
* @param {string} key Key.
* @param {T} value Value.
*/
ol.structs.LRUCache.prototype.replace = function(key, value) {
this.get(key); // update `newest_`
this.entries_[key].value_ = value;
};
/**
* @param {string} key Key.
* @param {T} value Value.

View File

@@ -44,6 +44,22 @@ ol.Tile = function(tileCoord, state) {
*/
this.state = state;
/**
* An "interim" tile for this tile. The interim tile may be used while this
* one is loading, for "smooth" transitions when changing params/dimensions
* on the source.
* @type {ol.Tile}
*/
this.interimTile = null;
/**
* A key assigned to the tile. This is used by the tile source to determine
* if this tile can effectively be used, or if a new tile should be created
* and this one be used as an interim tile for this new tile.
* @type {string}
*/
this.key = '';
};
goog.inherits(ol.Tile, goog.events.EventTarget);