Adding CacheRead and CacheWrite controls.

These controls read from and write to the browser's offline storage. Example with a seeding tool included.
This commit is contained in:
ahocevar
2012-03-08 14:18:44 +01:00
parent 0566b0a5bb
commit f18ac9911b
8 changed files with 761 additions and 0 deletions

View File

@@ -0,0 +1,149 @@
/* Copyright (c) 2006-2012 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/Control.js
*/
/**
* Class: OpenLayers.Control.CacheRead
* A control for using image tiles cached with <OpenLayers.Control.CacheWrite>
* from the browser's local storage.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.CacheRead = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: fetchEvent
* {String} The layer event to listen to for replacing remote resource tile
* URLs with cached data URIs. Supported values are "tileerror" (try
* remote first, fall back to cached) and "tileloadstart" (try cache
* first, fall back to remote). Default is "tileloadstart".
*
* Note that "tileerror" will not work for CORS enabled images (see
* https://developer.mozilla.org/en/CORS_Enabled_Image), i.e. layers
* configured with a <OpenLayers.Tile.Image.crossOriginKeyword> in
* <OpenLayers.Layer.Grid.tileOptions>.
*/
fetchEvent: "tileloadstart",
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer.HTTPRequest>)}. Optional. If provided, only
* these layers will receive tiles from the cache.
*/
layers: null,
/**
* APIProperty: autoActivate
* {Boolean} Activate the control when it is added to a map. Default is
* true.
*/
autoActivate: true,
/**
* Constructor: OpenLayers.Control.CacheRead
*
* Parameters:
* options - {Object} Object with API properties for this control
*/
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
var i, layers = this.layers || this.map.layers;
for (i=layers.length-1; i>=0; --i) {
this.addLayer({layer: layers[i]});
}
if (!this.layers) {
this.map.events.on({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
},
/**
* Method: addLayer
* Adds a layer to the control. Once added, tiles requested for this layer
* will be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
addLayer: function(evt) {
evt.layer.events.register(this.fetchEvent, this, this.fetch);
},
/**
* Method: removeLayer
* Removes a layer from the control. Once removed, tiles requested for this
* layer will no longer be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
removeLayer: function(evt) {
evt.layer.events.unregister(this.fetchEvent, this, this.fetch);
},
/**
* Method: fetch
* Listener to the <fetchEvent> event. Replaces a tile's url with a data
* URI from the cache.
*
* Parameters:
* evt - {Object} Event object with a tile property.
*/
fetch: function(evt) {
if (this.active && window.localStorage) {
var tile = evt.tile,
dataURI = window.localStorage.getItem("olCache_" + tile.url);
if (dataURI) {
if (evt.type === "tileerror") {
tile.setImgSrc(dataURI);
} else {
tile.url = dataURI;
}
}
}
},
/**
* Method: destroy
* The destroy method is used to perform any clean up before the control
* is dereferenced. Typically this is where event listeners are removed
* to prevent memory leaks.
*/
destroy: function() {
if (this.layers || this.map) {
var i, layers = this.layers || this.map.layers;
for (i=layers.length-1; i>=0; --i) {
this.removeLayer({layer: layers[i]});
}
}
if (this.map) {
this.map.events.un({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
CLASS_NAME: "OpenLayers.Control.CacheRead"
});

View File

@@ -0,0 +1,197 @@
/* Copyright (c) 2006-2012 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/Control.js
*/
/**
* Class: OpenLayers.Control.CacheWrite
* A control for caching image tiles in the browser's local storage. The
* <OpenLayers.Control.CacheRead> control is used to fetch and use the cached
* tile images.
*
* Inherits from:
* - <OpenLayers.Control>
*/
OpenLayers.Control.CacheWrite = OpenLayers.Class(OpenLayers.Control, {
/**
* APIProperty: events
* {<OpenLayers.Events>} Events instance for listeners and triggering
* control specific events.
*
* To register events in the constructor, configure <eventListeners>.
*
* Register a listener for a particular event with the following syntax:
* (code)
* control.events.register(type, obj, listener);
* (end)
*
* Supported event types (in addition to those from <OpenLayers.Control.events>):
* cachefull - Triggered when the cache is full. Listeners receive an
* object with a tile property as first argument. The tile references
* the tile that couldn't be cached.
*/
/**
* APIProperty: eventListeners
* {Object} Object with event listeners, keyed by event name. An optional
* scope property defines the scope that listeners will be executed in.
*/
/**
* APIProperty: layers
* {Array(<OpenLayers.Layer.Grid>)}. Optional. If provided, caching
* will be enabled for these layers only, otherwise for all cacheable
* layers.
*/
layers: null,
/**
* APIProperty: imageFormat
* {String} The image format used for caching. The default is "image/png".
* Supported formats depend on the user agent. If an unsupported
* <imageFormat> is provided, "image/png" will be used. For aerial
* imagery, "image/jpeg" is recommended.
*/
imageFormat: "image/png",
/**
* Property: quotaRegEx
* {RegExp}
*/
quotaRegEx: (/quota/i),
/**
* Constructor: OpenLayers.Control.CacheWrite
*
* Parameters:
* options - {Object} Object with API properties for this control.
*/
/**
* Method: setMap
* Set the map property for the control.
*
* Parameters:
* map - {<OpenLayers.Map>}
*/
setMap: function(map) {
OpenLayers.Control.prototype.setMap.apply(this, arguments);
var i, layers = this.layers || this.map.layers;
for (i=layers.length-1; i>=0; --i) {
this.addLayer({layer: layers[i]});
}
if (!this.layers) {
this.map.events.on({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
},
/**
* Method: addLayer
* Adds a layer to the control. Once added, tiles requested for this layer
* will be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
addLayer: function(evt) {
evt.layer.events.register("tileloaded", this, this.cache);
},
/**
* Method: removeLayer
* Removes a layer from the control. Once removed, tiles requested for this
* layer will no longer be cached.
*
* Parameters:
* evt - {Object} Object with a layer property referencing an
* <OpenLayers.Layer> instance
*/
removeLayer: function(evt) {
evt.layer.events.unregister("tileloaded", this, this.cache);
},
/**
* Method: cache
* Adds a tile to the cache. When the cache is full, the "cachefull" event
* is triggered.
*
* Parameters:
* obj - {Object} Object with a tile property, tile being the
* <OpenLayers.Tile.Image> with the data to add to the cache
*/
cache: function(obj) {
if (this.active && window.localStorage) {
var tile = obj.tile;
if (tile.url.substr(0, 5) !== 'data:') {
try {
var canvasContext = tile.getCanvasContext();
if (canvasContext) {
window.localStorage.setItem(
"olCache_" + tile.url,
canvasContext.canvas.toDataURL(this.imageFormat)
);
}
} catch(e) {
// local storage full or CORS violation
var reason = e.name || e.message;
if (reason && this.quotaRegEx.test(reason)) {
this.events.triggerEvent("cachefull", {tile: tile});
} else {
// throw exception in the next cycle
window.setTimeout(function() { throw(e); }, 0);
}
}
}
}
},
/**
* Method: destroy
* The destroy method is used to perform any clean up before the control
* is dereferenced. Typically this is where event listeners are removed
* to prevent memory leaks.
*/
destroy: function() {
if (this.layers || this.map) {
var i, layers = this.layers || this.map.layers;
for (i=layers.length-1; i>=0; --i) {
this.removeLayer({layer: layers[i]});
}
}
if (this.map) {
this.map.events.un({
addlayer: this.addLayer,
removeLayer: this.removeLayer,
scope: this
});
}
OpenLayers.Control.prototype.destroy.apply(this, arguments);
},
CLASS_NAME: "OpenLayers.Control.CacheWrite"
});
/**
* APIFunction: OpenLayers.Control.CacheWrite.clearCache
* Clears all tiles cached with <OpenLayers.Control.CacheWrite> from the cache.
*/
OpenLayers.Control.CacheWrite.clearCache = function() {
if (!window.localStorage) { return; }
var i, key;
for (i=window.localStorage.length-1; i>=0; --i) {
key = window.localStorage.key(i);
if (key.substr(0, 8) === "olCache_") {
window.localStorage.removeItem(key);
}
}
};