+ A full-screen map for both desktop and mobile devices. Uses
+ language dependent CSS content and the WMTSCapabilities format to
+ retrieve layers from the ogdwien open data initiative of the City
+ of Vienna. Also has a lightweight custom anchor permalink
+ functionality and uses the Geolocate control.
+
+
+
+
+
diff --git a/examples/mobile-wmts-vienna.js b/examples/mobile-wmts-vienna.js
new file mode 100644
index 0000000000..595897051f
--- /dev/null
+++ b/examples/mobile-wmts-vienna.js
@@ -0,0 +1,230 @@
+var map;
+
+(function() {
+ OpenLayers.ProxyHost = "proxy.cgi?url=";
+
+ // Set document language for css content
+ document.documentElement.lang = (navigator.userLanguage || navigator.language).split("-")[0];
+
+ // A panel for switching between Aerial and Map, and for turning labels
+ // on and off.
+ var layerPanel = new OpenLayers.Control.Panel({
+ displayClass: "layerPanel",
+ autoActivate: true
+ });
+ var aerialButton = new OpenLayers.Control({
+ type: OpenLayers.Control.TYPE_TOOL,
+ displayClass: "aerialButton",
+ eventListeners: {
+ activate: function() {
+ if (aerial) {map.setBaseLayer(aerial);}
+ }
+ }
+ });
+ var mapButton = new OpenLayers.Control({
+ type: OpenLayers.Control.TYPE_TOOL,
+ displayClass: "mapButton",
+ eventListeners: {
+ activate: function() {
+ if (fmzk) {map.setBaseLayer(fmzk);}
+ }
+ }
+ });
+ var labelButton = new OpenLayers.Control({
+ type: OpenLayers.Control.TYPE_TOGGLE,
+ displayClass: "labelButton",
+ eventListeners: {
+ activate: function() {
+ if (labels) {labels.setVisibility(true);}
+ },
+ deactivate: function() {
+ if (labels) {labels.setVisibility(false);}
+ }
+ }
+ });
+ layerPanel.addControls([aerialButton, mapButton, labelButton]);
+
+ var zoomPanel = new OpenLayers.Control.ZoomPanel();
+
+ // Geolocate control for the Locate button - the locationupdated handler
+ // draws a cross at the location and a circle showing the accuracy radius.
+ zoomPanel.addControls([
+ new OpenLayers.Control.Geolocate({
+ type: OpenLayers.Control.TYPE_TOGGLE,
+ geolocationOptions: {
+ enableHighAccuracy: false,
+ maximumAge: 0,
+ timeout: 7000
+ },
+ eventListeners: {
+ activate: function() {
+ map.addLayer(vector);
+ },
+ deactivate: function() {
+ map.removeLayer(vector);
+ vector.removeAllFeatures();
+ },
+ locationupdated: function(e) {
+ vector.removeAllFeatures();
+ vector.addFeatures([
+ new OpenLayers.Feature.Vector(e.point, null, {
+ graphicName: 'cross',
+ strokeColor: '#f00',
+ strokeWidth: 2,
+ fillOpacity: 0,
+ pointRadius: 10
+ }),
+ new OpenLayers.Feature.Vector(
+ OpenLayers.Geometry.Polygon.createRegularPolygon(
+ new OpenLayers.Geometry.Point(e.point.x, e.point.y),
+ e.position.coords.accuracy / 2, 50, 0
+ ), null, {
+ fillOpacity: 0.1,
+ fillColor: '#000',
+ strokeColor: '#f00',
+ strokeOpacity: 0.6
+ }
+ )
+ ]);
+ map.zoomToExtent(vector.getDataExtent());
+ }
+ }
+ })
+ ]);
+
+ // Map with navigation controls optimized for touch devices
+ map = new OpenLayers.Map({
+ div: "map",
+ theme: null,
+ projection: "EPSG:3857",
+ units: "m",
+ maxExtent: new OpenLayers.Bounds(
+ -20037508.34, -20037508.34, 20037508.34, 20037508.34
+ ),
+ maxResolution: 156543.0339,
+ numZoomLevels: 20,
+ controls: [
+ new OpenLayers.Control.TouchNavigation({
+ mouseWheelOptions: {
+ cumulative: false,
+ interval: 20
+ },
+ dragPanOptions: {
+ enableKinetic: {
+ deceleration: 0.02
+ }
+ },
+ zoomBoxEnabled: false
+ }),
+ new OpenLayers.Control.Attribution(),
+ zoomPanel,
+ layerPanel
+ ],
+ eventListeners: {
+ moveend: function() {
+ // update anchor for permalinks
+ var ctr = map.getCenter();
+ window.location.hash = "x="+ctr.lon+"&y="+ctr.lat+"&z="+map.getZoom();
+ }
+ }
+ });
+ layerPanel.activateControl(mapButton);
+ layerPanel.activateControl(labelButton);
+
+ // Vector layer for the location cross and circle
+ var vector = new OpenLayers.Layer.Vector("Vector Layer");
+
+ // The WMTS layers we're going to add
+ var fmzk, aerial, labels;
+
+ // The WMTSCapabilities format and the default options for the layers
+ var format = new OpenLayers.Format.WMTSCapabilities(), defaults = {
+ requestEncoding: "REST",
+ matrixSet: "google3857",
+ transitionEffect: "resize",
+ tileLoadingDelay: 0,
+ attribution: 'Datenquelle: Stadt Wien - data.wien.gv.at'
+ };
+
+ // Request capabilities and create layers
+ OpenLayers.Request.GET({
+ url: "http://maps.wien.gv.at/wmts/1.0.0/WMTSCapabilities.xml",
+ success: function(request) {
+ var doc = request.responseText,
+ caps = format.read(doc);
+ fmzk = format.createLayer(caps, OpenLayers.Util.applyDefaults(
+ {layer:"fmzk", requestEncoding:"REST"}, defaults
+ ));
+ aerial = format.createLayer(caps, OpenLayers.Util.applyDefaults(
+ {layer:"lb", requestEncoding:"REST"}, defaults
+ ));
+ labels = format.createLayer(caps, OpenLayers.Util.applyDefaults(
+ {layer:"beschriftung", requestEncoding:"REST", isBaseLayer: false},
+ defaults
+ ));
+ map.addLayers([fmzk, aerial, labels]);
+
+ // zoom to initial extent or restore position from permalink
+ var extent = fmzk.tileFullExtent,
+ ctr = extent.getCenterLonLat(),
+ zoom = map.getZoomForExtent(extent, true),
+ params = OpenLayers.Util.getParameters("?"+window.location.hash.substr(1));
+ OpenLayers.Util.applyDefaults(params, {x:ctr.lon, y:ctr.lat, z:zoom});
+ map.setCenter(new OpenLayers.LonLat(params.x, params.y), params.z);
+ }
+ });
+})();
+
+// Reliably hide the address bar on Android and iOS devices. From
+// http://blog.nateps.com/how-to-hide-the-address-bar-in-a-full-screen
+(function() {
+ var page = document.getElementById("map"),
+ ua = navigator.userAgent,
+ iphone = ~ua.indexOf('iPhone') || ~ua.indexOf('iPod'),
+ ipad = ~ua.indexOf('iPad'),
+ ios = iphone || ipad,
+ // Detect if this is running as a fullscreen app from the homescreen
+ fullscreen = window.navigator.standalone,
+ android = ~ua.indexOf('Android'),
+ lastWidth = 0;
+
+ if (android) {
+ // Android's browser adds the scroll position to the innerHeight, just to
+ // make this really fucking difficult. Thus, once we are scrolled, the
+ // page height value needs to be corrected in case the page is loaded
+ // when already scrolled down. The pageYOffset is of no use, since it always
+ // returns 0 while the address bar is displayed.
+ window.onscroll = function() {
+ page.style.height = window.innerHeight + 'px';
+ };
+ }
+ var setupScroll = window.onload = function() {
+ // Start out by adding the height of the location bar to the width, so that
+ // we can scroll past it
+ if (ios) {
+ // iOS reliably returns the innerWindow size for documentElement.clientHeight
+ // but window.innerHeight is sometimes the wrong value after rotating
+ // the orientation
+ var height = document.documentElement.clientHeight;
+ // Only add extra padding to the height on iphone / ipod, since the ipad
+ // browser doesn't scroll off the location bar.
+ if (iphone && !fullscreen) height += 60;
+ page.style.height = height + 'px';
+ } else if (android) {
+ // The stock Android browser has a location bar height of 56 pixels, but
+ // this very likely could be broken in other Android browsers.
+ page.style.height = (window.innerHeight + 56) + 'px';
+ }
+ // Scroll after a timeout, since iOS will scroll to the top of the page
+ // after it fires the onload event
+ setTimeout(scrollTo, 0, 0, 1);
+ };
+ (window.onresize = function() {
+ var pageWidth = page.offsetWidth;
+ // Android doesn't support orientation change, so check for when the width
+ // changes to figure out when the orientation changes
+ if (lastWidth == pageWidth) return;
+ lastWidth = pageWidth;
+ setupScroll();
+ })();
+})();
diff --git a/examples/point-grid.html b/examples/point-grid.html
index 4cff89d85b..8508fdb473 100644
--- a/examples/point-grid.html
+++ b/examples/point-grid.html
@@ -1,7 +1,7 @@
-
+
OpenLayers Point Grid Example
diff --git a/examples/proxy.cgi b/examples/proxy.cgi
index c668218c48..4358e2ca03 100755
--- a/examples/proxy.cgi
+++ b/examples/proxy.cgi
@@ -21,7 +21,8 @@ allowedHosts = ['www.openlayers.org', 'openlayers.org',
'sigma.openplans.org', 'demo.opengeo.org',
'www.openstreetmap.org', 'sample.azavea.com',
'v2.suite.opengeo.org', 'v-swe.uni-muenster.de:8080',
- 'vmap0.tiles.osgeo.org', 'www.openrouteservice.org']
+ 'vmap0.tiles.osgeo.org', 'www.openrouteservice.org',
+ 'maps.wien.gv.at']
method = os.environ["REQUEST_METHOD"]
diff --git a/examples/snap-grid.html b/examples/snap-grid.html
index b6b592d23e..9d0604bd69 100644
--- a/examples/snap-grid.html
+++ b/examples/snap-grid.html
@@ -1,7 +1,7 @@
-
+
OpenLayers Snap Grid Example
diff --git a/lib/OpenLayers.js b/lib/OpenLayers.js
index b378b7d24b..22fb1b8500 100644
--- a/lib/OpenLayers.js
+++ b/lib/OpenLayers.js
@@ -121,7 +121,6 @@
"OpenLayers/Marker/Box.js",
"OpenLayers/Popup.js",
"OpenLayers/Tile.js",
- "OpenLayers/Tile/BackBufferable.js",
"OpenLayers/Tile/Image.js",
"OpenLayers/Tile/Image/IFrame.js",
"OpenLayers/Tile/WFS.js",
diff --git a/lib/OpenLayers/Format/OWSCommon/v1.js b/lib/OpenLayers/Format/OWSCommon/v1.js
index 7ee97593ce..00f4e2b91b 100644
--- a/lib/OpenLayers/Format/OWSCommon/v1.js
+++ b/lib/OpenLayers/Format/OWSCommon/v1.js
@@ -171,12 +171,24 @@ OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
this.readChildNodes(node, dcp.http);
},
"Get": function(node, http) {
- http.get = this.getAttributeNS(node,
- this.namespaces.xlink, "href");
+ if (!http.get) {
+ http.get = [];
+ }
+ var obj = {
+ url: this.getAttributeNS(node, this.namespaces.xlink, "href")
+ };
+ this.readChildNodes(node, obj);
+ http.get.push(obj);
},
"Post": function(node, http) {
- http.post = this.getAttributeNS(node,
- this.namespaces.xlink, "href");
+ if (!http.post) {
+ http.post = [];
+ }
+ var obj = {
+ url: this.getAttributeNS(node, this.namespaces.xlink, "href")
+ };
+ this.readChildNodes(node, obj);
+ http.post.push(obj);
},
"Parameter": function(node, operation) {
if (!operation.parameters) {
@@ -186,6 +198,14 @@ OpenLayers.Format.OWSCommon.v1 = OpenLayers.Class(OpenLayers.Format.XML, {
operation.parameters[name] = {};
this.readChildNodes(node, operation.parameters[name]);
},
+ "Constraint": function(node, obj) {
+ if (!obj.constraints) {
+ obj.constraints = {};
+ }
+ var name = node.getAttribute("name");
+ obj.constraints[name] = {};
+ this.readChildNodes(node, obj.constraints[name]);
+ },
"Value": function(node, allowedValues) {
allowedValues[this.getChildValue(node)] = true;
},
diff --git a/lib/OpenLayers/Format/WMTSCapabilities.js b/lib/OpenLayers/Format/WMTSCapabilities.js
index bc5d35d211..d2dca6172e 100644
--- a/lib/OpenLayers/Format/WMTSCapabilities.js
+++ b/lib/OpenLayers/Format/WMTSCapabilities.js
@@ -114,10 +114,13 @@ OpenLayers.Format.WMTSCapabilities = OpenLayers.Class(OpenLayers.Format.XML.Vers
layer = new OpenLayers.Layer.WMTS(
OpenLayers.Util.applyDefaults(config, {
- url: capabilities.operationsMetadata.GetTile.dcp.http.get,
+ url: config.requestEncoding === "REST" && layerDef.resourceUrl ?
+ layerDef.resourceUrl.tile.template :
+ capabilities.operationsMetadata.GetTile.dcp.http.get[0].url,
name: layerDef.title,
style: style.identifier,
- matrixIds: matrixSet.matrixIds
+ matrixIds: matrixSet.matrixIds,
+ tileFullExtent: matrixSet.bounds
})
);
}
diff --git a/lib/OpenLayers/Layer.js b/lib/OpenLayers/Layer.js
index 06940d117a..fcc919833f 100644
--- a/lib/OpenLayers/Layer.js
+++ b/lib/OpenLayers/Layer.js
@@ -314,13 +314,6 @@ OpenLayers.Layer = OpenLayers.Class({
*/
transitionEffect: null,
- /**
- * Property: SUPPORTED_TRANSITIONS
- * {Array} An immutable (that means don't change it!) list of supported
- * transitionEffect values.
- */
- SUPPORTED_TRANSITIONS: ['resize'],
-
/**
* Property: metadata
* {Object} This object can be used to store additional information on a
diff --git a/lib/OpenLayers/Layer/Grid.js b/lib/OpenLayers/Layer/Grid.js
index 30021877cb..ce6332a3c9 100644
--- a/lib/OpenLayers/Layer/Grid.js
+++ b/lib/OpenLayers/Layer/Grid.js
@@ -111,6 +111,34 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/
timerId: null,
+ /**
+ * Property: backBuffer
+ * {DOMElement} The back buffer.
+ */
+ backBuffer: null,
+
+ /**
+ * Property: gridResolution
+ * {Number} The resolution of the current grid. Used for backbuffering.
+ * This property is updated each the grid is initialized.
+ */
+ gridResolution: null,
+
+ /**
+ * Property: backBufferResolution
+ * {Number} The resolution of the current back buffer. This property is
+ * updated each time a back buffer is created.
+ */
+ backBufferResolution: null,
+
+ /**
+ * Property: backBufferLonLat
+ * {Object} The top-left corner of the current back buffer. Includes lon
+ * and lat properties. This object is updated each time a back buffer
+ * is created.
+ */
+ backBufferLonLat: null,
+
/**
* Constructor: OpenLayers.Layer.Grid
* Create a new grid layer
@@ -133,7 +161,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
this.events.addEventType("tileloaded");
this.grid = [];
-
+
this._moveGriddedTiles = OpenLayers.Function.bind(
this.moveGriddedTiles, this
);
@@ -159,6 +187,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
*/
destroy: function() {
this.clearGrid();
+ // clearGrid should remove any back buffer from the layer,
+ // so no need to call removeBackBuffer here
+
this.grid = null;
this.tileSize = null;
OpenLayers.Layer.HTTPRequest.prototype.destroy.apply(this, arguments);
@@ -180,6 +211,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
}
}
this.grid = [];
+ this.gridResolution = null;
}
},
@@ -212,6 +244,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
// we do not want to copy reference to grid, so we make a new array
obj.grid = [];
+ obj.gridResolution = null;
return obj;
},
@@ -240,14 +273,37 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
// total bounds of the tiles
var tilesBounds = this.getTilesBounds();
-
+
+ // the new map resolution
+ var resolution = this.map.getResolution();
+
+ // the server-supported resolution for the new map resolution
+ var serverResolution = this.getServerResolution(resolution);
+
if (this.singleTile) {
// 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 ||
(!dragging && !tilesBounds.containsBounds(bounds))) {
+
+ // In single tile mode with no transition effect, we insert
+ // a non-scaled backbuffer when the layer is moved. But if
+ // a zoom occurs right after a move, i.e. before the new
+ // image is received, we need to remove the backbuffer, or
+ // an ill-positioned image will be visible during the zoom
+ // transition.
+
+ if(zoomChanged && this.transitionEffect !== 'resize') {
+ this.removeBackBuffer();
+ }
+
+ if(!zoomChanged || this.transitionEffect === 'resize') {
+ this.applyBackBuffer(serverResolution);
+ }
+
this.initSingleTile(bounds);
}
} else {
@@ -260,10 +316,6 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
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) {
@@ -281,6 +333,9 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
}
if(forceReTile) {
+ if(zoomChanged && this.transitionEffect === 'resize') {
+ this.applyBackBuffer(serverResolution);
+ }
this.initGriddedTiles(bounds);
} else {
this.scheduleMoveGriddedTiles();
@@ -306,7 +361,7 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
if(this.serverResolutions &&
OpenLayers.Util.indexOf(this.serverResolutions, resolution) === -1) {
var i, serverResolution;
- for(var i=this.serverResolutions.length-1; i>= 0; i--) {
+ for(i=this.serverResolutions.length-1; i>= 0; i--) {
serverResolution = this.serverResolutions[i];
if(serverResolution > resolution) {
resolution = serverResolution;
@@ -352,14 +407,13 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
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);
+ var x = (lcX - (size.w / 2.0)) * (scale - 1);
+ var y = (lcY - (size.h / 2.0)) * (scale - 1);
this.div.style.left = x + '%';
this.div.style.top = y + '%';
},
-
/**
* Method: getResolutionScale
* Return the value by which the layer is currently scaled.
@@ -371,6 +425,96 @@ OpenLayers.Layer.Grid = OpenLayers.Class(OpenLayers.Layer.HTTPRequest, {
return parseInt(this.div.style.width, 10) / 100;
},
+ /**
+ * Method: applyBackBuffer
+ * Create, insert, scale and position a back buffer for the layer.
+ *
+ * Parameters:
+ * resolution - {Number} The resolution to transition to.
+ */
+ applyBackBuffer: function(resolution) {
+ var backBuffer = this.backBuffer;
+ if(!backBuffer) {
+ backBuffer = this.createBackBuffer();
+ if(!backBuffer) {
+ return;
+ }
+ this.div.insertBefore(backBuffer, this.div.firstChild);
+ this.backBuffer = backBuffer;
+
+ // set some information in the instance for subsequent
+ // calls to applyBackBuffer where the same back buffer
+ // is reused
+ var topLeftTileBounds = this.grid[0][0].bounds;
+ this.backBufferLonLat = {
+ lon: topLeftTileBounds.left,
+ lat: topLeftTileBounds.top
+ };
+ this.backBufferResolution = this.gridResolution;
+ }
+
+ var style = backBuffer.style;
+
+ // scale the back buffer
+ var ratio = this.backBufferResolution / resolution;
+ style.width = 100 * ratio + '%';
+ style.height = 100 * ratio + '%';
+
+ // and position it (based on the grid's top-left corner)
+ var position = this.getViewPortPxFromLonLat(
+ this.backBufferLonLat, resolution);
+ var leftOffset = parseInt(this.map.layerContainerDiv.style.left, 10);
+ var topOffset = parseInt(this.map.layerContainerDiv.style.top, 10);
+ backBuffer.style.left = (position.x - leftOffset) + '%';
+ backBuffer.style.top = (position.y - topOffset) + '%';
+ },
+
+ /**
+ * Method: createBackBuffer
+ * Create a back buffer.
+ *
+ * Returns:
+ * {DOMElement} The DOM element for the back buffer, undefined if the
+ * grid isn't initialized yet.
+ */
+ createBackBuffer: function() {
+ var backBuffer;
+ if(this.grid.length > 0) {
+ backBuffer = document.createElement('div');
+ backBuffer.id = this.div.id + '_bb';
+ backBuffer.className = 'olBackBuffer';
+ backBuffer.style.position = 'absolute';
+ backBuffer.style.width = '100%';
+ backBuffer.style.height = '100%';
+ for(var i=0, lenI=this.grid.length; i.
*/
url: null,
@@ -416,39 +419,59 @@ OpenLayers.Layer.WMTS = OpenLayers.Class(OpenLayers.Layer.Grid, {
var center = bounds.getCenterLonLat();
var info = this.getTileInfo(center);
var matrixId = this.matrix.identifier;
+ var dimensions = this.dimensions, params;
if (this.requestEncoding.toUpperCase() === "REST") {
-
- // include 'version', 'layer' and 'style' in tile resource url
- var path = this.version + "/" + this.layer + "/" + this.style + "/";
-
- // append optional dimension path elements
- if (this.dimensions) {
- for (var i=0; i=0; --i) {
+ dimension = dimensions[i];
+ context[dimension] = params[dimension.toUpperCase()];
}
}
- }
-
- // append other required path elements
- path = path + this.matrixSet + "/" + this.matrix.identifier +
- "/" + info.row + "/" + info.col + "." + this.formatSuffix;
-
- if (OpenLayers.Util.isArray(this.url)) {
- url = this.selectUrl(path, this.url);
+ url = OpenLayers.String.format(template, context);
} else {
- url = this.url;
- }
- if (!url.match(/\/$/)) {
- url = url + "/";
- }
- url = url + path;
+ // include 'version', 'layer' and 'style' in tile resource url
+ var path = this.version + "/" + this.layer + "/" + this.style + "/";
+ // append optional dimension path elements
+ if (dimensions) {
+ for (var i=0; i";
- var containerElement = (this.map) ? this.map.layerContainerDiv
+ var containerElement = (this.map) ? this.map.div
: document.body;
var realSize = OpenLayers.Util.getRenderedDimensions(
preparedHTML, null, {
diff --git a/lib/OpenLayers/Tile/BackBufferable.js b/lib/OpenLayers/Tile/BackBufferable.js
deleted file mode 100644
index 47c3e3198c..0000000000
--- a/lib/OpenLayers/Tile/BackBufferable.js
+++ /dev/null
@@ -1,218 +0,0 @@
-/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the Clear BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
- * full text of the license. */
-
-
-/**
- * @requires OpenLayers/Tile.js
- * @requires OpenLayers/Util.js
- */
-
-/**
- * Class: OpenLayers.Tile.BackBufferable
- * Base class for tiles that can have backbuffers during transitions. Do not
- * create instances of this class.
- */
-OpenLayers.Tile.BackBufferable = OpenLayers.Class(OpenLayers.Tile, {
-
- /**
- * Property: backBufferMode
- * {Integer} Bitmap: 0 for no backbuffering at all, 1 for singleTile
- * layers, 2 for transition effect set, 3 for both.
- */
- backBufferMode: null,
-
- /**
- * Property: backBufferData
- * {Object} Object including the necessary data for the back
- * buffer.
- *
- * The object includes three properties:
- * tile - {DOMElement} The DOM element for the back buffer.
- * bounds - {} The bounds of the tile to back.
- * resolution - {Number} The resolution of the tile to back.
- */
- backBufferData: null,
-
- /**
- * Method: initialize
- * Determines the backBuffer mode and registers events
- */
- initialize: function() {
- OpenLayers.Tile.prototype.initialize.apply(this, arguments);
-
- var transitionSupported = OpenLayers.Util.indexOf(
- this.layer.SUPPORTED_TRANSITIONS,
- this.layer.transitionEffect) != -1;
- this.backBufferMode = (this.layer.singleTile && 1) |
- (transitionSupported && 2);
-
- this.backBufferData = {};
- if (!this.size) {
- this.size = new OpenLayers.Size(256, 256);
- }
- },
-
- /**
- * Method: draw
- * Check that a tile should be drawn, and draw it.
- *
- * Returns:
- * {Boolean} Was a tile drawn?
- */
- draw: function() {
- var draw = OpenLayers.Tile.prototype.shouldDraw.apply(this, arguments);
- if (draw) {
- this.updateBackBuffer();
- }
- this.clear();
- if (!draw) {
- this.resetBackBuffer();
- };
- return draw;
- },
-
- /**
- * Method: getTile
- * Get the tile's markup. To be implemented by subclasses.
- *
- * Returns:
- * {DOMElement} The tile's markup
- */
-
- /**
- * Method: createBackBuffer
- * Create a copy of this tile's markup for the back buffer. To be
- * implemented by subclasses.
- *
- * 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
- * Stores the current bounds and resolution, for offset and ratio
- * calculations
- */
- setBackBufferData: function() {
- this.backBufferData = OpenLayers.Util.extend(this.backBufferData, {
- bounds: this.bounds,
- resolution: this.getTileResolution()
- });
- },
-
- /**
- * Method: updateBackBuffer
- * Update the , and return a new or reposition the
- * backBuffer. When a backbuffer is returned, the tile's markup is not
- * available any more.
- *
- * Returns:
- * {HTMLDivElement} the tile's markup in a cloned element, or undefined if
- * no backbuffer is currently available or needed
- */
- updateBackBuffer: function() {
- var layer = this.layer, map = layer.map,
- backBufferMode = this.backBufferMode,
- data = this.backBufferData,
- tile = this.getTile(),
- backBuffer = data.tile,
- 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:
- // (1) current ratio and backBufferMode dont't require a backbuffer
- notNeeded = !(ratio == 1 ? backBufferMode & 1 : backBufferMode & 2),
- // (2) the tile is not appended to the layer's div
- noParent = tile && tile.parentNode !== layer.div,
- // (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;
-
- if (notNeeded || noParent || noTile || noBackBuffer) {
- this.setBackBufferData();
- return;
- }
-
- // Create a back buffer tile and add it to the DOM
- if (!backBuffer) {
- 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;
- }
-
- // Position the back buffer now that we have one
- var lonLat = {lon: data.bounds.left, lat: data.bounds.top},
- 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) + "%";
- style.top = (position.y - topOffset) + "%";
- style.width = (this.size.w * ratio) + "%";
- style.height = (this.size.h * ratio) + "%";
-
- return backBuffer;
- },
-
- /**
- * Method: resetBackBuffer
- * Handler for the layer's loadend event.
- */
- resetBackBuffer: function() {
- this.layer.events.unregister("loadend", this, this.resetBackBuffer);
- this.removeBackBuffer();
- this.setBackBufferData();
- },
-
- /**
- * Method: removeBackBuffer
- * Removes the backBuffer for this tile.
- */
- removeBackBuffer: function() {
- var backBufferData = this.backBufferData;
- var backBuffer = backBufferData.tile;
- delete backBufferData.tile;
- var parent = backBuffer && backBuffer.parentNode;
- if (backBuffer) {
- parent.removeChild(backBuffer);
- }
- },
-
- /**
- * APIMethod: destroy
- * nullify references to prevent circular references and memory leaks
- */
- destroy: function() {
- this.removeBackBuffer();
- this.layer.events.unregister("loadend", this, this.resetBackBuffer);
- this.backBufferData = null;
- OpenLayers.Tile.prototype.destroy.apply(this, arguments);
- }
-
-});
diff --git a/lib/OpenLayers/Tile/Image.js b/lib/OpenLayers/Tile/Image.js
index 087693d79f..ee35ed76bb 100644
--- a/lib/OpenLayers/Tile/Image.js
+++ b/lib/OpenLayers/Tile/Image.js
@@ -5,7 +5,7 @@
/**
- * @requires OpenLayers/Tile/BackBufferable.js
+ * @requires OpenLayers/Tile.js
*/
/**
@@ -15,9 +15,9 @@
* constructor.
*
* Inherits from:
- * -
+ * -
*/
-OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
+OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile, {
/**
* Property: url
@@ -96,7 +96,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
* options - {Object}
*/
initialize: function(layer, position, bounds, url, size, options) {
- OpenLayers.Tile.BackBufferable.prototype.initialize.apply(this, arguments);
+ OpenLayers.Tile.prototype.initialize.apply(this, arguments);
this.url = url; //deprecated remove me
@@ -123,7 +123,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
}
// don't handle async requests any more
this.asyncRequestId = null;
- OpenLayers.Tile.BackBufferable.prototype.destroy.apply(this, arguments);
+ OpenLayers.Tile.prototype.destroy.apply(this, arguments);
},
/**
@@ -134,7 +134,7 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
* {Boolean} Was a tile drawn?
*/
draw: function() {
- var drawn = OpenLayers.Tile.BackBufferable.prototype.draw.apply(this, arguments);
+ var drawn = OpenLayers.Tile.prototype.draw.apply(this, arguments);
if (drawn) {
if (this.layer != this.layer.map.baseLayer && this.layer.reproject) {
this.bounds = this.getBoundsFromBaseLayer(this.position);
@@ -320,19 +320,23 @@ OpenLayers.Tile.Image = OpenLayers.Class(OpenLayers.Tile.BackBufferable, {
},
/**
- * Method: insertBackBuffer
- * Create a copy of this tile's markup and insert it to the layer
- * div.
+ * Method: createBackBuffer
+ * Create a backbuffer for this tile. A backbuffer isn't exactly a clone
+ * of the tile's markup, because we want to avoid the reloading of the
+ * image. So we clone the frame, and steal the image from the tile.
*
* Returns:
- * {DOMElement} The back buffer.
+ * {DOMElement} The markup, or undefined if the tile has no image
+ * or if it's currently loading.
*/
- insertBackBuffer: function() {
- var frame = this.frame.cloneNode(false);
- this.layer.div.insertBefore(frame, this.frame);
- frame.appendChild(this.imgDiv);
+ createBackBuffer: function() {
+ if(!this.imgDiv || this.isLoading) {
+ return;
+ }
+ var backBuffer = this.frame.cloneNode(false);
+ backBuffer.appendChild(this.imgDiv);
this.imgDiv = null;
- return frame;
+ return backBuffer;
},
/**
diff --git a/lib/OpenLayers/Tile/Image/IFrame.js b/lib/OpenLayers/Tile/Image/IFrame.js
index 9616036815..d67db5f70e 100644
--- a/lib/OpenLayers/Tile/Image/IFrame.js
+++ b/lib/OpenLayers/Tile/Image/IFrame.js
@@ -15,9 +15,6 @@
*
* This mixin will be applied to instances
* configured with set.
- *
- * Inherits from:
- * -
*/
OpenLayers.Tile.Image.IFrame = {
@@ -29,40 +26,52 @@ OpenLayers.Tile.Image.IFrame = {
useIFrame: null,
/**
- * Method: updateBackBuffer
- * Update the , and return a new or reposition the
- * backBuffer. When a backbuffer is returned, the tile's markup is not
- * available any more.
- *
- * Returns:
- * {HTMLDivElement} the tile's markup in a cloned element, or undefined if
- * no backbuffer is currently available or needed
+ * Method: draw
+ * Set useIFrame in the instance, and operate the image/iframe switch.
+ * Then call Tile.Image.draw.
+ *
+ * Returns:
+ * {Boolean}
*/
- updateBackBuffer: function() {
- this.url = this.layer.getURL(this.bounds);
- var usedIFrame = this.useIFrame;
- this.useIFrame = this.maxGetUrlLength !== null && !this.layer.async &&
- this.url.length > this.maxGetUrlLength;
- var fromIFrame = usedIFrame && !this.useIFrame;
- var toIFrame = !usedIFrame && this.useIFrame;
- if (fromIFrame || toIFrame) {
- // switch between get (image) and post (iframe)
- this.clear();
- if (this.imgDiv && this.imgDiv.parentNode === this.frame) {
- this.frame.removeChild(this.imgDiv);
- }
- this.imgDiv = null;
- if (fromIFrame) {
- // remove eventPane
- this.frame.removeChild(this.frame.firstChild);
- this.resetBackBuffer();
+ draw: function() {
+ var draw = OpenLayers.Tile.Image.prototype.shouldDraw.call(this);
+ if(draw) {
+
+ // this.url isn't set to the currect value yet, so we call getURL
+ // on the layer and store the result in a local variable
+ var url = this.layer.getURL(this.bounds);
+
+ var usedIFrame = this.useIFrame;
+ this.useIFrame = this.maxGetUrlLength !== null &&
+ !this.layer.async &&
+ url.length > this.maxGetUrlLength;
+
+ var fromIFrame = usedIFrame && !this.useIFrame;
+ var toIFrame = !usedIFrame && this.useIFrame;
+
+ if(fromIFrame || toIFrame) {
+
+ // Switching between GET (image) and POST (iframe).
+
+ // We remove the imgDiv (really either an image or an iframe)
+ // from the frame and set it to null to make sure initImage
+ // will call createImage.
+
+ if(this.imgDiv && this.imgDiv.parentNode === this.frame) {
+ this.frame.removeChild(this.imgDiv);
+ }
+ this.imgDiv = null;
+
+ // And if we had an iframe we also remove the event pane.
+
+ if(fromIFrame) {
+ this.frame.removeChild(this.frame.firstChild);
+ }
}
}
- if (!this.useIFrame) {
- OpenLayers.Tile.Image.prototype.updateBackBuffer.apply(this, arguments);
- }
+ return OpenLayers.Tile.Image.prototype.draw.apply(this, arguments);
},
-
+
/**
* Method: createImage
* Creates the content for the frame on the tile.
@@ -183,6 +192,22 @@ OpenLayers.Tile.Image.IFrame = {
} else {
OpenLayers.Tile.Image.prototype.setImgSrc.apply(this, arguments);
}
- }
+ },
+ /**
+ * Method: createBackBuffer
+ * Override createBackBuffer to do nothing when we use an iframe. Moving an
+ * iframe from one element to another makes it necessary to reload the iframe
+ * because its content is lost. So we just give up.
+ *
+ * Returns:
+ * {DOMElement}
+ */
+ createBackBuffer: function() {
+ var backBuffer;
+ if(!this.useIFrame) {
+ backBuffer = OpenLayers.Tile.Image.prototype.createBackBuffer.call(this);
+ }
+ return backBuffer;
+ }
};
diff --git a/tests/Format/OWSCommon/v1_1_0.html b/tests/Format/OWSCommon/v1_1_0.html
index e899ea8486..1cdf7ee281 100644
--- a/tests/Format/OWSCommon/v1_1_0.html
+++ b/tests/Format/OWSCommon/v1_1_0.html
@@ -6,7 +6,7 @@
function test_read_exception(t) {
t.plan(6);
var text = '' +
-'' +
' ' +
@@ -18,7 +18,7 @@
var format = new OpenLayers.Format.OWSCommon();
var result = format.read(text);
var report = result.exceptionReport;
- t.eq(report.version, "1.0.0", "Version parsed correctly");
+ t.eq(report.version, "1.1.0", "Version parsed correctly");
t.eq(report.language, "en", "Language parsed correctly");
var exception = report.exceptions[0];
t.eq(exception.code, "InvalidParameterValue", "exceptionCode properly parsed");
diff --git a/tests/Format/SOSCapabilities/v1_0_0.html b/tests/Format/SOSCapabilities/v1_0_0.html
index ed7f25eb65..6713685903 100644
--- a/tests/Format/SOSCapabilities/v1_0_0.html
+++ b/tests/Format/SOSCapabilities/v1_0_0.html
@@ -40,7 +40,7 @@
t.eq(obj.serviceProvider.serviceContact.contactInfo.phone.voice, "+49-251-83-30088", "Voice phone correctly parsed");
// operationsMetadata (from OWSCommon)
- t.eq(obj.operationsMetadata.DescribeSensor.dcp.http.post, "http://v-swe.uni-muenster.de:8080/WeatherSOS/sos", "POST url for DescribeSensor correctly parsed");
+ t.eq(obj.operationsMetadata.DescribeSensor.dcp.http.post[0].url, "http://v-swe.uni-muenster.de:8080/WeatherSOS/sos", "POST url for DescribeSensor correctly parsed");
var counter = 0;
for (var key in obj.operationsMetadata.DescribeSensor.parameters.procedure.allowedValues) {
if (counter == 0) {
diff --git a/tests/Format/WMTSCapabilities/v1_0_0.html b/tests/Format/WMTSCapabilities/v1_0_0.html
index 9ed58b3580..f8b5a37215 100644
--- a/tests/Format/WMTSCapabilities/v1_0_0.html
+++ b/tests/Format/WMTSCapabilities/v1_0_0.html
@@ -4,7 +4,7 @@
diff --git a/tests/Layer/WMTS.html b/tests/Layer/WMTS.html
index 5b87d32e49..079e87876d 100644
--- a/tests/Layer/WMTS.html
+++ b/tests/Layer/WMTS.html
@@ -220,6 +220,39 @@
t.eq(tileurl1, "http://example.com/wmts/1.0.0/world/blue_marble/arcgis_online/1/0/0.jpg", "layer1 getURL returns correct url");
map.destroy();
}
+
+ function test_getURL_resourceUrl(t) {
+ t.plan(2);
+
+ var xml = document.getElementById("capabilities").firstChild.nodeValue;
+ var doc = new OpenLayers.Format.XML().read(xml);
+ var obj = new OpenLayers.Format.WMTSCapabilities().read(doc);
+
+ var template = "http://www.example.com/{style}/{Time}/{style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}.png";
+ var layer = new OpenLayers.Layer.WMTS({
+ requestEncoding: "REST",
+ url: template,
+ layer: "GeoWebCache_USA_WMTS",
+ style: "foo",
+ matrixSet: "arcgis-online",
+ params: {Time: "2011"},
+ dimensions: ["Time"]
+ });
+
+ var map = new OpenLayers.Map("map", {
+ layers: [layer],
+ projection: "EPSG:4326",
+ maxResolution: 0.3515625,
+ maxExtent: new OpenLayers.Bounds(-180, -90, 180, 90)
+ });
+ map.setCenter(new OpenLayers.LonLat(-97.0, 38.0), 1);
+ t.eq(layer.getURL(new OpenLayers.Bounds(-135.0, 0.0, -90.0, 45.0)),
+ "http://www.example.com/foo/2011/foo/arcgis-online/1/1/1.png", "getURL returns correct url");
+ map.zoomIn();
+ t.eq(layer.getURL(new OpenLayers.Bounds(-180.0, 0.0, -90.0, 90.0)),
+ "http://www.example.com/foo/2011/foo/arcgis-online/2/2/2.png", "getURL returns correct url");
+ map.destroy();
+ }
function test_destroy (t) {
t.plan(3);
diff --git a/tests/Tile/BackBufferable.html b/tests/Tile/BackBufferable.html
deleted file mode 100644
index 97275891e7..0000000000
--- a/tests/Tile/BackBufferable.html
+++ /dev/null
@@ -1,260 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/tests/Tile/Image.html b/tests/Tile/Image.html
index 0bfa4ae2ec..22aa3ab6fa 100644
--- a/tests/Tile/Image.html
+++ b/tests/Tile/Image.html
@@ -317,8 +317,8 @@
map.destroy();
}
- function test_insertBackBuffer(t) {
- t.plan(4);
+ function test_createBackBuffer(t) {
+ t.plan(3);
var map = new OpenLayers.Map('map');
var layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
@@ -326,11 +326,18 @@
map.addLayer(layer);
map.setCenter(new OpenLayers.LonLat(0,0), 5);
var tile = layer.grid[0][0];
+
+ // we're going to create a back buffer while the image
+ // is actually loading, so we call stopObservingElement
+ // to avoid any unexpected behavior
+ tile.isLoading = false;
+ OpenLayers.Event.stopObservingElement(tile.imgDiv);
+
var img = tile.imgDiv;
- 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");
+ var bb = tile.createBackBuffer();
+ t.eq(bb.style.left, tile.frame.style.left,
+ "backbuffer has same left style as frame");
+ t.ok(bb.firstChild === img, "image appended to bb");
t.ok(tile.imgDiv == null, "image reference removed from tile");
map.destroy();
}
diff --git a/tests/Tile/Image/IFrame.html b/tests/Tile/Image/IFrame.html
index ce57a31c6a..ab119ade38 100644
--- a/tests/Tile/Image/IFrame.html
+++ b/tests/Tile/Image/IFrame.html
@@ -15,7 +15,7 @@
var wmsUrl = "http://labs.metacarta.com/wms/vmap0?";
function test_Tile_Image_IFrame_create (t) {
- t.plan( 5 );
+ t.plan( 3 );
map = new OpenLayers.Map('map');
var bar = new Array(205).join("1234567890");
layer = new OpenLayers.Layer.WMS(name, wmsUrl,
@@ -25,7 +25,6 @@
map.addLayer(layer);
var tile = layer.addTile(bounds, position);
- t.eq(tile.backBufferMode, 2, "backBufferMode is 2 after tile creation");
tile.draw();
t.eq(tile.imgDiv.nodeName.toLowerCase(), "iframe", "IFrame used for long URL");
@@ -33,7 +32,6 @@
layer.mergeNewParams({foo: null});
tile.draw();
t.eq(tile.imgDiv.nodeName.toLowerCase(), "img", "IMG used for short URL");
- t.eq(tile.backBufferMode, 2, "backBufferMode reset to 2");
tile.maxGetUrlLength = 0;
tile.draw();
diff --git a/tests/list-tests.html b/tests/list-tests.html
index b5d052093f..4983e0b4f8 100644
--- a/tests/list-tests.html
+++ b/tests/list-tests.html
@@ -224,7 +224,6 @@
Symbolizer/Text.html
Tile.html
Tile/Image.html
-
Tile/BackBufferable.html
Tile/Image/IFrame.html
Tile/WFS.html
Tween.html
diff --git a/tools/closure.py b/tools/closure.py
index ab60be7eb0..03edbc351d 100644
--- a/tools/closure.py
+++ b/tools/closure.py
@@ -7,16 +7,14 @@ if not os.path.exists(path):
raise Exception("No closure-compiler.jar at %s; read README.txt!" % path)
def minimize(code):
- ntf = tempfile.NamedTemporaryFile(delete=False)
+ ntf = tempfile.NamedTemporaryFile()
ntf.write(code)
ntf.flush()
- ntf2 = tempfile.NamedTemporaryFile(delete=False)
- ntf.close()
- ntf2.close()
+ ntf2 = tempfile.NamedTemporaryFile()
os.system("java -jar %s --js %s --js_output_file %s" % (path, ntf.name, ntf2.name))
data = open(ntf2.name).read()
- os.unlink(ntf.name)
- os.unlink(ntf2.name)
+ ntf.close()
+ ntf2.close()
return data
diff --git a/tools/update_dev_dir.sh b/tools/update_dev_dir.sh
index 7637c4e64c..743bfc8f15 100755
--- a/tools/update_dev_dir.sh
+++ b/tools/update_dev_dir.sh
@@ -4,14 +4,17 @@
# Get current 'Last Changed Rev'
-REV=`svn info http://svn.openlayers.org/ | grep 'Revision' | awk '{print $2}'`
+GITREV=`svn info https://github.com/openlayers/openlayers/ | grep 'Revision' | awk '{print $2}'`
+SVNREV=`svn info http://svn.openlayers.org/ | grep 'Revision' | awk '{print $2}'`
# Get the last svn rev
+touch /tmp/ol_git_rev
touch /tmp/ol_svn_rev
-OLD_REV="o`cat /tmp/ol_svn_rev`"
+OLD_GITREV="o`cat /tmp/ol_git_rev`"
+OLD_SVNREV="o`cat /tmp/ol_svn_rev`"
# If they're not equal, do some work.
-if [ ! o$REV = $OLD_REV ]; then
+if [ ! o$GITREV = $OLD_GITREV ]; then
svn revert -R /osgeo/openlayers/docs/dev
svn up /osgeo/openlayers/docs/dev
@@ -37,10 +40,14 @@ if [ ! o$REV = $OLD_REV ]; then
sed -i -e 's!../lib/OpenLayers.js!../OpenLayers.js!' examples/*.html
naturaldocs -i /osgeo/openlayers/docs/dev/lib -o HTML /osgeo/openlayers/dev/apidocs -p /osgeo/openlayers/docs/dev/apidoc_config -s Default OL >/dev/null
naturaldocs -i /osgeo/openlayers/docs/dev/lib -o HTML /osgeo/openlayers/dev/docs -p /osgeo/openlayers/docs/dev/doc_config -s Default OL >/dev/null
-
- svn up /osgeo/openlayers/dev/sandbox/
# Record the revision
- echo -n $REV > /tmp/ol_svn_rev
+ echo -n $GITREV > /tmp/ol_git_rev
+fi
+if [ ! o$SVNREV = $OLD_SVNREV ]; then
+ svn up /osgeo/openlayers/dev/sandbox/
+ svn up /osgeo/openlayers/dev/addins/
+ # Record the revision
+ echo -n $SVNREV > /tmp/ol_svn_rev
fi
svn up /osgeo/openlayers/documentation-checkout