From 4911320eef001bd3e7368f842bf1ccdd38a15b6a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Jul 2012 18:36:34 -0600 Subject: [PATCH] Rework DOM composite renderer. --- src/ol/renderer/Composite.js | 169 +++++++++---- src/ol/renderer/LayerRenderer.js | 16 +- src/ol/renderer/TileLayerRenderer.js | 355 +++++++++++++++------------ 3 files changed, 317 insertions(+), 223 deletions(-) diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index 6ec58ffe7d..602d0c86fa 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -33,31 +33,27 @@ ol.renderer.Composite = function(container) { */ this.buffer_ = 128; - var containerSize = this.getContainerSize(); - var width = containerSize.width + (2 * this.buffer_); - var height = containerSize.height + (2 * this.buffer_); - - var target = goog.dom.createDom('div', { - 'class': 'ol-renderer-composite', - 'style': 'width:' + width + 'px;height:' + height + 'px;' + - 'top:-' + this.buffer_ + 'px;left:-' + this.buffer_ + 'px;' + - 'position:absolute' - }); - goog.dom.appendChild(container, target); - /** * @type {Element} * @private */ - this.target_ = target; + this.target_ = null; /** - * The cumulative offset from the original position of the target element. + * The current top left corner location of the target element (map coords). + * + * @type {ol.Loc} + * @private + */ + this.targetOrigin_ = null; + + /** + * The pixel offset of the target element with respect to its container. * * @type {goog.math.Coordinate} * @private */ - this.targetOffset_ = new goog.math.Coordinate(0, 0); + this.targetOffset_ = null; /** * @type {Object} @@ -68,19 +64,6 @@ ol.renderer.Composite = function(container) { }; goog.inherits(ol.renderer.Composite, ol.renderer.MapRenderer); -/** - * Adjust the position of the renderer target by some offset. - * - * @param {number} x The x-offset (in pixel space) - * @param {number} y The y-offset (in pixel space) - */ -ol.renderer.Composite.prototype.shiftTarget = function(x, y) { - var newX = this.targetOffset_.x + x; - var newY = this.targetOffset_.y + y; - this.targetOffset_ = new goog.math.Coordinate(newX, newY); - goog.style.setPosition(this.target_, newX-this.buffer_, newY-this.buffer_); -}; - /** * @param {Array.} layers * @param {ol.Loc} center @@ -88,46 +71,124 @@ ol.renderer.Composite.prototype.shiftTarget = function(x, y) { * @param {boolean} animate */ ol.renderer.Composite.prototype.draw = function(layers, center, resolution, animate) { + if (goog.isNull(this.target_)) { + // first rendering + this.createTarget_(center, resolution); + } + // TODO: deal with layer order and removal if (this.renderedResolution_) { if (resolution !== this.renderedResolution_) { // TODO: apply transition to old target - this.shiftTarget(0, 0); + this.resetTarget_(center, resolution); } } this.renderedResolution_ = resolution; // shift target element to account for center change if (this.renderedCenter_) { - this.shiftTarget( - Math.round((this.renderedCenter_.getX() - center.getX()) / resolution), - Math.round((center.getY() - this.renderedCenter_.getY()) / resolution) - ); + this.shiftTarget_(center, resolution); } this.renderedCenter_ = center; // update each layer renderer - var renderer, container; - + var renderer; for (var i=0, ii=layers.length; i} - */ -ol.renderer.TileLayerRenderer.prototype.getPreferredResAndZ_ = function(resolution) { - var minDiff = Number.POSITIVE_INFINITY; - var candidate, diff, z, r; - for (var i=0, ii=this.layerResolutions_.length; i} ijz + * @param {number} resolution + * @return {goog.math.Box} + */ +ol.renderer.TileLayerRenderer.prototype.getTilePixelBox_ = function(ijz, resolution) { + var tileResolution = this.layerResolutions_[ijz[2]]; + var scale = resolution / tileResolution; + var tileSize = this.tileSize_; + + // desired tile size (in fractional pixels) + var fpxTileWidth = tileSize[0] / scale; + var fpxTileHeight = tileSize[1] / scale; + + var col = ijz[0]; + var left = Math.round(col * fpxTileWidth); // inclusive + var right = Math.round((col + 1) * fpxTileWidth); // exclusive + + var row = ijz[1]; + var top = Math.round(row * fpxTileHeight); // inclusive + var bottom = Math.round((row + 1) * fpxTileWidth); // exclusive + + return new goog.math.Box(top, right, bottom, left); +}; + +/** + * @param {ol.Loc} loc + * @param {number} resolution + * @return {goog.math.Coordinate} + */ +ol.renderer.TileLayerRenderer.prototype.getNormalizedTileCoord_ = function(loc, resolution) { + var tileOrigin = this.tileOrigin_; + var tileSize = this.tileSize_; + var pair = this.getPreferredResAndZ_(resolution); + var tileResolution = pair[0]; + var z = pair[1]; + var scale = resolution / tileResolution; + + // offset from tile origin in pixel space + var dx = Math.round((loc.getX() - tileOrigin[0]) / resolution); + var dy = Math.round((tileOrigin[1] - loc.getY()) / resolution); + + // desired tile size (in fractional pixels) + var fpxTileWidth = tileSize[0] / scale; + var fpxTileHeight = tileSize[1] / scale; + + // determine normalized col number (0 based, ascending right) + var col = Math.floor(dx / fpxTileWidth); + // determine normalized row number (0 based, ascending down) + var row = Math.floor(dy / fpxTileHeight); + + var box = this.getTilePixelBox_([col, row, z], resolution); + + // adjust col to allow for stretched tiles + if (dx < box.left) { + col -= 1; + } else if (dx >= box.right) { + col += 1; + } + + // adjust row to allow for stretched tiles + if (dy < box.top) { + row -= 1; + } else if (dy >= box.bottom) { + row += 1; + } + + return new goog.math.Coordinate(col, row); +}; + +/** + * @param {number} resolution + * @return {Array.} + */ +ol.renderer.TileLayerRenderer.prototype.getPreferredResAndZ_ = (function() { + var cache = {}; + return function(resolution) { + if (resolution in cache) { + return cache[resolution]; + } + var minDiff = Number.POSITIVE_INFINITY; + var candidate, diff, z, r; + for (var i=0, ii=this.layerResolutions_.length; i} ijz + * @return {Array.} + */ +ol.renderer.TileLayerRenderer.prototype.getTileCoordsFromNormalizedCoords_ = function(ijz) { + return [ + this.xRight_ ? ijz[0] : -ijz[0] - 1, + this.yDown_ ? ijz[1] : -ijz[1] - 1, + ijz[2] + ]; +}; + +/** + * @param {ol.Loc} center + * @param {number} resolution + * @return {goog.math.Box} + */ +ol.renderer.TileLayerRenderer.prototype.getTileBox_ = function(center, resolution) { + var size = this.getContainerSize(); + var halfWidth = size.width / 2; + var halfHeight = size.height / 2; + + var leftTop = new ol.Loc( + center.getX() - (resolution * halfWidth), + center.getY() + (resolution * halfHeight)); + + var rightBottom = new ol.Loc( + center.getX() + (resolution * halfWidth), + center.getY() - (resolution * halfHeight)); + + var ltCoord = this.getNormalizedTileCoord_(leftTop, resolution); + var rbCoord = this.getNormalizedTileCoord_(rightBottom, resolution); + + // right and bottom are treated as excluded, so we increment for the box + rbCoord.x += 1; + rbCoord.y += 1; + + return goog.math.Box.boundingBox(ltCoord, rbCoord); +}; /** * Get rid of tiles outside the rendered extent. */ ol.renderer.TileLayerRenderer.prototype.removeInvisibleTiles_ = function() { - var index, prune, x, y, z, tile; - var xRight = this.xRight_; - var yDown = this.yDown_; - var top = this.renderedTop_; - var right = this.renderedRight_; - var bottom = this.renderedBottom_; - var left = this.renderedLeft_; - for (var xyz in this.renderedTiles_) { - index = xyz.split(","); - x = +index[0]; - y = +index[1]; + var index, prune, i, j, z, tile; + var box = this.renderedTileBox_; + for (var ijz in this.renderedTiles_) { + index = ijz.split(","); + i = +index[0]; + j = +index[1]; z = +index[2]; prune = this.renderedZ_ !== z || - // beyond on the left side - (xRight ? x < left : x > left) || - // beyond on the right side - (xRight ? x > right : x < right) || - // above - (yDown ? y < top : y > top) || - // below - (yDown ? y > bottom : y < bottom); + i < box.left || // beyond on the left side + i >= box.right || // beyond on the right side + j < box.top || // above + j >= box.bottom; // below if (prune) { - tile = this.renderedTiles_[xyz]; - delete this.renderedTiles_[xyz]; + tile = this.renderedTiles_[ijz]; + delete this.renderedTiles_[ijz]; this.container_.removeChild(tile.getImg()); } } @@ -275,10 +306,12 @@ ol.renderer.TileLayerRenderer.prototype.removeInvisibleTiles_ = function() { * Deal with changes in resolution. * TODO: implement the animation * - * @param {ol.Loc} center New center. * @param {number} resolution New resolution. */ -ol.renderer.TileLayerRenderer.prototype.changeResolution_ = function(center, resolution) { +ol.renderer.TileLayerRenderer.prototype.changeResolution_ = function(resolution) { + var pair = this.getPreferredResAndZ_(resolution); + this.renderedZ_ = pair[1]; + this.renderedResolution_ = resolution; this.renderedTiles_ = {}; goog.dom.removeChildren(this.container_); };