From 299173194c93d0ddab0989a703fda9566cff3600 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 25 Sep 2012 20:06:20 +0200 Subject: [PATCH] First pass at alt-tile rendering for dom renderer --- src/ol/renderer/dom/tilelayer.js | 178 +++++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 30 deletions(-) diff --git a/src/ol/renderer/dom/tilelayer.js b/src/ol/renderer/dom/tilelayer.js index 560890da9d..aeba996526 100644 --- a/src/ol/renderer/dom/tilelayer.js +++ b/src/ol/renderer/dom/tilelayer.js @@ -1,6 +1,10 @@ goog.provide('ol.renderer.dom.TileLayer'); +goog.require('goog.asserts'); goog.require('goog.dom'); +goog.require('goog.events'); +goog.require('goog.events.Event'); +goog.require('goog.events.EventType'); goog.require('ol.Coordinate'); goog.require('ol.Extent'); goog.require('ol.renderer.dom.Layer'); @@ -28,6 +32,26 @@ ol.renderer.dom.TileLayer = function(mapRenderer, tileLayer, target) { * @private */ this.renderedMapResolution_ = undefined; + + /** + * @type {number|undefined} + * @private + */ + this.renderedTileRange_ = undefined; + + /** + * @type {number|undefined} + * @private + */ + this.renderedZ_ = undefined; + + /** + * Map of tile keys loading at the currently rendered z. + * @type {Object.} + * @private + */ + this.loadingTiles_ = {}; + }; goog.inherits(ol.renderer.dom.TileLayer, ol.renderer.dom.Layer); @@ -63,11 +87,10 @@ ol.renderer.dom.TileLayer.prototype.getTileOffset_ = function(z, resolution) { /** * Get rid of tiles outside the rendered extent. * @private - * @param {ol.TileRange} tileRange Tile range. - * @param {number} z Z. */ -ol.renderer.dom.TileLayer.prototype.removeInvisibleTiles_ = function( - tileRange, z) { +ol.renderer.dom.TileLayer.prototype.removeInvisibleTiles_ = function() { + var tileRange = this.renderedTileRange_; + var z = this.renderedZ_; var key, tileCoord, prune, tile; for (key in this.renderedTiles_) { tileCoord = ol.TileCoord.createFromString(key); @@ -85,6 +108,30 @@ ol.renderer.dom.TileLayer.prototype.removeInvisibleTiles_ = function( }; +/** + * @param {goog.events.Event} event Tile change event. + * @private + */ +ol.renderer.dom.TileLayer.prototype.handleTileChange_ = function(event) { + var tile = event.target; + goog.asserts.assert(tile.getState() == ol.TileState.LOADED); + var tileCoord = tile.tileCoord; + if (tileCoord.z === this.renderedZ_) { + var key = tileCoord.toString(); + delete this.loadingTiles_[key]; + // determine if we've fully loaded this zoom level + var loaded = true; + for (key in this.loadingTiles_) { + loaded = false; + break; + } + if (loaded) { + this.removeInvisibleTiles_(); + } + } +}; + + /** * @inheritDoc */ @@ -101,48 +148,119 @@ ol.renderer.dom.TileLayer.prototype.render = function() { var tileStore = tileLayer.getStore(); var tileGrid = tileStore.getTileGrid(); - if (mapResolution != this.renderedMapResolution_) { - this.renderedTiles_ = {}; - goog.dom.removeChildren(this.target); - } - // z represents the "best" resolution var z = tileGrid.getZForResolution(mapResolution); + if (z != this.renderedZ_) { + // no longer wait for previously loading tiles + this.loadingTiles_ = {}; + } + + /** + * @type {Object.>} + */ + var tilesToDrawByZ = {}; + tilesToDrawByZ[z] = {}; + var tileRange = tileGrid.getTileRangeForExtentAndResolution(mapExtent, mapResolution); - var tileOffset = this.getTileOffset_(z, mapResolution); + + + // first pass through the tile range to determine all the tiles needed + var allTilesLoaded = true; + tileRange.forEachTileCoord(z, function(tileCoord) { + + var tile = tileStore.getTile(tileCoord); + if (goog.isNull(tile)) { + // we're outside the store's extent, continue + return; + } + + var key = tile.tileCoord.toString(); + if (tile.getState() == ol.TileState.LOADED) { + tilesToDrawByZ[z][key] = tile; + return; + } else { + if (!(key in this.loadingTiles_)) { + goog.events.listen(tile, goog.events.EventType.CHANGE, + this.handleTileChange_, false, this); + this.loadingTiles_[key] = tile; + allTilesLoaded = false; + tile.load(); + } + // TODO: only append after load? + tilesToDrawByZ[z][key] = tile; + } + + /** + * Look for already loaded tiles that can serve as placeholders. + */ + // FIXME this could be more efficient about filling partial holes + tileGrid.forEachTileCoordParentTileRange( + tileCoord, + function(altZ, altTileRange) { + var fullyCovered = true; + altTileRange.forEachTileCoord(altZ, function(altTileCoord) { + var tileKey = altTileCoord.toString(); + if (tilesToDrawByZ[altZ] && tilesToDrawByZ[altZ][tileKey]) { + return; + } + var altTile = tileStore.getTile(altTileCoord); + if (!goog.isNull(altTile) && + altTile.getState() == ol.TileState.LOADED) { + if (!(altZ in tilesToDrawByZ)) { + tilesToDrawByZ[altZ] = {}; + } + tilesToDrawByZ[altZ][tileKey] = altTile; + } else { + fullyCovered = false; + } + }); + return fullyCovered; + }); + + }, this); + + /** @type {Array.} */ + var zs = goog.object.getKeys(tilesToDrawByZ); + goog.array.sort(zs); var fragment = document.createDocumentFragment(); - - var key, tile, pixelBounds, img, newTiles = false; - tileRange.forEachTileCoord(z, function(tileCoord) { - key = tileCoord.toString(); - tile = this.renderedTiles_[key]; - if (!goog.isDef(tile)) { - tile = tileStore.getTile(tileCoord); - if (goog.isNull(tile)) { - } else { - tile.load(); + var newTiles = false; + for (var i = 0, ii = zs.length; i < ii; ++i) { + var tileZ = zs[i]; + var tilesToDraw = tilesToDrawByZ[tileZ]; + var tileOffset = this.getTileOffset_(tileZ, mapResolution); + for (var key in tilesToDraw) { + var tile = tilesToDraw[key]; + var tileCoord = tile.tileCoord; + var pixelBounds = tileGrid.getPixelBoundsForTileCoordAndResolution( + tileCoord, mapResolution); + var img = tile.getImage(this); + var style = img.style; + // TODO: use translate method + style.left = (pixelBounds.minX - tileOffset.x) + 'px'; + style.top = (-pixelBounds.maxY - tileOffset.y) + 'px'; + style.width = pixelBounds.getWidth() + 'px'; + style.height = pixelBounds.getHeight() + 'px'; + if (!(key in this.renderedTiles_)) { this.renderedTiles_[key] = tile; - pixelBounds = tileGrid.getPixelBoundsForTileCoordAndResolution( - tileCoord, mapResolution); - img = tile.getImage(this); - img.style.position = 'absolute'; - img.style.left = (pixelBounds.minX - tileOffset.x) + 'px'; - img.style.top = (-pixelBounds.maxY - tileOffset.y) + 'px'; - img.style.width = pixelBounds.getWidth() + 'px'; - img.style.height = pixelBounds.getHeight() + 'px'; + style.position = 'absolute'; goog.dom.appendChild(fragment, img); newTiles = true; } } - }, this); + } if (newTiles) { goog.dom.appendChild(this.target, fragment); } - this.removeInvisibleTiles_(tileRange, z); + if (allTilesLoaded) { + this.removeInvisibleTiles_(); + } + + this.renderedTileRange_ = tileRange; + this.renderedZ_ = z; this.renderedMapResolution_ = mapResolution; };