diff --git a/src/ol.js b/src/ol.js
index c4ae4425f3..4489f2b092 100644
--- a/src/ol.js
+++ b/src/ol.js
@@ -12,6 +12,7 @@ goog.require("ol.layer.xyz");
goog.require("ol.layer.osm");
goog.require("ol.Tile");
goog.require("ol.TileSet");
+goog.require("ol.TileCache");
goog.require("ol.geom.geometry");
goog.require("ol.geom.point");
goog.require("ol.geom.multipoint");
diff --git a/src/ol/Tile.js b/src/ol/Tile.js
index 788322f163..e6eb5bb905 100644
--- a/src/ol/Tile.js
+++ b/src/ol/Tile.js
@@ -88,6 +88,13 @@ ol.Tile.prototype.handleImageError = function(evt) {
this.events_.triggerEvent('error');
};
+/**
+ *
+ */
+ol.Tile.prototype.destroy = function() {
+ this.events_.triggerEvent('destroy');
+};
+
/**
* Create an image node. This is done by cloning
* the same image element.
diff --git a/src/ol/TileCache.js b/src/ol/TileCache.js
new file mode 100644
index 0000000000..08311ae19f
--- /dev/null
+++ b/src/ol/TileCache.js
@@ -0,0 +1,29 @@
+goog.provide('ol.TileCache');
+
+goog.require('goog.structs.LinkedMap');
+
+/**
+ * A cache of ol.Tile objects.
+ * @constructor
+ * @extends {goog.structs.LinkedMap}
+ * @param {number=} opt_size
+ */
+ol.TileCache = function(opt_size) {
+ /**
+ * @constant
+ * @type {number}
+ */
+ this.size_ = opt_size || 100;
+
+ goog.base(this, 1, true /* cache mode */);
+};
+
+goog.inherits(ol.TileCache, goog.structs.LinkedMap);
+
+/**
+ * @inheritDoc
+ */
+ol.TileCache.prototype.removeNode = function(node) {
+ goog.base(this, 'removeNode', node);
+ node.value.destroy();
+};
diff --git a/src/ol/layer/TileLayer.js b/src/ol/layer/TileLayer.js
index c87a318d0c..075d624d07 100644
--- a/src/ol/layer/TileLayer.js
+++ b/src/ol/layer/TileLayer.js
@@ -1,6 +1,7 @@
goog.provide('ol.layer.TileLayer');
goog.require('ol.layer.Layer');
+goog.require('ol.TileCache');
/**
* @constructor
@@ -68,6 +69,12 @@ ol.layer.TileLayer = function() {
*/
this.resolutions_ = null;
+ /**
+ * @private
+ * @type {ol.TileCache}
+ */
+ this.cache_ = new ol.TileCache();
+
};
goog.inherits(ol.layer.TileLayer, ol.layer.Layer);
@@ -214,3 +221,18 @@ ol.layer.TileLayer.prototype.setNumZoomLevels = function(numZoomLevels) {
ol.layer.TileLayer.prototype.setResolutions = function(resolutions) {
this.resolutions_ = resolutions;
};
+
+/**
+ * Get a tile from the cache, or create a tile and add to
+ * the cache.
+ * @param url {string}
+ * @param bounds {ol.Bounds}
+ */
+ol.layer.TileLayer.prototype.getTile = function(url, bounds) {
+ var tile = this.cache_.get(url);
+ if (!goog.isDef(tile)) {
+ tile = new ol.Tile(url, bounds);
+ this.cache_.set(tile.getUrl(), tile);
+ }
+ return tile;
+};
diff --git a/src/ol/layer/XYZ.js b/src/ol/layer/XYZ.js
index b9f1200347..72b604aba8 100644
--- a/src/ol/layer/XYZ.js
+++ b/src/ol/layer/XYZ.js
@@ -25,6 +25,17 @@ ol.layer.XYZ = function(url) {
goog.base(this);
this.setMaxResolution(156543.03390625);
+
+ this.setResolutions([
+ 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.29858214169740677, 0.14929107084870338,
+ 0.07464553542435169
+ ]);
};
goog.inherits(ol.layer.XYZ, ol.layer.TileLayer);
@@ -81,7 +92,7 @@ ol.layer.XYZ.prototype.getData = function(bounds, resolution) {
url = me.url_.replace('{x}', offsetX + x + '')
.replace('{y}', offsetY + y + '')
.replace('{z}', zoom + '');
- tile = new ol.Tile(url, tileBounds);
+ tile = this.getTile(url, tileBounds);
tiles[y][x] = tile;
}
}
diff --git a/test/index.html b/test/index.html
index f218500bb1..53d0f6c39d 100644
--- a/test/index.html
+++ b/test/index.html
@@ -63,6 +63,7 @@
+
diff --git a/test/spec/ol/TileCache.test.js b/test/spec/ol/TileCache.test.js
new file mode 100644
index 0000000000..5211ac22a1
--- /dev/null
+++ b/test/spec/ol/TileCache.test.js
@@ -0,0 +1,20 @@
+describe('ol.TileCache', function() {
+
+ describe('exceed the cache capacity', function() {
+
+ var tilecache, tile;
+
+ beforeEach(function() {
+ tilecache = new ol.TileCache(1);
+ tile = new ol.Tile('url1');
+ tilecache.set('url1', tile);
+ spyOn(tile, 'destroy');
+ });
+
+ it('calls tile.destroy', function() {
+ tilecache.set('url2', new ol.Tile('url2'));
+ expect(tile.destroy).toHaveBeenCalled();
+ });
+ });
+
+});