diff --git a/lib/OpenLayers/Layer.js b/lib/OpenLayers/Layer.js index efaeb8f876..6a59aeada9 100644 --- a/lib/OpenLayers/Layer.js +++ b/lib/OpenLayers/Layer.js @@ -569,6 +569,17 @@ OpenLayers.Layer = OpenLayers.Class({ this.display(display); }, + /** + * Method: moveByPx + * Move the layer based on pixel vector. To be implemented by subclasses. + * + * Parameters: + * dx - {Number} The x coord of the displacement vector. + * dy - {Number} The y coord of the displacement vector. + */ + moveByPx: function(dx, dy) { + }, + /** * Method: setMap * Set the map property for the layer. This is done through an accessor @@ -1201,22 +1212,17 @@ OpenLayers.Layer = OpenLayers.Class({ */ getLonLatFromViewPortPx: function (viewPortPx) { var lonlat = null; - if (viewPortPx != null) { - var size = this.map.getSize(); - var center = this.map.getCenter(); - if (center) { - var res = this.map.getResolution(); - - var delta_x = viewPortPx.x - (size.w / 2); - var delta_y = viewPortPx.y - (size.h / 2); - - lonlat = new OpenLayers.LonLat(center.lon + delta_x * res , - center.lat - delta_y * res); + var map = this.map; + if (viewPortPx != null && map.minPx) { + var res = map.getResolution(); + var maxExtent = map.getMaxExtent(); + var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left; + var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top; + lonlat = new OpenLayers.LonLat(lon, lat); - if (this.wrapDateLine) { - lonlat = lonlat.wrapDateLine(this.maxExtent); - } - } // else { DEBUG STATEMENT } + if (this.wrapDateLine) { + lonlat = lonlat.wrapDateLine(this.maxExtent); + } } return lonlat; }, diff --git a/lib/OpenLayers/Layer/EventPane.js b/lib/OpenLayers/Layer/EventPane.js index 5382bcb41e..a87a88c438 100644 --- a/lib/OpenLayers/Layer/EventPane.js +++ b/lib/OpenLayers/Layer/EventPane.js @@ -211,6 +211,22 @@ OpenLayers.Layer.EventPane = OpenLayers.Class(OpenLayers.Layer, { OpenLayers.Layer.prototype.setZIndex.apply(this, arguments); this.pane.style.zIndex = parseInt(this.div.style.zIndex) + 1; }, + + /** + * Method: moveByPx + * Move the layer based on pixel vector. To be implemented by subclasses. + * + * Parameters: + * dx - {Number} The x coord of the displacement vector. + * dy - {Number} The y coord of the displacement vector. + */ + moveByPx: function(dx, dy) { + OpenLayers.Layer.prototype.moveByPx.apply(this, arguments); + + if (this.dragPanMapObject) { + this.dragPanMapObject(dx, -dy); + } + }, /** * Method: moveTo diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js index 07229a4abd..371e192099 100644 --- a/lib/OpenLayers/Layer/Grid.js +++ b/lib/OpenLayers/Layer/Grid.js @@ -251,19 +251,37 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { if (forceReTile || !tilesBounds.containsBounds(bounds, true)) { this.initGriddedTiles(bounds); } else { - // we might have to shift our buffer tiles, schedule - // that - if (this.timerId != null) { - window.clearTimeout(this.timerId); - } - this.timerId = window.setTimeout( - this._moveGriddedTiles, - this.tileLoadingDelay - ); + this.scheduleMoveGriddedTiles(); } } } }, + + /** + * Method: moveByPx + * Move the layer based on pixel vector. + * + * Parameters: + * dx - {Number} + * dy - {Number} + */ + moveByPx: function(dx, dy) { + this.scheduleMoveGriddedTiles(); + }, + + /** + * Method: scheduleMoveGriddedTiles + * Schedule the move of tiles. + */ + scheduleMoveGriddedTiles: function() { + if (this.timerId != null) { + window.clearTimeout(this.timerId); + } + this.timerId = window.setTimeout( + this._moveGriddedTiles, + this.tileLoadingDelay + ); + }, /** * APIMethod: setTileSize @@ -682,7 +700,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, { var shifted = true; var buffer = this.buffer || 1; var tlLayer = this.grid[0][0].position; - var tlViewPort = this.map.getViewPortPxFromLayerPx(tlLayer); + var offsetX = parseInt(this.map.layerContainerDiv.style.left); + var offsetY = parseInt(this.map.layerContainerDiv.style.top); + tlViewPort = tlLayer.add(offsetX, offsetY); if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) { this.shiftColumn(true); } else if (tlViewPort.x < -this.tileSize.w * buffer) { diff --git a/lib/OpenLayers/Map.js b/lib/OpenLayers/Map.js index c45cbb1e1d..dc5d2edce6 100644 --- a/lib/OpenLayers/Map.js +++ b/lib/OpenLayers/Map.js @@ -415,6 +415,23 @@ OpenLayers.Map = OpenLayers.Class({ */ paddingForPopups : null, + /** + * Property: minPx + * {} Lower left of maxExtent in viewport pixel space. + * Used to verify in moveByPx that the new location we're moving to + * is valid. It is also used in the getLonLatFromViewPortPx function + * of Layer. + */ + minPx: null, + + /** + * Property: maxPx + * {} Top right of maxExtent in viewport pixel space. + * Used to verify in moveByPx that the new location we're moving to + * is valid. + */ + maxPx: null, + /** * Constructor: OpenLayers.Map * Constructor for a new OpenLayers.Map instance. There are two possible @@ -1140,7 +1157,7 @@ OpenLayers.Map = OpenLayers.Class({ if (OpenLayers.Util.indexOf(this.layers, newBaseLayer) != -1) { // preserve center and scale when changing base layers - var center = this.getCenter(); + var center = this.getCachedCenter(); var newResolution = OpenLayers.Util.getResolutionFromScale( this.getScale(), newBaseLayer.units ); @@ -1402,7 +1419,7 @@ OpenLayers.Map = OpenLayers.Class({ this.layers[i].onMapResize(); } - var center = this.getCenter(); + var center = this.getCachedCenter(); if (this.baseLayer != null && center != null) { var zoom = this.getZoom(); @@ -1453,7 +1470,7 @@ OpenLayers.Map = OpenLayers.Class({ var extent = null; if (center == null) { - center = this.getCenter(); + center = this.getCachedCenter(); } if (resolution == null) { resolution = this.getResolution(); @@ -1493,12 +1510,27 @@ OpenLayers.Map = OpenLayers.Class({ */ getCenter: function () { var center = null; - if (this.center) { - center = this.center.clone(); + var cachedCenter = this.getCachedCenter(); + if (cachedCenter) { + center = cachedCenter.clone(); } return center; }, + /** + * Method: getCachedCenter + * + * Returns: + * {} + */ + getCachedCenter: function() { + if (!this.center && this.size) { + this.center = this.getLonLatFromViewPortPx( + new OpenLayers.Pixel(this.size.w / 2, this.size.h / 2) + ); + } + return this.center; + }, /** * APIMethod: getZoom @@ -1527,21 +1559,29 @@ OpenLayers.Map = OpenLayers.Class({ animate: true, dragging: false }); - // getCenter - var centerPx = this.getViewPortPxFromLonLat(this.getCenter()); + if (options.dragging) { + if (dx != 0 || dy != 0) { + this.moveByPx(dx, dy); + } + } else { + // if we don't have a center, we were using moveByPx previously + var forceSetCenter = !this.center; + + // getCenter + var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter()); - // adjust - var newCenterPx = centerPx.add(dx, dy); - - // only call setCenter if not dragging or there has been a change - if (!options.dragging || !newCenterPx.equals(centerPx)) { - var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx); - if (options.animate) { - this.panTo(newCenterLonLat); - } else { - this.setCenter(newCenterLonLat, null, options.dragging); - } - } + // adjust + var newCenterPx = centerPx.add(dx, dy); + + if (forceSetCenter || !newCenterPx.equals(centerPx)) { + var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx); + if (options.animate) { + this.panTo(newCenterLonLat); + } else { + this.setCenter(newCenterLonLat, null, options.dragging); + } + } + } }, @@ -1558,7 +1598,7 @@ OpenLayers.Map = OpenLayers.Class({ if (!this.panTween) { this.panTween = new OpenLayers.Tween(this.panMethod); } - var center = this.getCenter(); + var center = this.getCachedCenter(); // center will not change, don't do nothing if (lonlat.lon == center.lon && @@ -1621,6 +1661,71 @@ OpenLayers.Map = OpenLayers.Class({ 'caller': 'setCenter' }); }, + + /** + * Method: moveByPx + * Drag the map by pixels. + * + * Parameters: + * dx - {Number} + * dy - {Number} + */ + moveByPx: function(dx, dy) { + dx = Math.round(dx); + dy = Math.round(dy); + var hw = this.size.w / 2; + var hh = this.size.h / 2; + var x = hw + dx; + var y = hh + dy; + var valid = y <= this.maxPx.y && + y >= this.minPx.y; + var minX, maxX; + if (this.baseLayer.wrapDateLine === true) { + minX = this.minPx.x, maxX = this.maxPx.x; + } else { + valid = valid && + x <= this.maxPx.x && + x >= this.minPx.x; + } + if (this.restrictedExtent && valid) { + valid = !(this.maxPx.x - x < hw || + x - this.minPx.x < hw || + this.maxPx.y - y < hh || + y - this.minPx.y < hh); + } + if (valid) { + this.center = null; + if (dx) { + this.layerContainerDiv.style.left = + parseInt(this.layerContainerDiv.style.left) - dx + "px"; + this.minPx.x -= dx; + this.maxPx.x -= dx; + if (this.baseLayer.wrapDateLine === true) { + if (this.maxPx.x > maxX) { + this.maxPx.x -= (maxX - minX); + }; + if (this.minPx.x < minX) { + this.minPx.x += (maxX - minX); + }; + } + } + if (dy) { + this.layerContainerDiv.style.top = + parseInt(this.layerContainerDiv.style.top) - dy + "px"; + this.minPx.y -= dy; + this.maxPx.y -= dy; + } + var layer, i, len; + for (i=0, len=this.layers.length; i} */ centerLayerContainer: function (lonlat) { - var originPx = this.getViewPortPxFromLonLat(this.layerContainerOrigin); var newPx = this.getViewPortPxFromLonLat(lonlat); if ((originPx != null) && (newPx != null)) { - this.layerContainerDiv.style.left = Math.round(originPx.x - newPx.x) + "px"; - this.layerContainerDiv.style.top = Math.round(originPx.y - newPx.y) + "px"; - } + var oldLeft = parseInt(this.layerContainerDiv.style.left); + var oldTop = parseInt(this.layerContainerDiv.style.top); + var newLeft = Math.round(originPx.x - newPx.x); + var newTop = Math.round(originPx.y - newPx.y); + this.layerContainerDiv.style.left = newLeft + "px"; + this.layerContainerDiv.style.top = newTop + "px"; + var dx = oldLeft - newLeft; + var dy = oldTop - newTop; + this.minPx.x -= dx; + this.maxPx.x -= dx; + this.minPx.y -= dy; + this.maxPx.y -= dy; + } }, /** @@ -2225,7 +2351,7 @@ OpenLayers.Map = OpenLayers.Class({ var size = this.getSize(); var w_deg = size.w * res; var h_deg = size.h * res; - var center = this.getCenter(); + var center = this.getCachedCenter(); var extent = new OpenLayers.Bounds(center.lon - w_deg / 2, center.lat - h_deg / 2, @@ -2337,8 +2463,8 @@ OpenLayers.Map = OpenLayers.Class({ * {} The geodesic size of the pixel in kilometers. */ getGeodesicPixelSize: function(px) { - var lonlat = px ? this.getLonLatFromPixel(px) : (this.getCenter() || - new OpenLayers.LonLat(0, 0)); + var lonlat = px ? this.getLonLatFromPixel(px) : ( + this.getCachedCenter() || new OpenLayers.LonLat(0, 0)); var res = this.getResolution(); var left = lonlat.add(-res / 2, 0); var right = lonlat.add(res / 2, 0); diff --git a/tests/Map.html b/tests/Map.html index 63c3cbca05..351f0545cb 100644 --- a/tests/Map.html +++ b/tests/Map.html @@ -1323,7 +1323,7 @@ var m = { 'baseLayer': { 'units': {} }, 'getSize': function() { return {'w': 10, 'h': 15}; }, - 'getCenter': function() { return {'lon': -5, 'lat': -25}; }, + 'getCachedCenter': function() { return {'lon': -5, 'lat': -25}; }, 'zoomToExtent': function(extent, closest) { t.ok(extent.equals(g_ExpectedExtent), "extent correctly calculated for zoomToExtent()"); t.ok(closest == g_Closest, "closest correctly passed on to zoomToExtent()"); @@ -1701,6 +1701,41 @@ map.destroy(); } + function test_moveByPx(t) { + t.plan(8); + + var map = new OpenLayers.Map({ + div: 'map', + maxExtent: new OpenLayers.Bounds(-50, -50, 50, 50), + restrictedExtent: new OpenLayers.Bounds(-10, -10, 10, 10), + layers: [ + new OpenLayers.Layer('name', {isBaseLayer: true}) + ] + }); + map.zoomToExtent(new OpenLayers.Bounds(-1, -1, 1, 1)); + + // check initial state + t.eq(map.layerContainerDiv.style.left, '0px', 'layer container left correct'); + t.eq(map.layerContainerDiv.style.top, '0px', 'layer container top correct'); + + // move to a valid position + map.moveByPx(-455, 455); + t.eq(map.layerContainerDiv.style.left, '455px', 'layer container left correct'); + t.eq(map.layerContainerDiv.style.top, '-455px', 'layer container top correct'); + + // move outside the max extent + map.moveByPx(-4500, 4500); + t.eq(map.layerContainerDiv.style.left, '455px', 'layer container left correct'); + t.eq(map.layerContainerDiv.style.top, '-455px', 'layer container top correct'); + + // move outside the restricted extent + map.moveByPx(-500, 500); + t.eq(map.layerContainerDiv.style.left, '455px', 'layer container left correct'); + t.eq(map.layerContainerDiv.style.top, '-455px', 'layer container top correct'); + + map.destroy(); + } +