From 0650d93d17af2176a6539ff32323bc5aa1e0c739 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Wed, 4 Jul 2012 22:17:56 -0600 Subject: [PATCH 01/18] Let the composite renderer manage containers. --- src/ol/renderer/Composite.js | 31 +++++++++++++++++++++------- src/ol/renderer/LayerRenderer.js | 12 ----------- src/ol/renderer/TileLayerRenderer.js | 6 +++--- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index 1857e3dc42..505d9e4a9f 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -4,7 +4,9 @@ goog.require('ol.renderer.MapRenderer'); goog.require('ol.renderer.LayerRenderer'); goog.require('ol.layer.Layer'); goog.require('ol.Loc'); + goog.require('goog.array'); +goog.require('goog.dom'); /** * @constructor @@ -20,13 +22,12 @@ ol.renderer.Composite = function(container) { * @private */ this.renderers_ = []; - - var target = document.createElement("div"); - target.className = "ol-renderer-composite"; - target.style.position = "absolute"; - target.style.height = "100%"; - target.style.width = "100%"; - container.appendChild(target); + + var target = goog.dom.createDom('div', { + 'class': 'ol-renderer-composite', + 'style': 'width:100%;height:100%;top:0;left:0;position:absolute' + }); + goog.dom.appendChild(container, target); /** * @type Element @@ -34,6 +35,12 @@ ol.renderer.Composite = function(container) { */ this.target_ = target; + /** + * @type Object + * @private + */ + this.layerContainers_ = {}; + }; goog.inherits(ol.renderer.Composite, ol.renderer.MapRenderer); @@ -82,7 +89,15 @@ ol.renderer.Composite.prototype.getRenderer = function(layer) { ol.renderer.Composite.prototype.createRenderer = function(layer) { var Renderer = this.pickRendererType(layer); goog.asserts.assert(Renderer, "No supported renderer for layer: " + layer); - return new Renderer(this.target_, layer); + + var container = goog.dom.createDom('div', { + 'class': 'ol-renderer-composite-layer', + 'style': 'width:100%;height:100%;top:0;left:0;position:absolute' + }); + goog.dom.appendChild(this.target_, container); + var renderer = new Renderer(container, layer); + this.layerContainers_[goog.getUid(renderer)] = container; + return renderer; }; /** diff --git a/src/ol/renderer/LayerRenderer.js b/src/ol/renderer/LayerRenderer.js index d5d863a015..6d9bd151de 100644 --- a/src/ol/renderer/LayerRenderer.js +++ b/src/ol/renderer/LayerRenderer.js @@ -21,18 +21,6 @@ ol.renderer.LayerRenderer = function(container, layer) { */ this.layer_ = layer; - var target = goog.dom.createDom('div', { - 'class': 'ol-renderer-layer', - 'style': 'position:absolute;height:1px:width:1px' - }); - goog.dom.appendChild(container, target); - - /** - * @type Element - * @protected - */ - this.target_ = target; - }; /** diff --git a/src/ol/renderer/TileLayerRenderer.js b/src/ol/renderer/TileLayerRenderer.js index 0a01910432..375a1049a6 100644 --- a/src/ol/renderer/TileLayerRenderer.js +++ b/src/ol/renderer/TileLayerRenderer.js @@ -244,7 +244,7 @@ ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) { pxTileLeft = pxTileRight; } if (newTiles) { - this.target_.appendChild(fragment); + this.container_.appendChild(fragment); } this.renderedResolution_ = resolution; this.renderedTop_ = topTileY; @@ -285,7 +285,7 @@ ol.renderer.TileLayerRenderer.prototype.removeInvisibleTiles_ = function() { if (prune) { tile = this.renderedTiles_[xyz]; delete this.renderedTiles_[xyz]; - this.target_.removeChild(tile.getImg()); + this.container_.removeChild(tile.getImg()); } } }; @@ -299,7 +299,7 @@ ol.renderer.TileLayerRenderer.prototype.removeInvisibleTiles_ = function() { */ ol.renderer.TileLayerRenderer.prototype.changeResolution_ = function(center, resolution) { this.renderedTiles_ = {}; - goog.dom.removeChildren(this.target_); + goog.dom.removeChildren(this.container_); }; From d435f6b4fdd62a1cb08e5ac8d20309e0540b8c66 Mon Sep 17 00:00:00 2001 From: tschaub Date: Thu, 5 Jul 2012 13:55:24 -0600 Subject: [PATCH 02/18] Simplifying the loader. Plovr checks the referrer for query string parameters (like mode), so we don't need any of this query string parsing (thanks @elemoine for the tip). The reason we still dynamically generate the script tag is to work on other domains (e.g. testing on a VM or mobile device). In this case, we make use of the `window.location.hostname`. --- demo/loader.js | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/demo/loader.js b/demo/loader.js index 0b9230f5a5..8b01bc2322 100644 --- a/demo/loader.js +++ b/demo/loader.js @@ -1,36 +1,7 @@ /** - Adds the plovr generated script to the document. The following default - values may be overridden with query string parameters: - - * hostname - localhost - * port - 9810 - * mode - SIMPLE - * id - ol + Adds the plovr generated script to the document. */ (function() { - var search = window.location.search.substring(1); - var params = { - hostname: "localhost", - port: "9810", - mode: "SIMPLE", - id: "ol" - }; - var chunks = search.split("&"); - var pair; - for (var i=chunks.length-1; i>=0; --i) { - pair = chunks[i].split("="); - params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]); - } - - var host = params.hostname + ":" + params.port; - delete params.hostname; - delete params.port; - - var pairs = []; - for (var key in params) { - pairs.push(encodeURIComponent(key) + "=" + encodeURIComponent(params[key])); - } - - var url = "http://" + host + "/compile?" + pairs.join("&"); + var url = "http://" + window.location.hostname + ":9810/compile?id=ol"; document.write(""); })(); From 523f4d61fc064743e3cf298bdf3dc61ba501bbd9 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Jul 2012 13:13:07 -0600 Subject: [PATCH 03/18] Shift container for all layer renderers together. --- src/ol/renderer/Composite.js | 56 +++++++++++++-- src/ol/renderer/LayerRenderer.js | 21 ++++++ src/ol/renderer/TileLayerRenderer.js | 103 +++++++++++++-------------- 3 files changed, 120 insertions(+), 60 deletions(-) diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index 505d9e4a9f..054392bf70 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -7,6 +7,8 @@ goog.require('ol.Loc'); goog.require('goog.array'); goog.require('goog.dom'); +goog.require('goog.style'); +goog.require('goog.math.Coordinate'); /** * @constructor @@ -30,19 +32,25 @@ ol.renderer.Composite = function(container) { goog.dom.appendChild(container, target); /** - * @type Element + * @type {Element} * @private */ this.target_ = target; + + /** + * @type {goog.math.Coordinate} + * @private + */ + this.targetOffset_ = new goog.math.Coordinate(0, 0); /** - * @type Object + * @type {Object} * @private */ this.layerContainers_ = {}; + }; - goog.inherits(ol.renderer.Composite, ol.renderer.MapRenderer); /** @@ -53,13 +61,38 @@ goog.inherits(ol.renderer.Composite, ol.renderer.MapRenderer); */ ol.renderer.Composite.prototype.draw = function(layers, center, resolution, animate) { // TODO: deal with layer order and removal - for (var i=0, ii=layers.length; i 0) { - pxMinX = Math.round(leftTileX * pxTileWidth) - pxOffsetX; - } else { - pxMinX = Math.round((-leftTileX-1) * pxTileWidth) - pxOffsetX; - } - var pxMinY; - if (yDown > 0) { - pxMinY = Math.round(topTileY * pxTileHeight) - pxOffsetY; - } else { - pxMinY = Math.round((-topTileY-1) * pxTileHeight) - pxOffsetY; - } - - var pxTileLeft = pxMinX; - var pxTileTop = pxMinY; - - var numTilesWide = Math.ceil(pxMapSize.width / pxTileWidth); - var numTilesHigh = Math.ceil(pxMapSize.height / pxTileHeight); + // calculate vector from tile origin to top-left tile (in pixel space) + var colsLeft = Math.floor(pxMap[0] / pxTileWidth); + var rowsAbove = Math.floor(pxMap[1] / pxTileHeight); + var pxTile = [colsLeft * pxTileWidth, rowsAbove * pxTileHeight]; - // assume a buffer of zero for now - if (pxMinX < 0) { + // offset vector from container origin to top-left tile (in pixel space) + var pxOffsetX = pxTile[0] - pxMap[0] - this.containerOffset_.x; + var pxOffsetY = pxTile[1] - pxMap[1] - this.containerOffset_.y; + + // index of the top left tile + var leftTileX = xRight ? colsLeft : (-colsLeft - 1); + var topTileY = yDown ? rowsAbove : (-rowsAbove - 1); + + var numTilesWide = Math.ceil(containerSize.width / pxTileWidth); + var numTilesHigh = Math.ceil(containerSize.height / pxTileHeight); + if (pxOffsetX !== 0) { numTilesWide += 1; } - if (pxMinY < 0) { + if (pxOffsetY !== 0) { numTilesHigh += 1; } + + var pxMinX = pxOffsetX; + var pxMinY = pxOffsetY; + var pxTileLeft = pxMinX; - var tileX, tileY, tile, img, pxTileRight, pxTileBottom, xyz, append; + var tileX, tileY, tile, img, pxTileBottom, pxTileRight, pxTileTop, xyz, append; var fragment = document.createDocumentFragment(); var newTiles = false; for (var i=0; i 0); - var yDown = (this.yDown_ > 0); + var xRight = this.xRight_; + var yDown = this.yDown_; var top = this.renderedTop_; var right = this.renderedRight_; var bottom = this.renderedBottom_; From c0777cc0039b06ca6fe00f9963774f1d9b8052ae Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Jul 2012 15:19:49 -0600 Subject: [PATCH 04/18] Working with integer pixel space. --- src/ol/renderer/TileLayerRenderer.js | 40 ++++++++++++++-------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ol/renderer/TileLayerRenderer.js b/src/ol/renderer/TileLayerRenderer.js index 1c7e598a83..8315744952 100644 --- a/src/ol/renderer/TileLayerRenderer.js +++ b/src/ol/renderer/TileLayerRenderer.js @@ -156,37 +156,37 @@ ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) { var centerX = center.getX(); var centerY = center.getY(); - // calculate vector from tile origin to map top-left (in pixel space) + // calculate vector from tile origin to map top-left (in integer pixel space) var tileOrigin = this.tileOrigin_; var mapOrigin = [centerX - halfMapWidth, centerY + halfMapHeight]; var pxMap = [ - (mapOrigin[0] - tileOrigin[0]) / resolution, - (tileOrigin[1] - mapOrigin[1]) / resolution + Math.round((mapOrigin[0] - tileOrigin[0]) / resolution), + Math.round((tileOrigin[1] - mapOrigin[1]) / resolution) ]; // desired tile size in fractional pixels - var pxTileWidth = this.tileSize_[0] / scale; - var pxTileHeight = this.tileSize_[1] / scale; + var fpxTileWidth = this.tileSize_[0] / scale; + var fpxTileHeight = this.tileSize_[1] / scale; - // calculate vector from tile origin to top-left tile (in pixel space) - var colsLeft = Math.floor(pxMap[0] / pxTileWidth); - var rowsAbove = Math.floor(pxMap[1] / pxTileHeight); - var pxTile = [colsLeft * pxTileWidth, rowsAbove * pxTileHeight]; + // calculate vector from tile origin to top-left tile (in integer pixel space) + var colsLeft = Math.floor(pxMap[0] / fpxTileWidth); + var rowsAbove = Math.floor(pxMap[1] / fpxTileHeight); + var pxTile = [Math.round(colsLeft * fpxTileWidth), Math.round(rowsAbove * fpxTileHeight)]; - // offset vector from container origin to top-left tile (in pixel space) - var pxOffsetX = pxTile[0] - pxMap[0] - this.containerOffset_.x; - var pxOffsetY = pxTile[1] - pxMap[1] - this.containerOffset_.y; + // offset vector from container origin to top-left tile (in integer pixel space) + var pxOffsetX = Math.round(pxTile[0] - pxMap[0] - this.containerOffset_.x); + var pxOffsetY = Math.round(pxTile[1] - pxMap[1] - this.containerOffset_.y); // index of the top left tile var leftTileX = xRight ? colsLeft : (-colsLeft - 1); var topTileY = yDown ? rowsAbove : (-rowsAbove - 1); - var numTilesWide = Math.ceil(containerSize.width / pxTileWidth); - var numTilesHigh = Math.ceil(containerSize.height / pxTileHeight); - if (pxOffsetX !== 0) { + var numTilesWide = Math.ceil(containerSize.width / fpxTileWidth); + var numTilesHigh = Math.ceil(containerSize.height / fpxTileHeight); + if (this.containerOffset_.x !== -pxOffsetX) { numTilesWide += 1; } - if (pxOffsetY !== 0) { + if (this.containerOffset_.y !== -pxOffsetY) { numTilesHigh += 1; } @@ -201,18 +201,18 @@ ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) { pxTileTop = pxMinY; tileX = xRight ? leftTileX + i : leftTileX - i; if (scale !== 1) { - pxTileRight = Math.round(pxMinX + ((i+1) * pxTileWidth)); + pxTileRight = Math.round(pxMinX + ((i+1) * fpxTileWidth)); } else { - pxTileRight = pxTileLeft + pxTileWidth; + pxTileRight = pxTileLeft + fpxTileWidth; } for (var j=0; j Date: Sat, 7 Jul 2012 15:42:08 -0600 Subject: [PATCH 05/18] Resize images in case they were previously scaled. --- src/ol/renderer/TileLayerRenderer.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/ol/renderer/TileLayerRenderer.js b/src/ol/renderer/TileLayerRenderer.js index 8315744952..1082cb962e 100644 --- a/src/ol/renderer/TileLayerRenderer.js +++ b/src/ol/renderer/TileLayerRenderer.js @@ -226,10 +226,14 @@ ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) { img = tile.getImg(); img.style.top = pxTileTop + "px"; img.style.left = pxTileLeft + "px"; - if (scale !== 1) { - img.style.height = (pxTileRight - pxTileLeft) + "px"; - img.style.width = (pxTileBottom - pxTileTop) + "px"; - } + /** + * We need to set the size here even if the scale is 1 + * because the image may have been scaled previously. If + * we want to avoid setting size unnecessarily, the tile + * should keep track of the scale. + */ + img.style.height = (pxTileRight - pxTileLeft) + "px"; + img.style.width = (pxTileBottom - pxTileTop) + "px"; goog.dom.appendChild(fragment, img); newTiles = true; } From ddd57426b5d79671c9b67f0e1558e014c10efddd Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sat, 7 Jul 2012 16:50:22 -0600 Subject: [PATCH 06/18] Allow renderer to have a buffer. --- src/ol/renderer/Composite.js | 43 ++++++++++++++++++++++------ src/ol/renderer/LayerRenderer.js | 20 +++++++++++++ src/ol/renderer/MapRenderer.js | 21 ++++++++++++++ src/ol/renderer/TileLayerRenderer.js | 22 ++------------ 4 files changed, 78 insertions(+), 28 deletions(-) diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index 054392bf70..6ec58ffe7d 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -24,10 +24,24 @@ ol.renderer.Composite = function(container) { * @private */ this.renderers_ = []; + + /** + * Pixel buffer for renderer container. + * + * @type {number} + * @private + */ + 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:100%;height:100%;top:0;left:0;position:absolute' + 'style': 'width:' + width + 'px;height:' + height + 'px;' + + 'top:-' + this.buffer_ + 'px;left:-' + this.buffer_ + 'px;' + + 'position:absolute' }); goog.dom.appendChild(container, target); @@ -38,6 +52,8 @@ ol.renderer.Composite = function(container) { this.target_ = target; /** + * The cumulative offset from the original position of the target element. + * * @type {goog.math.Coordinate} * @private */ @@ -48,11 +64,23 @@ ol.renderer.Composite = function(container) { * @private */ this.layerContainers_ = {}; - }; 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 @@ -65,24 +93,23 @@ ol.renderer.Composite.prototype.draw = function(layers, center, resolution, anim if (this.renderedResolution_) { if (resolution !== this.renderedResolution_) { // TODO: apply transition to old target - this.targetOffset_ = new goog.math.Coordinate(0, 0); - goog.style.setPosition(this.target_, this.targetOffset_); + this.shiftTarget(0, 0); } } this.renderedResolution_ = resolution; // shift target element to account for center change if (this.renderedCenter_) { - this.targetOffset_ = new goog.math.Coordinate( - this.targetOffset_.x + Math.round((this.renderedCenter_.getX() - center.getX()) / resolution), - this.targetOffset_.y + Math.round((center.getY() - this.renderedCenter_.getY()) / resolution) + this.shiftTarget( + Math.round((this.renderedCenter_.getX() - center.getX()) / resolution), + Math.round((center.getY() - this.renderedCenter_.getY()) / resolution) ); - goog.style.setPosition(this.target_, this.targetOffset_); } this.renderedCenter_ = center; // update each layer renderer var renderer, container; + for (var i=0, ii=layers.length; i} layers * @param {ol.Loc} center diff --git a/src/ol/renderer/TileLayerRenderer.js b/src/ol/renderer/TileLayerRenderer.js index 1082cb962e..f390764f0a 100644 --- a/src/ol/renderer/TileLayerRenderer.js +++ b/src/ol/renderer/TileLayerRenderer.js @@ -82,12 +82,6 @@ ol.renderer.TileLayerRenderer = function(container, layer) { */ this.renderedZ_ = undefined; - /** - * @type {goog.math.Size} - * @private - */ - this.containerSize_ = null; - }; goog.inherits(ol.renderer.TileLayerRenderer, ol.renderer.LayerRenderer); @@ -114,18 +108,6 @@ ol.renderer.TileLayerRenderer.prototype.getPreferredResAndZ_ = function(resoluti return [r, z]; }; -/** - * @return {goog.math.Size} - */ -ol.renderer.TileLayerRenderer.prototype.getContainerSize_ = function() { - // TODO: listen for resize and set this.constainerSize_ null - // https://github.com/openlayers/ol3/issues/2 - if (goog.isNull(this.containerSize_)) { - this.containerSize_ = goog.style.getSize(this.container_); - } - return this.containerSize_; -}; - /** * Tiles rendered at the current resolution; * @type {Object} @@ -147,7 +129,7 @@ ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) { var tileZ = pair[1]; var scale = resolution / tileResolution; - var containerSize = this.getContainerSize_(); + var containerSize = this.getContainerSize(); var xRight = this.xRight_; var yDown = this.yDown_; @@ -155,7 +137,7 @@ ol.renderer.TileLayerRenderer.prototype.draw = function(center, resolution) { var halfMapHeight = (resolution * containerSize.height) / 2; var centerX = center.getX(); var centerY = center.getY(); - + // calculate vector from tile origin to map top-left (in integer pixel space) var tileOrigin = this.tileOrigin_; var mapOrigin = [centerX - halfMapWidth, centerY + halfMapHeight]; From e8ac3f1a6b0e896fda2034551fea43921ddc039d Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 8 Jul 2012 11:55:31 +0200 Subject: [PATCH 07/18] preventDefault for mousemove and touchmove. This avoids panning of the browser page when drag-panning the map. --- src/ol/event/Drag.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/ol/event/Drag.js b/src/ol/event/Drag.js index d05c97f396..2ef0bec8db 100644 --- a/src/ol/event/Drag.js +++ b/src/ol/event/Drag.js @@ -30,11 +30,21 @@ ol.event.Drag = function(target) { */ this.dragger_ = dragger; + /** + * @private + * @type {ol.event.Events} + */ + this.target_ = target; + // We want to swallow the click event that gets fired after dragging. var newSequence; function unregisterClickStopper() { target.unregister('click', goog.functions.FALSE, undefined, true); } + + // no default for mousemove and touchmove events to avoid page scrolling. + target.register('mousemove', this.preventDefault); + target.register('touchmove', this.preventDefault); dragger.defaultAction = function(x, y) {}; dragger.addEventListener(goog.fx.Dragger.EventType.START, function(evt) { @@ -73,10 +83,19 @@ ol.event.Drag = function(target) { ); }; +/** + * @private + */ +ol.event.Drag.prototype.preventDefault = function(evt) { + evt.preventDefault(); +}; + /** @inheritDoc */ ol.event.Drag.prototype.destroy = function() { + this.target_.unregister('mousemove', this.preventDefault); + this.target_.unregister('touchmove', this.preventDefault); this.dragger_.dispose(); - delete this.dragger_; + goog.object.clear(this); }; From e67e9049cabfb7f9d33d2773d5434396d6637a03 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 8 Jul 2012 12:12:19 +0200 Subject: [PATCH 08/18] preventDefault and stopPropagation convenience functions. These functions can directly be used as browser event listeners. --- src/ol/event/Drag.js | 15 ++++----------- src/ol/event/Events.js | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/ol/event/Drag.js b/src/ol/event/Drag.js index 2ef0bec8db..af1eee8fe3 100644 --- a/src/ol/event/Drag.js +++ b/src/ol/event/Drag.js @@ -43,8 +43,8 @@ ol.event.Drag = function(target) { } // no default for mousemove and touchmove events to avoid page scrolling. - target.register('mousemove', this.preventDefault); - target.register('touchmove', this.preventDefault); + target.register('mousemove', ol.event.preventDefault); + target.register('touchmove', ol.event.preventDefault); dragger.defaultAction = function(x, y) {}; dragger.addEventListener(goog.fx.Dragger.EventType.START, function(evt) { @@ -83,17 +83,10 @@ ol.event.Drag = function(target) { ); }; -/** - * @private - */ -ol.event.Drag.prototype.preventDefault = function(evt) { - evt.preventDefault(); -}; - /** @inheritDoc */ ol.event.Drag.prototype.destroy = function() { - this.target_.unregister('mousemove', this.preventDefault); - this.target_.unregister('touchmove', this.preventDefault); + this.target_.unregister('mousemove', ol.event.preventDefault); + this.target_.unregister('touchmove', ol.event.preventDefault); this.dragger_.dispose(); goog.object.clear(this); }; diff --git a/src/ol/event/Events.js b/src/ol/event/Events.js index fe65d8b363..9089779aec 100644 --- a/src/ol/event/Events.js +++ b/src/ol/event/Events.js @@ -42,16 +42,21 @@ ol.event.isMultiTouch = function(evt) { }; /** - * Is the event a keyboard event with Enter or Space pressed? + * Call preventDefault on the provided event. * * @param {!Event} evt - * @return {boolean} */ -ol.event.isEnterOrSpace = function(evt) { - return evt.type === "keypress" && - (evt.keyCode === goog.events.KeyCodes.ENTER || - evt.keyCode === goog.events.KeyCodes.SPACE || - evt.keyCode === goog.events.KeyCodes.MAC_ENTER); +ol.event.preventDefault = function(evt) { + evt.preventDefault(); +}; + +/** + * Call stopPropagation on the provided event. + * + * @param {!Event} evt + */ +ol.event.stopPropagation = function(evt) { + evt.stopPropagation(); }; From a38a33108522d95be62425b2b8cbac4d3223523c Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 8 Jul 2012 12:14:26 +0200 Subject: [PATCH 09/18] Allow for events on other elements than the map viewport. Not registering browser events as priority events makes it easy for controls to preventDefault or stopPropagation when events are registered on children of the map viewport element. --- src/ol/event/Events.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ol/event/Events.js b/src/ol/event/Events.js index 9089779aec..3ad7969228 100644 --- a/src/ol/event/Events.js +++ b/src/ol/event/Events.js @@ -160,7 +160,7 @@ ol.event.Events.prototype.setElement = function(element) { if (this.element_) { for (t in types) { goog.events.unlisten( - this.element_, types[t], this.handleBrowserEvent, true, this + this.element_, types[t], this.handleBrowserEvent, false, this ); } this.destroySequences(); @@ -171,7 +171,7 @@ ol.event.Events.prototype.setElement = function(element) { this.createSequences(); for (t in types) { goog.events.listen( - element, types[t], this.handleBrowserEvent, true, this + element, types[t], this.handleBrowserEvent, false, this ); } } From ca2414c9d6cfabd3abff6467a4cceab4c93e7673 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 8 Jul 2012 12:17:11 +0200 Subject: [PATCH 10/18] Simplify controls by registering click events on own elements. This makes sense for all controls that provide buttons or similar UI elements that don't interfer with map interaction. --- src/ol/control/Attribution.js | 15 +------- src/ol/control/Zoom.js | 69 ++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 47 deletions(-) diff --git a/src/ol/control/Attribution.js b/src/ol/control/Attribution.js index 0c03182366..35875a3de4 100644 --- a/src/ol/control/Attribution.js +++ b/src/ol/control/Attribution.js @@ -43,9 +43,7 @@ ol.control.Attribution.prototype.setMap = function(map) { var staticOverlay = map.getStaticOverlay(); if (goog.isNull(this.container_)) { this.container_ = goog.dom.createDom('div', this.CLS); - // This is not registered as priority listener, so priority listeners - // can still get the click event. - map.getEvents().register('click', this.stopLinkClick, this); + goog.events.listen(this.container_, 'click', ol.event.stopPropagation); } if (!goog.isNull(staticOverlay)) { goog.dom.append(staticOverlay, this.container_); @@ -53,16 +51,6 @@ ol.control.Attribution.prototype.setMap = function(map) { goog.base(this, 'setMap', map); }; -/** - * Prevent clicks on links in the attribution from getting through to - * listeners. - */ -ol.control.Attribution.prototype.stopLinkClick = function(evt) { - var node = evt.target; - return node.nodeName !== 'A' || - !goog.dom.getAncestorByClass(node, this.CLS); -}; - /** @inheritDoc */ ol.control.Attribution.prototype.activate = function() { var active = goog.base(this, 'activate'); @@ -96,6 +84,7 @@ ol.control.Attribution.prototype.update = function() { }; ol.control.Attribution.prototype.destroy = function() { + goog.events.unlisten(this.container_, 'click', ol.event.stopPropagation); goog.dom.removeNode(this.container_); goog.base(this, 'destroy'); }; diff --git a/src/ol/control/Zoom.js b/src/ol/control/Zoom.js index 07010a492a..a25cbac6d7 100644 --- a/src/ol/control/Zoom.js +++ b/src/ol/control/Zoom.js @@ -12,13 +12,23 @@ goog.require('goog.dom'); * @param {boolean|undefined} opt_autoActivate */ ol.control.Zoom = function(opt_autoActivate) { - + goog.base(this, opt_autoActivate); + /** + * @type {Node} + */ + this.inButton_ = null; + + /** + * @type {Node} + */ + this.outButton_ = null; + /** * Activate this control when it is added to a map. Default is true. * - * @type {boolean} autoActivate + * @type {boolean} */ this.autoActivate_ = goog.isDef(opt_autoActivate) ? opt_autoActivate : true; @@ -30,38 +40,38 @@ goog.inherits(ol.control.Zoom, ol.control.Control); * @param {ol.Map} map */ ol.control.Zoom.prototype.setMap = function(map) { - goog.base(this, 'setMap', map); var container = goog.dom.createDom('div', ol.control.Zoom.RES.CLS); - var inButton = goog.dom.createDom( + this.inButton_ = goog.dom.createDom( 'div', ol.control.Zoom.RES.IN_CLS, goog.dom.createDom('a', {'href': '#zoomIn'}) ); goog.dom.setTextContent( - /** @type {Element} */ (inButton.firstChild), + /** @type {Element} */ (this.inButton_.firstChild), ol.control.Zoom.RES.IN_TEXT ); - var outButton = goog.dom.createDom( + this.outButton_ = goog.dom.createDom( 'div', ol.control.Zoom.RES.OUT_CLS, goog.dom.createDom('a', {'href': '#zoomOut'}) ); goog.dom.setTextContent( - /** @type {Element} */ (outButton.firstChild), + /** @type {Element} */ (this.outButton_.firstChild), ol.control.Zoom.RES.OUT_TEXT ); - goog.dom.append(container, inButton, outButton); + goog.dom.append(container, this.inButton_, this.outButton_); var overlay = map.getStaticOverlay(); if (goog.isDefAndNotNull(overlay)) { goog.dom.append(overlay, container); } + goog.base(this, 'setMap', map); }; /** @inheritDoc */ ol.control.Zoom.prototype.activate = function() { var active = goog.base(this, 'activate'); if (active) { - this.map_.getEvents().register('click', this.handle, this); - this.map_.getEvents().register('keypress', this.handle, this); + goog.events.listen(this.inButton_, 'click', this.handleIn, false, this); + goog.events.listen(this.outButton_, 'click', this.handleOut, false, this); } return active; }; @@ -70,37 +80,28 @@ ol.control.Zoom.prototype.activate = function() { ol.control.Zoom.prototype.deactivate = function() { var inactive = goog.base(this, 'deactivate'); if (inactive) { - this.map_.getEvents().unregister('click', this.handle, this); - this.map_.getEvents().unregister('keypress', this.handle, this); + goog.events.unlisten(this.inButton_, 'click', this.handleIn, false, this); + goog.events.unlisten(this.outButton_, 'click', this.handleOut, false, this); } return inactive; }; /** * @param {Event} evt - * @return {boolean} */ -ol.control.Zoom.prototype.handle = function(evt) { - var target = /** @type {Node} */ (evt.target), - handled = false; - if (evt.type === 'click' || ol.event.isEnterOrSpace(evt)) { - if (goog.dom.getAncestorByClass(target, ol.control.Zoom.RES.IN_CLS)) { - this.map_.zoomIn(); - handled = true; - } else - if (goog.dom.getAncestorByClass(target, ol.control.Zoom.RES.OUT_CLS)) { - this.map_.zoomOut(); - handled = true; - } - if (handled) { - // Stop default behavior (i.e. don't follow link to anchor) - evt.preventDefault(); - // On Android 2.3, the above does not prevent the link from being - // followed, but stopPropagation does. - evt.stopPropagation(); - } - } - return !handled; +ol.control.Zoom.prototype.handleIn = function(evt) { + this.map_.zoomIn(); + evt.preventDefault(); + evt.stopPropagation(); +}; + +/** + * @param {Event} evt + */ +ol.control.Zoom.prototype.handleOut = function(evt) { + this.map_.zoomOut(); + evt.preventDefault(); + evt.stopPropagation(); }; ol.control.Zoom.prototype.destroy = function() { From 7bf1ffe606808162959bf13d4aa0acd83040d203 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Sun, 8 Jul 2012 20:27:10 +0200 Subject: [PATCH 11/18] Nice improvement of the wheel zooming experience. --- src/ol/control/Navigation.js | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/ol/control/Navigation.js b/src/ol/control/Navigation.js index 2adaf5b570..c6904fbd7f 100644 --- a/src/ol/control/Navigation.js +++ b/src/ol/control/Navigation.js @@ -20,6 +20,12 @@ ol.control.Navigation = function(opt_autoActivate) { this.autoActivate_ = goog.isDef(opt_autoActivate) ? opt_autoActivate : true; + /** + * @type {number?} + * @private + */ + this.zoomBlocked_ = null; + }; goog.inherits(ol.control.Navigation, ol.control.Control); @@ -57,12 +63,17 @@ ol.control.Navigation.prototype.moveMap = function(evt) { * @param {Event} evt */ ol.control.Navigation.prototype.zoomMap = function(evt) { - var map = this.map_, - delta = ((evt.deltaY / 3) | 0); - if (Math.abs(delta) === 0) { - return; - } - map.setZoom(map.getZoom()-delta, map.getEvents().getPointerPosition(evt)); + var me = this; + if (evt.deltaY === 0 || me.zoomBlocked_) { + return; + } + me.zoomBlocked_ = window.setTimeout(function() { + me.zoomBlocked_ = null; + }, 200); + + var map = me.map_, + step = evt.deltaY / Math.abs(evt.deltaY); + map.setZoom(map.getZoom()-step, map.getEvents().getPointerPosition(evt)); // We don't want the page to scroll. evt.preventDefault(); return false; From ceaba3cdd41113098589543a27c634c74db288ab Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Jul 2012 18:35:23 -0600 Subject: [PATCH 12/18] Overlays above the renderer. --- src/ol/Map.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/Map.js b/src/ol/Map.js index 180e5a11c2..b0ae42057e 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -487,7 +487,7 @@ ol.Map.prototype.createRenderer = function() { this.mapOverlay_ = goog.dom.createDom('div', 'ol-overlay-map'); this.staticOverlay_ = goog.dom.createDom('div', { 'class': staticCls, - 'style': 'width:100%;height:100%;top:0;left:0;position:absolute' + 'style': 'width:100%;height:100%;top:0;left:0;position:absolute;z-index:1' }); } if (!goog.isNull(viewport)) { From 4911320eef001bd3e7368f842bf1ccdd38a15b6a Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Jul 2012 18:36:34 -0600 Subject: [PATCH 13/18] 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_); }; From b24d63a898cb639177e121a45c9eab86555d2a54 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Jul 2012 19:34:53 -0600 Subject: [PATCH 14/18] Spaces for tabs. --- src/ol/control/Navigation.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ol/control/Navigation.js b/src/ol/control/Navigation.js index c6904fbd7f..8c9fbaafc9 100644 --- a/src/ol/control/Navigation.js +++ b/src/ol/control/Navigation.js @@ -63,10 +63,10 @@ ol.control.Navigation.prototype.moveMap = function(evt) { * @param {Event} evt */ ol.control.Navigation.prototype.zoomMap = function(evt) { - var me = this; - if (evt.deltaY === 0 || me.zoomBlocked_) { - return; - } + var me = this; + if (evt.deltaY === 0 || me.zoomBlocked_) { + return; + } me.zoomBlocked_ = window.setTimeout(function() { me.zoomBlocked_ = null; }, 200); From 4dfb5d0dee609f60772aff63fcb892c0dbb9fe25 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Jul 2012 20:42:15 -0600 Subject: [PATCH 15/18] Set center on reset. Only shift if offset. --- src/ol/renderer/Composite.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ol/renderer/Composite.js b/src/ol/renderer/Composite.js index 602d0c86fa..62105865bc 100644 --- a/src/ol/renderer/Composite.js +++ b/src/ol/renderer/Composite.js @@ -157,10 +157,14 @@ ol.renderer.Composite.prototype.getOriginForCenterAndRes_ = function(center, res */ ol.renderer.Composite.prototype.shiftTarget_ = function(center, resolution) { var oldCenter = this.renderedCenter_; - var offset = this.targetOffset_; - offset.x += Math.round((oldCenter.getX() - center.getX()) / resolution); - offset.y += Math.round((center.getY() - oldCenter.getY()) / resolution); - goog.style.setPosition(this.target_, offset); + var dx = Math.round((oldCenter.getX() - center.getX()) / resolution); + var dy = Math.round((center.getY() - oldCenter.getY()) / resolution); + if (!(dx == 0 && dy == 0)) { + var offset = this.targetOffset_; + offset.x += Math.round((oldCenter.getX() - center.getX()) / resolution); + offset.y += Math.round((center.getY() - oldCenter.getY()) / resolution); + goog.style.setPosition(this.target_, offset); + } }; /** @@ -176,6 +180,7 @@ ol.renderer.Composite.prototype.resetTarget_ = function(center, resolution) { } var offset = new goog.math.Coordinate(-this.buffer_, -this.buffer_); this.targetOffset_ = offset; + this.renderedCenter_ = center; goog.style.setPosition(this.target_, offset); }; From 3d130d3325d49f82c896c4a23f6f6f1420963d42 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Jul 2012 20:56:36 -0600 Subject: [PATCH 16/18] Letting the map ask renderer for resolution. --- src/ol/Map.js | 8 ++++++++ src/ol/renderer/MapRenderer.js | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/src/ol/Map.js b/src/ol/Map.js index b0ae42057e..8690522657 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -264,6 +264,14 @@ ol.Map.prototype.getResolutionForZoom = function(zoom) { } }; +/** + * @return {number} the resolution for the map at the given zoom level + */ +ol.Map.prototype.getResolution = function() { + goog.asserts.assert(goog.isDef(this.renderer_)); + return this.renderer_.getResolution(); +}; + /** * @param {goog.math.Coordinate|{x: number, y: number}} pixel diff --git a/src/ol/renderer/MapRenderer.js b/src/ol/renderer/MapRenderer.js index c7cc8e385d..911d4f8891 100644 --- a/src/ol/renderer/MapRenderer.js +++ b/src/ol/renderer/MapRenderer.js @@ -58,6 +58,13 @@ ol.renderer.MapRenderer.prototype.getContainerSize = function() { ol.renderer.MapRenderer.prototype.draw = function(layers, center, resolution, animate) { }; +/** + * @return {number} The rendered resolution. + */ +ol.renderer.MapRenderer.prototype.getResolution = function() { + return this.renderedResolution_; +}; + /** * TODO: determine a closure friendly way to register map renderers. * @type {Array} From 5a485ea85686da73e11e850b09f73fe5fe1916ca Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Sun, 8 Jul 2012 20:57:36 -0600 Subject: [PATCH 17/18] Getting explicit about pixels and size. Eventually, well have to ask renderers for all pixel and size related stuff. And the map overlay renderer will do all pixel<->loc math. For now, we let the map do this math based on its viewport - as this is where the mouse events are handled and it is pixels relative to the viewport that are being generated. For now, the getViewportPixelForLoc and getLocForViewportPixel are now more explicitly named. --- src/ol/Map.js | 55 +++++++++++++++++++++++++--------- src/ol/Popup.js | 2 +- src/ol/control/Navigation.js | 2 +- src/ol/renderer/MapRenderer.js | 35 ---------------------- tests/Map.html | 14 ++++----- 5 files changed, 50 insertions(+), 58 deletions(-) diff --git a/src/ol/Map.js b/src/ol/Map.js index 8690522657..b524710bc8 100644 --- a/src/ol/Map.js +++ b/src/ol/Map.js @@ -87,6 +87,12 @@ ol.Map = function() { * @type {Element} */ this.viewport_ = null; + + /** + * @private + * @type {goog.math.Size} + */ + this.viewportSize_ = null; /** * @private @@ -274,32 +280,50 @@ ol.Map.prototype.getResolution = function() { /** + * TODO: We'll have to ask the map overlay renderer for this. This method will + * not work once map space is not aligned with pixel space. + * * @param {goog.math.Coordinate|{x: number, y: number}} pixel * @return {ol.Loc} */ -ol.Map.prototype.getLocForPixel = function(pixel) { - return goog.isDef(this.renderer_) ? - this.renderer_.getLocForPixel(pixel) : null; +ol.Map.prototype.getLocForViewportPixel = function(pixel) { + var size = this.getViewportSize(); + var center = this.center_; + var resolution = this.getResolution(); + var x = center.getX() + (resolution * (pixel.x - (size.width / 2))); + var y = center.getY() - (resolution * (pixel.y - (size.height / 2))); + return new ol.Loc(x, y, undefined, this.getProjection()); }; /** + * TODO: We'll have to ask the map overlay renderer for this. This method will + * not work once map space is not aligned with pixel space. + * * @param {ol.Loc} loc * @return {{x: number, y: number}} */ -ol.Map.prototype.getPixelForLoc = function(loc) { - return goog.isDef(this.renderer_) ? - this.renderer_.getPixelForLoc(loc) : null; +ol.Map.prototype.getViewportPixelForLoc = function(loc) { + var size = this.getViewportSize(); + var center = this.center_; + var resolution = this.getResolution(); + return { + x: ((loc.getX() - center.getX()) / resolution) + (size.width / 2), + y: ((center.getY() - loc.getY()) / resolution) + (size.height / 2) + }; }; /** - * @return {goog.math.Size} The currently rendered map size in pixels. + * @return {goog.math.Size} */ -ol.Map.prototype.getSize = function() { - //TODO consider caching this when we have something like updateSize - return goog.isDef(this.renderer_) ? this.renderer_.getSize() : null; +ol.Map.prototype.getViewportSize = function() { + // TODO: listen for resize and set this.viewportSize_ null + // https://github.com/openlayers/ol3/issues/2 + if (goog.isNull(this.viewportSize_)) { + this.viewportSize_ = goog.style.getSize(this.viewport_); + } + return this.viewportSize_; }; - /** * @param {ol.Loc} center Center in map projection. */ @@ -335,8 +359,8 @@ ol.Map.prototype.setZoom = function(zoom, opt_anchor) { return; } if (goog.isDef(opt_anchor)) { - var size = this.getSize(), - anchorLoc = this.getLocForPixel(opt_anchor), + var size = this.getViewportSize(), + anchorLoc = this.getLocForViewportPixel(opt_anchor), newRes = this.getResolutionForZoom(newZoom); newCenter = anchorLoc.add( (size.width/2 - opt_anchor.x) * newRes, @@ -511,10 +535,13 @@ ol.Map.prototype.getEvents = function() { }; /** + * TODO: This method will need to be reworked/revisited when renderers can + * display a map that is rotated or otherwise not aligned with pixel space. + * * @param {number} dx pixels to move in x direction * @param {number} dy pixels to move in x direction */ -ol.Map.prototype.moveByPx = function(dx, dy) { +ol.Map.prototype.moveByViewportPx = function(dx, dy) { if (!goog.isNull(this.center_) && goog.isDef(this.zoom_)) { var resolution = this.getResolutionForZoom(this.zoom_), center = new ol.Loc( diff --git a/src/ol/Popup.js b/src/ol/Popup.js index 8ec7018aee..5a5beff366 100644 --- a/src/ol/Popup.js +++ b/src/ol/Popup.js @@ -243,7 +243,7 @@ ol.Popup.prototype.setAnchorOffset_ = function() { this.pos_ = new ol.geom.Point(this.anchor_.getX(), this.anchor_.getY()); } var pos = /** @type {ol.Loc} */ (this.pos_); - var popupPosPx = this.map_.getPixelForLoc(pos); + var popupPosPx = this.map_.getViewportPixelForLoc(pos); var popupSize = goog.style.getSize(this.container_); switch(this.placement_) { diff --git a/src/ol/control/Navigation.js b/src/ol/control/Navigation.js index 8c9fbaafc9..e56a41b2f4 100644 --- a/src/ol/control/Navigation.js +++ b/src/ol/control/Navigation.js @@ -55,7 +55,7 @@ ol.control.Navigation.prototype.deactivate = function() { * @param {Object} evt */ ol.control.Navigation.prototype.moveMap = function(evt) { - this.map_.moveByPx(evt.deltaX, evt.deltaY); + this.map_.moveByViewportPx(evt.deltaX, evt.deltaY); return false; }; diff --git a/src/ol/renderer/MapRenderer.js b/src/ol/renderer/MapRenderer.js index 911d4f8891..5fc37f62b9 100644 --- a/src/ol/renderer/MapRenderer.js +++ b/src/ol/renderer/MapRenderer.js @@ -108,38 +108,3 @@ ol.renderer.MapRenderer.pickRendererType = function(preferences) { // if we didn't find any of the preferred renderers, use the first return Renderer || Candidates[0] || null; }; - -/** - * @param {goog.math.Coordinate|{x: number, y: number}} pixel - * @return {ol.Loc} - */ -ol.renderer.MapRenderer.prototype.getLocForPixel = function(pixel) { - var center = this.renderedCenter_, - resolution = this.renderedResolution_, - size = goog.style.getSize(this.container_); - return center.add( - (pixel.x - size.width/2) * resolution, - (size.height/2 - pixel.y) * resolution - ); -}; - -/** - * @param {ol.Loc} loc - * @return {{x: number, y: number}} - */ -ol.renderer.MapRenderer.prototype.getPixelForLoc = function(loc) { - var center = this.renderedCenter_, - resolution = this.renderedResolution_, - size = this.getSize(); - return { - x: (size.width*resolution/2 + loc.getX() - center.getX())/resolution, - y: (size.height*resolution/2 - loc.getY() + center.getY())/resolution - }; -}; - -/** - * @return {goog.math.Size} The currently rendered map size in pixels. - */ -ol.renderer.MapRenderer.prototype.getSize = function() { - return goog.style.getSize(this.container_); -}; diff --git a/tests/Map.html b/tests/Map.html index e09f132442..1b7c5c2e60 100644 --- a/tests/Map.html +++ b/tests/Map.html @@ -1935,12 +1935,12 @@ map.destroy(); } - function test_moveByPx(t) { + function test_moveByViewportPx(t) { t.plan(16); var moved; var Layer = OpenLayers.Class(OpenLayers.Layer, { - moveByPx: function(dx, dy) { + moveByViewportPx: function(dx, dy) { moved[this.name] = true; } }); @@ -1972,7 +1972,7 @@ // move to a valid position moved = {}; - map.moveByPx(-455, 455); + map.moveByViewportPx(-455, 455); t.eq(map.layerContainerDiv.style.left, '455px', '[valid position] layer container left correct'); t.eq(map.layerContainerDiv.style.top, '-455px', @@ -1984,7 +1984,7 @@ // move outside the max extent moved = {}; - map.moveByPx(-4500, 4500); + map.moveByViewportPx(-4500, 4500); t.eq(map.layerContainerDiv.style.left, '455px', '[outside max extent] layer container left correct'); t.eq(map.layerContainerDiv.style.top, '-455px', @@ -1996,7 +1996,7 @@ // move outside the restricted extent moved = {}; - map.moveByPx(-500, 500); + map.moveByViewportPx(-500, 500); t.eq(map.layerContainerDiv.style.left, '455px', '[outside restricted extent] layer container left correct'); t.eq(map.layerContainerDiv.style.top, '-455px', @@ -2011,7 +2011,7 @@ } // test for http://trac.osgeo.org/openlayers/ticket/3388 - function test_moveByPx_restrictedExtent(t) { + function test_moveByViewportPx_restrictedExtent(t) { t.plan(2); var map = new OpenLayers.Map({ @@ -2024,7 +2024,7 @@ map.zoomToExtent(new OpenLayers.Bounds(-11.25, 0, 11.25, 11.25)); - map.moveByPx(-10, -10); + map.moveByViewportPx(-10, -10); t.eq(map.layerContainerDiv.style.left, '10px', 'layer container left correct'); t.eq(map.layerContainerDiv.style.top, '0px', 'layer container top correct'); } From d76eea8ac43a4720a69f48af2fa5ecd0e4c980d7 Mon Sep 17 00:00:00 2001 From: ahocevar Date: Mon, 9 Jul 2012 08:26:25 +0200 Subject: [PATCH 18/18] Fixing tests. --- test/spec/ol/Events.test.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/spec/ol/Events.test.js b/test/spec/ol/Events.test.js index 643f1a648c..604e131708 100644 --- a/test/spec/ol/Events.test.js +++ b/test/spec/ol/Events.test.js @@ -172,10 +172,4 @@ describe("ol.event.Events", function() { expect(ol.event.isMultiTouch({})).toBe(false); }); - it("provides an isEnterOrSpace() function", function() { - expect(ol.event.isEnterOrSpace({type: 'keypress', keyCode: 13})).toBe(true); - expect(ol.event.isEnterOrSpace({type: 'keypress', keyCode: 32})).toBe(true); - expect(ol.event.isEnterOrSpace({type: 'keypress', keyCode: 3})).toBe(true); - }); - });