Merge pull request #5 from elemoine/clientzoom

add continuous zooming support for tiled layers
This commit is contained in:
Éric Lemoine
2011-10-07 13:30:50 -07:00
18 changed files with 712 additions and 66 deletions

72
examples/clientzoom.html Normal file
View File

@@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>OpenLayers Client Zoom Example</title>
<link rel="stylesheet" href="../theme/default/style.css" type="text/css"/>
<link rel="stylesheet" href="style.css" type="text/css">
<script src="../lib/Firebug/firebug.js"></script>
<script src="../lib/OpenLayers.js"></script>
<script src="clientzoom.js"></script>
<style>
.olControlAttribution {
bottom: 5px;
}
#map {
width: 600px;
height: 400px;
}
</style>
</head>
<body onload="init();">
<h1 id="title">Client Zoom</h1>
<div id="tags">
client zoom continuous zooming
</div>
<p id="shortdesc">
This example demonstrates the <strong>"client zoom"</strong>
functionality, where OpenLayers stretches the layer div when the
resolution is not supported by that layer's tile service.
</p>
<div id="map"></div>
<div id="docs">
<p>
The map of this example is configured with 22 resolutions, while
the OSM tile server supports the first 19 resolutions only. When
the zoom level is 19, 20 or 21 "client zoom" is applied to the OSM
layer, i.e. the OSM layer div is stretched as necessary. The map's
initial zoom is 18. So if you zoom in using the zoom bar's "+"
button you'll effectively trigger "client zoom".
</p>
<p>
For demonstration purpose the map of this example has
<code>fractionalZoom</code> set to true. So "client zoom" also
applies if you choose arbitrary zoom levels using the slider of the
zoom bar, or shift-drag boxes to zoom to arbitrary extents.
"client zoom" therefore allows continous zooming for tiled layers.
</p>
<p>
Enabling "client zoom" on a layer is done by passing
<code>serverResolutions</code> to the layer constructor.
<code>serverResolutions</code> is the list of resolutions supported
by the tile service. See the <a href="clientzoom.js"
target="_blank"> clientzoom.js source</a>.
</p>
</div>
</body>
</html>

43
examples/clientzoom.js Normal file
View File

@@ -0,0 +1,43 @@
var map;
function init() {
map = new OpenLayers.Map({
div: "map",
projection: "EPSG:900913",
units: "m",
maxExtent: new OpenLayers.Bounds(
-20037508.34, -20037508.34, 20037508.34, 20037508.34
),
controls: [],
fractionalZoom: true
});
var osm = new OpenLayers.Layer.OSM(null, null, {
resolutions: [156543.03390625, 78271.516953125, 39135.7584765625,
19567.87923828125, 9783.939619140625, 4891.9698095703125,
2445.9849047851562, 1222.9924523925781, 611.4962261962891,
305.74811309814453, 152.87405654907226, 76.43702827453613,
38.218514137268066, 19.109257068634033, 9.554628534317017,
4.777314267158508, 2.388657133579254, 1.194328566789627,
0.5971642833948135, 0.25, 0.1, 0.05],
serverResolutions: [156543.03390625, 78271.516953125, 39135.7584765625,
19567.87923828125, 9783.939619140625,
4891.9698095703125, 2445.9849047851562,
1222.9924523925781, 611.4962261962891,
305.74811309814453, 152.87405654907226,
76.43702827453613, 38.218514137268066,
19.109257068634033, 9.554628534317017,
4.777314267158508, 2.388657133579254,
1.194328566789627, 0.5971642833948135],
transitionEffect: 'resize'
});
map.addLayers([osm]);
map.addControls([
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.Attribution(),
new OpenLayers.Control.PanZoomBar()
]);
map.setCenter(new OpenLayers.LonLat(659688.852138, 5710701.2962197), 18);
}

View File

@@ -1245,11 +1245,11 @@ OpenLayers.Layer = OpenLayers.Class({
* {<OpenLayers.Pixel>} An <OpenLayers.Pixel> which is the passed-in
* <OpenLayers.LonLat>,translated into view port pixels.
*/
getViewPortPxFromLonLat: function (lonlat) {
getViewPortPxFromLonLat: function (lonlat, resolution) {
var px = null;
if (lonlat != null) {
var resolution = this.map.getResolution();
var extent = this.map.getExtent();
resolution = resolution || this.map.getResolution();
var extent = this.map.calculateBounds(null, resolution);
px = new OpenLayers.Pixel(
(1/resolution * (lonlat.lon - extent.left)),
(1/resolution * (extent.top - lonlat.lat))

View File

@@ -98,6 +98,13 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/
tileLoadingDelay: 100,
/**
* Property: serverResolutions
* {Array(Number}} This property is documented in subclasses as
* an API property.
*/
serverResolutions: null,
/**
* Property: timerId
* {Number} - The id of the tileLoadingDelay timer.
@@ -221,8 +228,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* dragging - {Boolean}
*/
moveTo:function(bounds, zoomChanged, dragging) {
OpenLayers.Layer.HTTPRequest.prototype.moveTo.apply(this, arguments);
bounds = bounds || this.map.getExtent();
if (bounds != null) {
@@ -238,18 +246,41 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
// We want to redraw whenever even the slightest part of the
// current bounds is not contained by our tile.
// (thus, we do not specify partial -- its default is false)
if ( forceReTile ||
if ( forceReTile ||
(!dragging && !tilesBounds.containsBounds(bounds))) {
this.initSingleTile(bounds);
}
} else {
// if the bounds have changed such that they are not even
// *partially* contained by our tiles (IE user has
// programmatically panned to the other side of the earth)
// then we want to reTile (thus, partial true).
//
if (forceReTile || !tilesBounds.containsBounds(bounds, true)) {
forceReTile = forceReTile ||
!tilesBounds.containsBounds(bounds, true);
var resolution = this.map.getResolution();
var serverResolution =
this.getServerResolution(resolution);
if(resolution !== serverResolution) {
bounds = this.map.calculateBounds(null, serverResolution);
if(forceReTile) {
// stretch the layer div
var scale = serverResolution / resolution;
this.transformDiv(scale);
}
} else {
// reset the layer width, height, left, top, to deal with
// the case where the layer was previously transformed
this.div.style.width = '100%';
this.div.style.height = '100%';
this.div.style.left = '0%';
this.div.style.top = '0%';
}
if(forceReTile) {
this.initGriddedTiles(bounds);
} else {
this.scheduleMoveGriddedTiles();
@@ -258,6 +289,88 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
}
},
/**
* Method: getServerResolution
* Return the server-supported resolution that is the closest to
* the resolution passed as a parameter. If no resolution is
* passed the map resolution is used.
*
* Parameters:
* resolution - {Number} The base resolution.
*
* Returns:
* {Number} The closest server supported resolution.
*/
getServerResolution: function(resolution) {
resolution = resolution || this.map.getResolution();
if(this.serverResolutions &&
OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {
var i, serverResolution;
for(var i=this.serverResolutions.length-1; i>= 0; i--) {
serverResolution = this.serverResolutions[i];
if(serverResolution > resolution) {
resolution = serverResolution;
break;
}
}
if(i === -1) {
throw 'no appropriate resolution in serverResolutions';
}
}
return resolution;
},
/**
* Method: getServerZoom
* Return the zoom value corresponding to the best zoom supported by the server
* resolution.
*
* Returns:
* {Number} The closest server supported zoom.
*/
getServerZoom: function() {
return this.map.getZoomForResolution(this.getServerResolution());
},
/**
* Method: transformDiv
* Transform the layer div.
*
* Parameters:
* scale - {Number} The value by which the layer div is to
* be scaled.
*/
transformDiv: function(scale) {
// scale the layer div
this.div.style.width = 100 * scale + '%';
this.div.style.height = 100 * scale + '%';
// and translate the layer div as necessary
var size = this.map.getSize();
var lcX = parseInt(this.map.layerContainerDiv.style.left, 10);
var lcY = parseInt(this.map.layerContainerDiv.style.top, 10);
var x = (lcX - (size.w / 2.)) * (scale - 1);
var y = (lcY - (size.h / 2.)) * (scale - 1);
this.div.style.left = x + '%';
this.div.style.top = y + '%';
},
/**
* Method: getResolutionScale
* Return the value by which the layer is currently scaled.
*
* Returns:
* {Number} The resolution scale.
*/
getResolutionScale: function() {
return parseInt(this.div.style.width, 10) / 100;
},
/**
* Method: moveByPx
* Move the layer based on pixel vector.
@@ -471,7 +584,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
* bounds - {<OpenLayers.Bounds>}
*/
initGriddedTiles:function(bounds) {
// work out mininum number of rows and columns; this is the number of
// tiles required to cover the viewport plus at least one for panning
@@ -482,7 +595,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
Math.max(1, 2 * this.buffer);
var origin = this.getTileOrigin();
var resolution = this.map.getResolution();
var resolution = this.getServerResolution();
var tileLayout = this.calculateGridLayout(bounds, origin, resolution);
@@ -504,7 +617,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
var layerContainerDivLeft = parseInt(this.map.layerContainerDiv.style.left);
var layerContainerDivTop = parseInt(this.map.layerContainerDiv.style.top);
do {
var row = this.grid[rowidx++];
@@ -713,17 +826,25 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
moveGriddedTiles: function() {
var shifted = true;
var buffer = this.buffer || 1;
var tlLayer = this.grid[0][0].position;
var scale = this.getResolutionScale();
var tlLayer = this.grid[0][0].position.clone();
tlLayer.x *= scale;
tlLayer.y *= scale;
tlLayer = tlLayer.add(parseInt(this.div.style.left, 10),
parseInt(this.div.style.top, 10));
var offsetX = parseInt(this.map.layerContainerDiv.style.left);
var offsetY = parseInt(this.map.layerContainerDiv.style.top);
var tlViewPort = tlLayer.add(offsetX, offsetY);
if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) {
var tileSize = this.tileSize.clone();
tileSize.w *= scale;
tileSize.h *= scale;
if (tlViewPort.x > -tileSize.w * (buffer - 1)) {
this.shiftColumn(true);
} else if (tlViewPort.x < -this.tileSize.w * buffer) {
} else if (tlViewPort.x < -tileSize.w * buffer) {
this.shiftColumn(false);
} else if (tlViewPort.y > -this.tileSize.h * (buffer - 1)) {
} else if (tlViewPort.y > -tileSize.h * (buffer - 1)) {
this.shiftRow(true);
} else if (tlViewPort.y < -this.tileSize.h * buffer) {
} else if (tlViewPort.y < -tileSize.h * buffer) {
this.shiftRow(false);
} else {
shifted = false;
@@ -749,7 +870,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
var grid = this.grid;
var modelRow = grid[modelRowIndex];
var resolution = this.map.getResolution();
var resolution = this.getServerResolution();
var deltaY = (prepend) ? -this.tileSize.h : this.tileSize.h;
var deltaLat = resolution * -deltaY;
@@ -782,7 +903,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/
shiftColumn: function(prepend) {
var deltaX = (prepend) ? -this.tileSize.w : this.tileSize.w;
var resolution = this.map.getResolution();
var resolution = this.getServerResolution();
var deltaLon = resolution * deltaX;
for (var i=0, len=this.grid.length; i<len; i++) {
@@ -805,7 +926,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
}
}
},
/**
* Method: removeExcessTiles
* When the size of the map or the buffer changes, we may need to

View File

@@ -87,8 +87,17 @@ OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
/**
* APIProperty: serverResolutions
* {Array} A list of all resolutions available on the server. Only set this
* property if the map resolutions differs from the server.
* {Array} A list of all resolutions available on the server. Only set this
* property if the map resolutions differ from the server. This
* property serves two purposes. (a) <serverResolutions> can include
* resolutions that the server supports and that you don't want to
* provide with this layer; you can also look at <zoomOffset>, which is
* an alternative to <serverResolutions> for that specific purpose.
* (b) The map can work with resolutions that aren't supported by
* the server, i.e. that aren't in <serverResolutions>. When the
* map is displayed in such a resolution data for the closest
* server-supported resolution is loaded and the layer div is
* stretched as necessary.
*/
serverResolutions: null,
@@ -162,12 +171,12 @@ OpenLayers.Layer.TMS = OpenLayers.Class(OpenLayers.Layer.Grid, {
*/
getURL: function (bounds) {
bounds = this.adjustBounds(bounds);
var res = this.map.getResolution();
var res = this.getServerResolution();
var x = Math.round((bounds.left - this.tileOrigin.lon) / (res * this.tileSize.w));
var y = Math.round((bounds.bottom - this.tileOrigin.lat) / (res * this.tileSize.h));
var z = this.serverResolutions != null ?
OpenLayers.Util.indexOf(this.serverResolutions, res) :
this.map.getZoom() + this.zoomOffset;
this.getServerZoom() + this.zoomOffset;
var path = this.serviceVersion + "/" + this.layername + "/" + z + "/" + x + "/" + y + "." + this.type;
var url = this.url;
if (OpenLayers.Util.isArray(url)) {

View File

@@ -37,8 +37,15 @@ OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {
/**
* APIProperty: serverResolutions
* {Array} A list of all resolutions available on the server. Only set this
* property if the map resolutions differs from the server.
* {Array} A list of all resolutions available on the server. Only set this
* property if the map resolutions differ from the server. This
* property serves two purposes. (a) <serverResolutions> can include
* resolutions that the server supports and that you don't want to
* provide with this layer. (b) The map can work with resolutions
* that aren't supported by the server, i.e. that aren't in
* <serverResolutions>. When the map is displayed in such a resolution
* data for the closest server-supported resolution is loaded and the
* layer div is stretched as necessary.
*/
serverResolutions: null,
@@ -102,7 +109,7 @@ OpenLayers.Layer.TileCache = OpenLayers.Class(OpenLayers.Layer.Grid, {
* passed-in bounds and appropriate tile size specified as parameters.
*/
getURL: function(bounds) {
var res = this.map.getResolution();
var res = this.getServerResolution();
var bbox = this.maxExtent;
var size = this.tileSize;
var tileX = Math.round((bounds.left - bbox.left) / (res * size.w));

View File

@@ -150,7 +150,23 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
* the <matrixIds> property. Defaults to 0 (no zoom offset).
*/
zoomOffset: 0,
/**
* APIProperty: serverResolutions
* {Array} A list of all resolutions available on the server. Only set this
* property if the map resolutions differ from the server. This
* property serves two purposes. (a) <serverResolutions> can include
* resolutions that the server supports and that you don't want to
* provide with this layer; you can also look at <zoomOffset>, which is
* an alternative to <serverResolutions> for that specific purpose.
* (b) The map can work with resolutions that aren't supported by
* the server, i.e. that aren't in <serverResolutions>. When the
* map is displayed in such a resolution data for the closest
* server-supported resolution is loaded and the layer div is
* stretched as necessary.
*/
serverResolutions: null,
/**
* Property: formatSuffixMap
* {Object} a map between WMTS 'format' request parameter and tile image file suffix
@@ -308,6 +324,17 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
// copy/set any non-init, non-simple values here
return obj;
},
/**
* Method: getIdentifier
* Get the current index in the matrixIds array.
*/
getIdentifier: function() {
return this.serverResolutions != null ?
OpenLayers.Util.indexOf(this.serverResolutions,
this.getServerResolution()) :
this.getServerZoom() + this.zoomOffset;
},
/**
* Method: getMatrix
@@ -316,7 +343,7 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
getMatrix: function() {
var matrix;
if (!this.matrixIds || this.matrixIds.length === 0) {
matrix = {identifier: this.map.getZoom() + this.zoomOffset};
matrix = {identifier: this.getIdentifier()};
} else {
// get appropriate matrix given the map scale if possible
if ("scaleDenominator" in this.matrixIds[0]) {
@@ -324,7 +351,7 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
var denom =
OpenLayers.METERS_PER_INCH *
OpenLayers.INCHES_PER_UNIT[this.units] *
this.map.getResolution() / 0.28E-3;
this.getServerResolution() / 0.28E-3;
var diff = Number.POSITIVE_INFINITY;
var delta;
for (var i=0, ii=this.matrixIds.length; i<ii; ++i) {
@@ -336,7 +363,7 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
}
} else {
// fall back on zoom as index
matrix = this.matrixIds[this.map.getZoom() + this.zoomOffset];
matrix = this.matrixIds[this.getIdentifier()];
}
}
return matrix;
@@ -356,7 +383,7 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
* (respectively) of the given location within the target tile.
*/
getTileInfo: function(loc) {
var res = this.map.getResolution();
var res = this.getServerResolution();
var fx = (loc.lon - this.tileOrigin.lon) / (res * this.tileSize.w);
var fy = (this.tileOrigin.lat - loc.lat) / (res * this.tileSize.h);

View File

@@ -49,7 +49,16 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
/**
* APIProperty: serverResolutions
* {Array} A list of all resolutions available on the server. Only set this
* property if the map resolutions differs from the server.
* property if the map resolutions differ from the server. This
* property serves two purposes. (a) <serverResolutions> can include
* resolutions that the server supports and that you don't want to
* provide with this layer; you can also look at <zoomOffset>, which is
* an alternative to <serverResolutions> for that specific purpose.
* (b) The map can work with resolutions that aren't supported by
* the server, i.e. that aren't in <serverResolutions>. When the
* map is displayed in such a resolution data for the closest
* server-supported resolution is loaded and the layer div is
* stretched as necessary.
*/
serverResolutions: null,
@@ -139,7 +148,7 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
* {Object} - an object with x, y and z properties.
*/
getXYZ: function(bounds) {
var res = this.map.getResolution();
var res = this.getServerResolution();
var x = Math.round((bounds.left - this.maxExtent.left) /
(res * this.tileSize.w));
var y = Math.round((this.maxExtent.top - bounds.top) /
@@ -147,7 +156,7 @@ OpenLayers.Layer.XYZ = OpenLayers.Class(OpenLayers.Layer.Grid, {
var resolutions = this.serverResolutions || this.resolutions;
var z = this.zoomOffset == 0 ?
OpenLayers.Util.indexOf(resolutions, res) :
this.map.getZoom() + this.zoomOffset;
this.getServerZoom() + this.zoomOffset;
var limit = Math.pow(2, z);
if (this.wrapDateLine)

View File

@@ -520,6 +520,8 @@ OpenLayers.Map = OpenLayers.Class({
// the layerContainerDiv is the one that holds all the layers
id = this.id + "_OpenLayers_Container";
this.layerContainerDiv = OpenLayers.Util.createDiv(id);
this.layerContainerDiv.style.width = '100px';
this.layerContainerDiv.style.height = '100px';
this.layerContainerDiv.style.zIndex=this.Z_INDEX_BASE['Popup']-1;
this.eventsDiv.appendChild(this.layerContainerDiv);

View File

@@ -90,6 +90,22 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, {
* Returns:
* {DOMElement} A copy of the tile's markup.
*/
/**
* Method: getTileResolution
* Get the tile's actual resolution.
*
* Returns:
* {Number}
*/
getTileResolution: function() {
var layer = this.layer,
map = layer.map,
mapResolution = map.getResolution();
return layer.getServerResolution ?
layer.getServerResolution(mapResolution) :
mapResolution;
},
/**
* Method: setBackBufferData
@@ -99,7 +115,7 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, {
setBackBufferData: function() {
this.backBufferData = OpenLayers.Util.extend(this.backBufferData, {
bounds: this.bounds,
resolution: this.layer.map.getResolution()
resolution: this.getTileResolution()
});
},
@@ -119,8 +135,9 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, {
data = this.backBufferData,
tile = this.getTile(),
backBuffer = data.tile,
resolution = data.resolution,
ratio = resolution ? resolution / map.getResolution() : 1,
prevResolution = data.resolution,
nextResolution = this.getTileResolution(),
ratio = prevResolution ? prevResolution / nextResolution : 1,
// Cases where we don't position and return a back buffer, but only
// update backBufferData and return undefined:
@@ -131,7 +148,8 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, {
// (3) we don't have a tile available that we could use as buffer
noTile = !(tile && tile.childNodes.length > 0),
// (4) no backbuffer is displayed for a tile that's still loading
noBackBuffer = !backBuffer && this.isLoading;
noBackBuffer = !backBuffer && this.isLoading;
if (notNeeded || noParent || noTile || noBackBuffer) {
this.setBackBufferData();
return;
@@ -139,26 +157,26 @@ OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, {
// Create a back buffer tile and add it to the DOM
if (!backBuffer) {
backBuffer = this.createBackBuffer();
backBuffer = this.insertBackBuffer();
// some browsers fire the onload event before the image is
// displayed, so we keep the buffer until the whole layer finished
// loading to avoid visual glitches
layer.events.register("loadend", this, this.resetBackBuffer);
data.tile = backBuffer;
layer.div.insertBefore(backBuffer, tile);
}
// Position the back buffer now that we have one
var lonLat = {lon: data.bounds.left, lat: data.bounds.top},
position = map.getPixelFromLonLat(lonLat),
position = layer.getViewPortPxFromLonLat(lonLat, nextResolution),
containerStyle = map.layerContainerDiv.style,
leftOffset = parseInt(containerStyle.left, 10),
topOffset = parseInt(containerStyle.top, 10),
style = backBuffer.style;
style.left = (position.x - leftOffset) + "px";
style.top = (position.y - topOffset) + "px";
style.width = (this.size.w * ratio) + "px";
style.height = (this.size.h * ratio) + "px";
style.left = (position.x - leftOffset) + "%";
style.top = (position.y - topOffset) + "%";
style.width = (this.size.w * ratio) + "%";
style.height = (this.size.h * ratio) + "%";
return backBuffer;
},

View File

@@ -187,10 +187,10 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
*/
positionTile: function() {
var style = this.frame.style;
style.left = this.position.x + "px";
style.top = this.position.y + "px";
style.width = this.size.w + "px";
style.height = this.size.h + "px";
style.left = this.position.x + "%";
style.top = this.position.y + "%";
style.width = this.size.w + "%";
style.height = this.size.h + "%";
},
/**
@@ -320,14 +320,16 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
},
/**
* Method: createBackBuffer
* Create a copy of this tile's markup for the <layer>'s backBufferDiv
* Method: insertBackBuffer
* Create a copy of this tile's markup and insert it to the layer
* div.
*
* Returns:
* {DOMElement} a clone of the tile content
* {DOMElement} The back buffer.
*/
createBackBuffer: function() {
insertBackBuffer: function() {
var frame = this.frame.cloneNode(false);
this.layer.div.insertBefore(frame, this.frame);
frame.appendChild(this.imgDiv);
this.imgDiv = null;
return frame;

View File

@@ -75,8 +75,8 @@
t.eq( tile.url,
url + "?" + OpenLayers.Util.getParameterString(tParams),
"image src is created correctly via addtile" );
t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" );
var firstChild = layer.div.firstChild.firstChild;
t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );

View File

@@ -624,6 +624,195 @@
t.eq( layer.tileSize, null, "layer.tileSize is null after destroy" );
}
function test_getServerResolution(t) {
t.plan(4);
var layer = new OpenLayers.Layer.Grid('', '', {}, {});
var res;
res = layer.getServerResolution(1);
t.eq(res, 1, '[1] getServerResolution return value is correct');
layer.serverResolutions = [2, 1];
res = layer.getServerResolution(1);
t.eq(res, 1, '[2] getServerResolution return value is correct');
layer.serverResolutions = [2];
res = layer.getServerResolution(1);
t.eq(res, 2, '[3] getServerResolution return value is correct');
var exc;
layer.serverResolutions = [0.5];
try {
res = layer.getServerResolution(1);
} catch(e) {
exc = e;
}
t.ok(exc != undefined, '[4] getServerResolution generates exception');
}
function test_getServerZoom(t) {
t.plan(5);
var resolution, zoom;
var map = new OpenLayers.Map('map', {
resolutions: [8, 4, 2, 1, 0.5],
getResolution: function() {
return resolution;
}
});
var layer = new OpenLayers.Layer.WMS('', '', {}, {isBaseLayer: true});
map.addLayer(layer);
resolution = 8;
zoom = layer.getServerZoom();
t.eq(zoom, 0, '[1] getServerZoom return value is correct');
resolution = 4;
zoom = layer.getServerZoom();
t.eq(zoom, 1, '[2] getServerZoom return value is correct');
layer.serverResolutions = [2, 1];
resolution = 1;
zoom = layer.getServerZoom();
t.eq(zoom, 3, '[3] getServerZoom return value is correct');
layer.serverResolutions = [2];
resolution = 0.5;
zoom = layer.getServerZoom();
t.eq(zoom, 2, '[4] getServerZoom return value is correct');
var exc;
layer.serverResolutions = [0.5];
resolution = 1;
try {
zoom = layer.getServerZoom();
} catch(e) {
exc = e;
}
t.ok(exc != undefined, '[4] getServerZoom generates exception');
map.destroy();
}
function test_moveTo_scale(t) {
t.plan(11);
var map = new OpenLayers.Map('map', {
resolutions: [32, 16, 8, 4, 2, 1]
});
var layer = new OpenLayers.Layer.WMS('', '', {}, {
isBaseLayer: true,
serverResolutions: [32, 16, 8]
});
map.addLayer(layer);
// initial resolution is 8
map.setCenter(new OpenLayers.LonLat(0, 0), 2);
// test initial conditions
t.eq(layer.div.style.width, '100%', 'layer div scale is 1');
// change from resolution 8 to 4
map.zoomTo(3);
t.eq(layer.div.style.width, '200%', '[8->4] layer div scale is 2');
// change from resolution 8 to 2
map.zoomTo(2); map.zoomTo(4);
t.eq(layer.div.style.width, '400%', '[8->2] layer div scale is 4');
// change from resolution 8 to 1
map.zoomTo(2); map.zoomTo(5);
t.eq(layer.div.style.width, '800%', '[8->1] layer div scale is 8');
// change from resolution 4 to 2
map.zoomTo(3); map.zoomTo(4);
t.eq(layer.div.style.width, '400%', '[4->2] layer div scale is 4');
// change from resolution 4 to 1
map.zoomTo(3); map.zoomTo(5);
t.eq(layer.div.style.width, '800%', '[4->1] layer div scale is 8');
// change from resolution 2 to 1
map.zoomTo(4); map.zoomTo(5);
t.eq(layer.div.style.width, '800%', '[2->1] layer div scale is 8');
// change from resolution 1 to 2
map.zoomTo(5); map.zoomTo(4);
t.eq(layer.div.style.width, '400%', '[1->2] layer div scale is 4');
// change from resolution 1 to 4
map.zoomTo(5); map.zoomTo(3);
t.eq(layer.div.style.width, '200%', '[1->4] layer div scale is 2');
// change from resolution 1 to 8
map.zoomTo(5); map.zoomTo(2);
t.eq(layer.div.style.width, '100%', '[1->8] layer div scale is 1');
// change from resolution 1 to 16
map.zoomTo(5); map.zoomTo(1);
t.eq(layer.div.style.width, '100%', '[1->16] layer div scale is 1');
map.destroy();
}
function test_transformDiv(t) {
t.plan(8);
var map = new OpenLayers.Map('map4');
var layer = new OpenLayers.Layer.WMS('', '', {}, {
isBaseLayer: true
});
map.addLayer(layer);
map.zoomToMaxExtent();
// the layer container's dimensions are 100px width 100px height
// the position of the viewport center is 384, 256
layer.transformDiv(2);
t.eq(layer.div.style.width, '200%', '[1] layer div has correct width');
t.eq(layer.div.style.height, '200%', '[1] layer div has correct height');
t.eq(layer.div.style.left, '-384%', '[1] layer div has correct left');
t.eq(layer.div.style.top, '-256%', '[1] layer div has correct top');
// now move the layer container and test again
map.layerContainerDiv.style.left = '-1024px';
map.layerContainerDiv.style.top = '768px';
layer.transformDiv(2);
t.eq(layer.div.style.width, '200%', '[2] layer div has correct width');
t.eq(layer.div.style.height, '200%', '[2] layer div has correct height');
t.eq(layer.div.style.left, '-1408%', '[2] layer div has correct left');
t.eq(layer.div.style.top, '512%', '[2] layer div has correct top');
map.destroy();
}
function test_getResolutionScale(t) {
t.plan(1);
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS('', '', {}, {
isBaseLayer: true
});
map.addLayer(layer);
map.zoomToMaxExtent();
layer.transformDiv(2);
var scale = layer.getResolutionScale();
t.eq(scale, 2, 'getResolutionScale returns correct value');
map.destroy();
}
</script>
</head>
@@ -631,5 +820,6 @@
<div id="map" style="width:499px;height:549px;display:none"></div>
<div id="map2" style="width:500px;height:550px;display:none"></div>
<div id="map3" style="width:594px;height:464px;display:none"></div>
<div id="map4" style="width:768px;height:512px;display:none"></div>
</body>
</html>

View File

@@ -56,8 +56,8 @@
t.eq( tile.url,
url + "?" + OpenLayers.Util.getParameterString(tParams).replace(/,/g, "+"),
"image src is created correctly via addtile" );
t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" );
var firstChild = layer.div.firstChild.firstChild;
t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );

View File

@@ -86,8 +86,8 @@
t.eq( tile.url,
layer.getFullRequestString(tParams),
"image src is created correctly via addtile" );
t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" );
var firstChild = layer.div.firstChild.firstChild;
t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );
@@ -117,8 +117,8 @@
t.eq( tile.url,
layer.getFullRequestString(tParams),
"image src is created correctly via addtile" );
t.eq( tile.frame.style.top, "6px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5px", "image top is set correctly via addtile" );
t.eq( tile.frame.style.top, "6%", "image top is set correctly via addtile" );
t.eq( tile.frame.style.left, "5%", "image top is set correctly via addtile" );
var firstChild = layer.div.firstChild.firstChild;
t.eq( firstChild.nodeName.toLowerCase(), "img", "div first child is an image object" );

View File

@@ -258,6 +258,37 @@
t.ok( layer2.grid == null, "tiles appropriately destroyed");
map.destroy();
}
function test_getIdentifier(t) {
t.plan(2);
var map = new OpenLayers.Map('map');
var layer, identifier;
layer = new OpenLayers.Layer.WMTS({
name: "Blue Marble WMTS",
url: "http://example.com/wmts/",
layer: "world",
style: "blue_marble",
matrixSet: "arcgis_online",
tileSize: new OpenLayers.Size(512, 512),
requestEncoding: "REST",
});
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(0,0), 5);
layer.zoomOffset = 2;
identifier = layer.getIdentifier();
t.eq(identifier, 7, '[zoomOffset] getIdentifier return value is correct');
layer.serverResolutions = ['offset', 1.40625, 0.703125, 0.3515625, 0.17578125,
0.087890625, 0.0439453125];
identifier = layer.getIdentifier();
t.eq(identifier, 6, '[serverResolutions] getIdentifier return value is correct');
map.destroy();
}
</script>
</head>
<body>

View File

@@ -80,7 +80,7 @@
tile.isLoading = false;
map.zoomIn();
tile.updateBackBuffer();
t.eq(tile.backBufferData.tile.style.width, (layer.tileSize.w*2)+"px",
t.eq(tile.backBufferData.tile.style.width, (layer.tileSize.w*2)+"%",
"backBuffer frame correctly resized");
map.removeLayer(layer);
map.destroy();
@@ -106,9 +106,123 @@
map.destroy();
}
function test_updateBackBuffer_scaled_layer(t) {
t.plan(16);
//
// set up
//
var backBuffer;
var map = new OpenLayers.Map('map', {
resolutions: [32, 16, 8, 4, 2, 1]
});
var serverResolitions = layer.serverResolutions;
layer.serverResolutions = [32, 16, 8];
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(0, 0), 2);
tile = new OpenLayers.Tile.Image(layer, position, bounds, null);
tile.backBufferMode = 2; // transition effect
// mock createBackBuffer to avoid removing the image
// div from the tile
tile.insertBackBuffer = function() {
return this.frame.cloneNode(false);
};
tile.removeBackBuffer = function() {
};
//
// test
//
tile.draw();
// check initial state
t.eq(tile.backBufferData.resolution, 8,
'resolution 8 is set in the back buffer data');
tile.isLoading = false;
// change resolution from 8 to 4
map.zoomTo(3);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer == undefined,
'[8->4] updateBackBuffer returns undefined');
// change resolution from 8 to 2
map.zoomTo(2); tile.setBackBufferData(); map.zoomTo(4);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer == undefined,
'[8->2] updateBackBuffer returns undefined');
// change resolution from 16 to 4
map.zoomTo(1); tile.setBackBufferData(); map.zoomTo(3);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer != undefined,
'[16->4] updateBackBuffer returns a back buffer tile');
t.eq(backBuffer.style.width, '512%',
'[16->4] back buffer width is as expected');
t.eq(backBuffer.style.width, '512%',
'[16->4] back buffer height is as expected');
// change resolution from 32 to 1
map.zoomTo(0); tile.setBackBufferData(); map.zoomTo(5);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer != undefined,
'[32->1] updateBackBuffer returns a back buffer tile');
t.eq(backBuffer.style.width, '1024%',
'[32->1] back buffer width is as expected');
t.eq(backBuffer.style.width, '1024%',
'[32->1] back buffer height is as expected');
// change resolution from 4 to 2
map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(4);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer == undefined,
'[4->2] updateBackBuffer returns undefined');
// change resolution from 4 to 1
map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(5);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer == undefined,
'[4->1] updateBackBuffer returns undefined');
// change resolution from 1 to 4
map.zoomTo(5); tile.setBackBufferData(); map.zoomTo(3);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer == undefined,
'[1->4] updateBackBuffer returns undefined');
// change resolution from 4 to 8
map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(2);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer == undefined,
'[4->8] updateBackBuffer returns undefined');
// change resolution from 4 to 16
map.zoomTo(3); tile.setBackBufferData(); map.zoomTo(1);
backBuffer = tile.updateBackBuffer();
t.ok(backBuffer != undefined,
'[4->16] updateBackBuffer returns a back buffer tile');
t.eq(backBuffer.style.width, '128%',
'[4->16] back buffer width is as expected');
t.eq(backBuffer.style.width, '128%',
'[4->16] back buffer height is as expected');
//
// tear down
//
map.removeLayer(layer);
map.destroy();
}
</script>
</head>
<body>
<div id="map" style="height:550px;width:500px"></div>
</body>
</html>
</html>

View File

@@ -300,8 +300,8 @@
map.destroy();
}
function test_createBackBuffer(t) {
t.plan(3);
function test_insertBackBuffer(t) {
t.plan(4);
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
@@ -310,8 +310,9 @@
map.setCenter(new OpenLayers.LonLat(0,0), 5);
var tile = layer.grid[0][0];
var img = tile.imgDiv;
var backBuffer = tile.createBackBuffer();
var backBuffer = tile.insertBackBuffer();
t.eq(backBuffer.style.left, tile.frame.style.left, "backBuffer tile has same left style as frame");
t.ok(backBuffer.parentNode === layer.div, "backBuffer inserted into layer div");
t.ok(backBuffer.firstChild === img, "image appended to backBuffer");
t.ok(tile.imgDiv == null, "image reference removed from tile");
map.destroy();