diff --git a/examples/canvas-tiles.html b/examples/canvas-tiles.html
new file mode 100644
index 0000000000..7340cdc3fc
--- /dev/null
+++ b/examples/canvas-tiles.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+ ol3 canvas tiles demo
+
+
+ Canvas tiles example
+ The black grid tiles are generated on the client with an HTML5 canvas. Note that the tile coordinates are ol3 normalized tile coordinates (origin bottom left), not OSM tile coordinates (origin top left).
+
+
+ DOM
+ WebGL
+
+
+
+
+
+
+
+ layers, stamen, canvas
+
+
+
diff --git a/examples/canvas-tiles.js b/examples/canvas-tiles.js
new file mode 100644
index 0000000000..92c3f15046
--- /dev/null
+++ b/examples/canvas-tiles.js
@@ -0,0 +1,43 @@
+goog.require('ol.Collection');
+goog.require('ol.Coordinate');
+goog.require('ol.Map');
+goog.require('ol.Projection');
+goog.require('ol.RendererHint');
+goog.require('ol.layer.TileLayer');
+goog.require('ol.source.DebugTileSource');
+goog.require('ol.source.Stamen');
+
+
+var layers = new ol.Collection([
+ new ol.layer.TileLayer({
+ source: new ol.source.Stamen({
+ provider: ol.source.StamenProvider.WATERCOLOR
+ })
+ }),
+ new ol.layer.TileLayer({
+ source: new ol.source.DebugTileSource({
+ projection: ol.Projection.getFromCode('EPSG:3857'),
+ tileGrid: new ol.tilegrid.XYZ({
+ maxZoom: 22
+ })
+ })
+ })
+]);
+
+var webglMap = new ol.Map({
+ view: new ol.View2D({
+ center: ol.Projection.transformWithCodes(
+ new ol.Coordinate(-0.1275, 51.507222), 'EPSG:4326', 'EPSG:3857'),
+ zoom: 10
+ }),
+ layers: layers,
+ renderer: ol.RendererHint.WEBGL,
+ target: 'webglMap'
+});
+
+var domMap = new ol.Map({
+ renderer: ol.RendererHint.DOM,
+ target: 'domMap'
+});
+domMap.bindTo('layers', webglMap);
+domMap.bindTo('view', webglMap);
diff --git a/src/ol/imagetile.js b/src/ol/imagetile.js
new file mode 100644
index 0000000000..c75c11ea23
--- /dev/null
+++ b/src/ol/imagetile.js
@@ -0,0 +1,137 @@
+goog.provide('ol.ImageTile');
+
+goog.require('goog.array');
+goog.require('goog.events');
+goog.require('goog.events.EventTarget');
+goog.require('goog.events.EventType');
+goog.require('ol.Tile');
+goog.require('ol.TileCoord');
+goog.require('ol.TileState');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.Tile}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {string} src Image source URI.
+ * @param {?string} crossOrigin Cross origin.
+ */
+ol.ImageTile = function(tileCoord, src, crossOrigin) {
+
+ goog.base(this, tileCoord);
+
+ /**
+ * Image URI
+ *
+ * @private
+ * @type {string}
+ */
+ this.src_ = src;
+
+ /**
+ * @private
+ * @type {Image}
+ */
+ this.image_ = new Image();
+ if (!goog.isNull(crossOrigin)) {
+ this.image_.crossOrigin = crossOrigin;
+ }
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.imageByContext_ = {};
+
+ /**
+ * @private
+ * @type {Array.}
+ */
+ this.imageListenerKeys_ = null;
+
+};
+goog.inherits(ol.ImageTile, ol.Tile);
+
+
+/**
+ * @inheritDoc
+ */
+ol.ImageTile.prototype.getImage = function(opt_context) {
+ if (goog.isDef(opt_context)) {
+ var image;
+ var key = goog.getUid(opt_context);
+ if (key in this.imageByContext_) {
+ return this.imageByContext_[key];
+ } else if (goog.object.isEmpty(this.imageByContext_)) {
+ image = this.image_;
+ } else {
+ image = /** @type {Image} */ (this.image_.cloneNode(false));
+ }
+ this.imageByContext_[key] = image;
+ return image;
+ } else {
+ return this.image_;
+ }
+};
+
+
+/**
+ * @inheritDoc
+ */
+ol.ImageTile.prototype.getKey = function() {
+ return this.src_;
+};
+
+
+/**
+ * Tracks loading or read errors.
+ *
+ * @private
+ */
+ol.ImageTile.prototype.handleImageError_ = function() {
+ this.state = ol.TileState.ERROR;
+ this.unlistenImage_();
+};
+
+
+/**
+ * Tracks successful image load.
+ *
+ * @private
+ */
+ol.ImageTile.prototype.handleImageLoad_ = function() {
+ this.state = ol.TileState.LOADED;
+ this.unlistenImage_();
+ this.dispatchChangeEvent();
+};
+
+
+/**
+ * Load not yet loaded URI.
+ */
+ol.ImageTile.prototype.load = function() {
+ if (this.state == ol.TileState.IDLE) {
+ this.state = ol.TileState.LOADING;
+ goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
+ this.imageListenerKeys_ = [
+ goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
+ this.handleImageError_, false, this),
+ goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
+ this.handleImageLoad_, false, this)
+ ];
+ this.image_.src = this.src_;
+ }
+};
+
+
+/**
+ * Discards event handlers which listen for load completion or errors.
+ *
+ * @private
+ */
+ol.ImageTile.prototype.unlistenImage_ = function() {
+ goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
+ goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
+ this.imageListenerKeys_ = null;
+};
diff --git a/src/ol/renderer/webgl/webglmaprenderer.js b/src/ol/renderer/webgl/webglmaprenderer.js
index 72c98aa8e3..4bfea15f74 100644
--- a/src/ol/renderer/webgl/webglmaprenderer.js
+++ b/src/ol/renderer/webgl/webglmaprenderer.js
@@ -17,6 +17,7 @@ goog.require('goog.events.EventType');
goog.require('goog.functions');
goog.require('goog.style');
goog.require('goog.webgl');
+goog.require('ol.Tile');
goog.require('ol.layer.Layer');
goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.webgl.FragmentShader');
@@ -219,15 +220,15 @@ ol.renderer.webgl.Map.prototype.addLayer = function(layer) {
/**
- * @param {Image} image Image.
+ * @param {ol.Tile} tile Tile.
* @param {number} magFilter Mag filter.
* @param {number} minFilter Min filter.
*/
-ol.renderer.webgl.Map.prototype.bindImageTexture =
- function(image, magFilter, minFilter) {
+ol.renderer.webgl.Map.prototype.bindTileTexture =
+ function(tile, magFilter, minFilter) {
var gl = this.getGL();
- var imageKey = image.src;
- var textureCacheEntry = this.textureCache_[imageKey];
+ var tileKey = tile.getKey();
+ var textureCacheEntry = this.textureCache_[tileKey];
if (goog.isDef(textureCacheEntry)) {
gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture);
if (textureCacheEntry.magFilter != magFilter) {
@@ -244,7 +245,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
var texture = gl.createTexture();
gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA,
- goog.webgl.UNSIGNED_BYTE, image);
+ goog.webgl.UNSIGNED_BYTE, tile.getImage());
gl.texParameteri(
goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
gl.texParameteri(
@@ -253,7 +254,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
goog.webgl.CLAMP_TO_EDGE);
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T,
goog.webgl.CLAMP_TO_EDGE);
- this.textureCache_[imageKey] = {
+ this.textureCache_[tileKey] = {
texture: texture,
magFilter: magFilter,
minFilter: minFilter
@@ -485,11 +486,11 @@ ol.renderer.webgl.Map.prototype.initializeGL_ = function() {
/**
- * @param {Image} image Image.
- * @return {boolean} Is image texture loaded.
+ * @param {ol.Tile} tile Tile.
+ * @return {boolean} Is tile texture loaded.
*/
-ol.renderer.webgl.Map.prototype.isImageTextureLoaded = function(image) {
- return image.src in this.textureCache_;
+ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) {
+ return tile.getKey() in this.textureCache_;
};
diff --git a/src/ol/renderer/webgl/webgltilelayerrenderer.js b/src/ol/renderer/webgl/webgltilelayerrenderer.js
index b43cf8710f..2152b6a824 100644
--- a/src/ol/renderer/webgl/webgltilelayerrenderer.js
+++ b/src/ol/renderer/webgl/webgltilelayerrenderer.js
@@ -386,9 +386,9 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
var tilesToDrawByZ = {};
/**
- * @type {Array.}
+ * @type {Array.}
*/
- var imagesToLoad = [];
+ var tilesToLoad = [];
var allTilesLoaded = true;
@@ -405,12 +405,11 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
if (tileState == ol.TileState.IDLE) {
tile.load();
} else if (tileState == ol.TileState.LOADED) {
- var image = tile.getImage();
- if (mapRenderer.isImageTextureLoaded(image)) {
+ if (mapRenderer.isTileTextureLoaded(tile)) {
tilesToDrawByZ[z][tileCoord.toString()] = tile;
return;
} else {
- imagesToLoad.push(image);
+ tilesToLoad.push(tile);
}
} else if (tileState == ol.TileState.ERROR) {
return;
@@ -459,29 +458,28 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
framebufferExtentSize.height - 1;
goog.vec.Vec4.setFromValues(uTileOffset, sx, sy, tx, ty);
gl.uniform4fv(this.locations_.uTileOffset, uTileOffset);
- mapRenderer.bindImageTexture(
- tile.getImage(), goog.webgl.LINEAR, goog.webgl.LINEAR);
+ mapRenderer.bindTileTexture(tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
}, this);
}, this);
- if (!goog.array.isEmpty(imagesToLoad)) {
+ if (!goog.array.isEmpty(tilesToLoad)) {
goog.events.listenOnce(
map,
ol.MapEventType.POSTRENDER,
- goog.partial(function(mapRenderer, imagesToLoad) {
+ goog.partial(function(mapRenderer, tilesToLoad) {
if (goog.DEBUG) {
this.logger.info(
- 'uploading ' + imagesToLoad.length + ' textures');
+ 'uploading ' + tilesToLoad.length + ' textures');
}
- goog.array.forEach(imagesToLoad, function(image) {
- mapRenderer.bindImageTexture(
- image, goog.webgl.LINEAR, goog.webgl.LINEAR);
+ goog.array.forEach(tilesToLoad, function(tile) {
+ mapRenderer.bindTileTexture(
+ tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
});
if (goog.DEBUG) {
this.logger.info('uploaded textures');
}
- }, mapRenderer, imagesToLoad));
+ }, mapRenderer, tilesToLoad));
}
if (allTilesLoaded) {
diff --git a/src/ol/source/bingmapssource.js b/src/ol/source/bingmapssource.js
index 1b805faf93..7693cb81d9 100644
--- a/src/ol/source/bingmapssource.js
+++ b/src/ol/source/bingmapssource.js
@@ -6,7 +6,7 @@ goog.require('goog.events');
goog.require('goog.events.EventType');
goog.require('goog.net.Jsonp');
goog.require('ol.TileCoverageArea');
-goog.require('ol.source.TileSource');
+goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -25,7 +25,7 @@ ol.BingMapsStyle = {
/**
* @constructor
- * @extends {ol.source.TileSource}
+ * @extends {ol.source.ImageTileSource}
* @param {ol.source.BingMapsOptions} bingMapsOptions Bing Maps options.
*/
ol.source.BingMaps = function(bingMapsOptions) {
@@ -57,7 +57,7 @@ ol.source.BingMaps = function(bingMapsOptions) {
}, goog.bind(this.handleImageryMetadataResponse, this));
};
-goog.inherits(ol.source.BingMaps, ol.source.TileSource);
+goog.inherits(ol.source.BingMaps, ol.source.ImageTileSource);
/**
diff --git a/src/ol/source/debugtilesource.js b/src/ol/source/debugtilesource.js
new file mode 100644
index 0000000000..d4dd63bdec
--- /dev/null
+++ b/src/ol/source/debugtilesource.js
@@ -0,0 +1,125 @@
+goog.provide('ol.source.DebugTileSource');
+goog.provide('ol.source.DebugTileSourceOptions');
+
+goog.require('ol.Tile');
+goog.require('ol.TileCoord');
+goog.require('ol.source.TileSource');
+goog.require('ol.tilegrid.TileGrid');
+
+
+
+/**
+ * @constructor
+ * @extends {ol.Tile}
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @param {ol.tilegrid.TileGrid} tileGrid Tile grid.
+ * @private
+ */
+ol.DebugTile_ = function(tileCoord, tileGrid) {
+
+ goog.base(this, tileCoord);
+
+ this.state = ol.TileState.LOADED;
+
+ var tileSize = tileGrid.getTileSize();
+
+ /**
+ * @private
+ * @type {HTMLCanvasElement}
+ */
+ this.canvas_ = /** @type {HTMLCanvasElement} */
+ (goog.dom.createElement(goog.dom.TagName.CANVAS));
+ this.canvas_.width = tileSize.width;
+ this.canvas_.height = tileSize.height;
+
+ var context = this.canvas_.getContext('2d');
+
+ context.strokeStyle = 'black';
+ context.strokeRect(0.5, 0.5, tileSize.width + 0.5, tileSize.height + 0.5);
+
+ context.fillStyle = 'black';
+ context.textAlign = 'center';
+ context.textBaseline = 'middle';
+ context.font = '24px sans-serif';
+ context.fillText(
+ tileCoord.toString(), tileSize.width / 2, tileSize.height / 2);
+
+ /**
+ * @private
+ * @type {Object.}
+ */
+ this.canvasByContext_ = {};
+
+};
+goog.inherits(ol.DebugTile_, ol.Tile);
+
+
+/**
+ * @inheritDoc
+ */
+ol.DebugTile_.prototype.getImage = function(opt_context) {
+ if (goog.isDef(opt_context)) {
+ var canvas;
+ var key = goog.getUid(opt_context);
+ if (key in this.canvasByContext_) {
+ return this.canvasByContext_[key];
+ } else if (goog.object.isEmpty(this.canvasByContext_)) {
+ canvas = this.canvas_;
+ } else {
+ canvas = /** @type {HTMLCanvasElement} */ (this.canvas_.cloneNode(false));
+ }
+ this.canvasByContext_[key] = canvas;
+ return canvas;
+ } else {
+ return this.canvas_;
+ }
+};
+
+
+/**
+ * @typedef {{extent: (ol.Extent|undefined),
+ * projection: (ol.Projection|undefined),
+ * tileGrid: (ol.tilegrid.TileGrid|undefined)}}
+ */
+ol.source.DebugTileSourceOptions;
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.TileSource}
+ * @param {ol.source.DebugTileSourceOptions} options Options.
+ */
+ol.source.DebugTileSource = function(options) {
+
+ goog.base(this, {
+ extent: options.extent,
+ projection: options.projection,
+ tileGrid: options.tileGrid
+ });
+
+ /**
+ * @private
+ * @type {Object.}
+ * FIXME will need to expire elements from this cache
+ * FIXME see elemoine's work with goog.structs.LinkedMap
+ */
+ this.tileCache_ = {};
+
+};
+goog.inherits(ol.source.DebugTileSource, ol.source.TileSource);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.DebugTileSource.prototype.getTile = function(tileCoord) {
+ var key = tileCoord.toString();
+ if (goog.object.containsKey(this.tileCache_, key)) {
+ return this.tileCache_[key];
+ } else {
+ var tile = new ol.DebugTile_(tileCoord, this.tileGrid);
+ this.tileCache_[key] = tile;
+ return tile;
+ }
+};
diff --git a/src/ol/source/imagetilesource.js b/src/ol/source/imagetilesource.js
new file mode 100644
index 0000000000..5baea4539f
--- /dev/null
+++ b/src/ol/source/imagetilesource.js
@@ -0,0 +1,95 @@
+goog.provide('ol.source.ImageTileSource');
+goog.provide('ol.source.ImageTileSourceOptions');
+
+goog.require('ol.Attribution');
+goog.require('ol.Extent');
+goog.require('ol.ImageTile');
+goog.require('ol.Projection');
+goog.require('ol.TileCoord');
+goog.require('ol.TileUrlFunction');
+goog.require('ol.TileUrlFunctionType');
+goog.require('ol.source.TileSource');
+goog.require('ol.tilegrid.TileGrid');
+
+
+/**
+ * @typedef {{attributions: (Array.|undefined),
+ * crossOrigin: (null|string|undefined),
+ * extent: (ol.Extent|undefined),
+ * projection: (ol.Projection|undefined),
+ * tileGrid: (ol.tilegrid.TileGrid|undefined),
+ * tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
+ */
+ol.source.ImageTileSourceOptions;
+
+
+
+/**
+ * @constructor
+ * @extends {ol.source.TileSource}
+ * @param {ol.source.ImageTileSourceOptions} options Options.
+ */
+ol.source.ImageTileSource = function(options) {
+
+ goog.base(this, {
+ attributions: options.attributions,
+ extent: options.extent,
+ projection: options.projection,
+ tileGrid: options.tileGrid
+ });
+
+ /**
+ * @protected
+ * @type {ol.TileUrlFunctionType}
+ */
+ this.tileUrlFunction = goog.isDef(options.tileUrlFunction) ?
+ options.tileUrlFunction :
+ ol.TileUrlFunction.nullTileUrlFunction;
+
+ /**
+ * @private
+ * @type {?string}
+ */
+ this.crossOrigin_ =
+ goog.isDef(options.crossOrigin) ? options.crossOrigin : 'anonymous';
+
+ /**
+ * @private
+ * @type {Object.}
+ * FIXME will need to expire elements from this cache
+ * FIXME see elemoine's work with goog.structs.LinkedMap
+ */
+ this.tileCache_ = {};
+
+};
+goog.inherits(ol.source.ImageTileSource, ol.source.TileSource);
+
+
+/**
+ * @inheritDoc
+ */
+ol.source.ImageTileSource.prototype.getTile = function(tileCoord) {
+ var key = tileCoord.toString();
+ if (goog.object.containsKey(this.tileCache_, key)) {
+ return this.tileCache_[key];
+ } else {
+ var tileUrl = this.getTileCoordUrl(tileCoord);
+ var tile;
+ if (goog.isDef(tileUrl)) {
+ tile = new ol.ImageTile(tileCoord, tileUrl, this.crossOrigin_);
+ } else {
+ tile = null;
+ }
+ this.tileCache_[key] = tile;
+ return tile;
+ }
+};
+
+
+/**
+ * @param {ol.TileCoord} tileCoord Tile coordinate.
+ * @return {string|undefined} Tile URL.
+ */
+ol.source.ImageTileSource.prototype.getTileCoordUrl = function(tileCoord) {
+ return this.tileUrlFunction(tileCoord);
+};
diff --git a/src/ol/source/tiledwmssource.js b/src/ol/source/tiledwmssource.js
index b3bddea36a..e6d61e520d 100644
--- a/src/ol/source/tiledwmssource.js
+++ b/src/ol/source/tiledwmssource.js
@@ -10,14 +10,14 @@ goog.require('ol.Attribution');
goog.require('ol.Projection');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
-goog.require('ol.source.TileSource');
+goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.TileGrid');
/**
* @constructor
- * @extends {ol.source.TileSource}
+ * @extends {ol.source.ImageTileSource}
* @param {ol.source.TiledWMSOptions} tiledWMSOptions options.
*/
ol.source.TiledWMS = function(tiledWMSOptions) {
@@ -116,4 +116,4 @@ ol.source.TiledWMS = function(tiledWMSOptions) {
});
};
-goog.inherits(ol.source.TiledWMS, ol.source.TileSource);
+goog.inherits(ol.source.TiledWMS, ol.source.ImageTileSource);
diff --git a/src/ol/source/tilejsonsource.js b/src/ol/source/tilejsonsource.js
index 35c9124468..81eacaf7a4 100644
--- a/src/ol/source/tilejsonsource.js
+++ b/src/ol/source/tilejsonsource.js
@@ -16,7 +16,7 @@ goog.require('goog.string');
goog.require('ol.Projection');
goog.require('ol.TileCoverageArea');
goog.require('ol.TileUrlFunction');
-goog.require('ol.source.TileSource');
+goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -45,7 +45,7 @@ goog.exportSymbol('grid', grid);
/**
* @constructor
- * @extends {ol.source.TileSource}
+ * @extends {ol.source.ImageTileSource}
* @param {ol.source.TileJSONOptions} tileJsonOptions TileJSON optios.
*/
ol.source.TileJSON = function(tileJsonOptions) {
@@ -69,7 +69,7 @@ ol.source.TileJSON = function(tileJsonOptions) {
this.deferred_.addCallback(this.handleTileJSONResponse, this);
};
-goog.inherits(ol.source.TileJSON, ol.source.TileSource);
+goog.inherits(ol.source.TileJSON, ol.source.ImageTileSource);
/**
diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js
index 9b915efe16..6b82b8f3b8 100644
--- a/src/ol/source/tilesource.js
+++ b/src/ol/source/tilesource.js
@@ -14,11 +14,9 @@ goog.require('ol.tilegrid.TileGrid');
/**
* @typedef {{attributions: (Array.|undefined),
- * crossOrigin: (null|string|undefined),
* extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined),
- * tileGrid: (ol.tilegrid.TileGrid|undefined),
- * tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
+ * tileGrid: (ol.tilegrid.TileGrid|undefined)}}
*/
ol.source.TileSourceOptions;
@@ -44,29 +42,6 @@ ol.source.TileSource = function(tileSourceOptions) {
this.tileGrid = goog.isDef(tileSourceOptions.tileGrid) ?
tileSourceOptions.tileGrid : null;
- /**
- * @protected
- * @type {ol.TileUrlFunctionType}
- */
- this.tileUrlFunction = goog.isDef(tileSourceOptions.tileUrlFunction) ?
- tileSourceOptions.tileUrlFunction :
- ol.TileUrlFunction.nullTileUrlFunction;
-
- /**
- * @private
- * @type {?string}
- */
- this.crossOrigin_ = goog.isDef(tileSourceOptions.crossOrigin) ?
- tileSourceOptions.crossOrigin : 'anonymous';
-
- /**
- * @private
- * @type {Object.}
- * FIXME will need to expire elements from this cache
- * FIXME see elemoine's work with goog.structs.LinkedMap
- */
- this.tileCache_ = {};
-
};
goog.inherits(ol.source.TileSource, ol.source.Source);
@@ -83,31 +58,7 @@ ol.source.TileSource.prototype.getResolutions = function() {
* @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {ol.Tile} Tile.
*/
-ol.source.TileSource.prototype.getTile = function(tileCoord) {
- var key = tileCoord.toString();
- if (goog.object.containsKey(this.tileCache_, key)) {
- return this.tileCache_[key];
- } else {
- var tileUrl = this.getTileCoordUrl(tileCoord);
- var tile;
- if (goog.isDef(tileUrl)) {
- tile = new ol.Tile(tileCoord, tileUrl, this.crossOrigin_);
- } else {
- tile = null;
- }
- this.tileCache_[key] = tile;
- return tile;
- }
-};
-
-
-/**
- * @param {ol.TileCoord} tileCoord Tile coordinate.
- * @return {string|undefined} Tile URL.
- */
-ol.source.TileSource.prototype.getTileCoordUrl = function(tileCoord) {
- return this.tileUrlFunction(tileCoord);
-};
+ol.source.TileSource.prototype.getTile = goog.abstractMethod;
/**
diff --git a/src/ol/source/xyzsource.js b/src/ol/source/xyzsource.js
index f1ed03cc3b..0758d99f89 100644
--- a/src/ol/source/xyzsource.js
+++ b/src/ol/source/xyzsource.js
@@ -11,7 +11,7 @@ goog.require('ol.Size');
goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction');
goog.require('ol.TileUrlFunctionType');
-goog.require('ol.source.TileSource');
+goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ');
@@ -31,7 +31,7 @@ ol.source.XYZOptions;
/**
* @constructor
- * @extends {ol.source.TileSource}
+ * @extends {ol.source.ImageTileSource}
* @param {ol.source.XYZOptions} xyzOptions XYZ options.
*/
ol.source.XYZ = function(xyzOptions) {
@@ -110,4 +110,4 @@ ol.source.XYZ = function(xyzOptions) {
});
};
-goog.inherits(ol.source.XYZ, ol.source.TileSource);
+goog.inherits(ol.source.XYZ, ol.source.ImageTileSource);
diff --git a/src/ol/tile.js b/src/ol/tile.js
index c390db3bd5..364374c9e6 100644
--- a/src/ol/tile.js
+++ b/src/ol/tile.js
@@ -24,10 +24,8 @@ ol.TileState = {
* @constructor
* @extends {goog.events.EventTarget}
* @param {ol.TileCoord} tileCoord Tile coordinate.
- * @param {string} src Image source URI.
- * @param {?string} crossOrigin Cross origin.
*/
-ol.Tile = function(tileCoord, src, crossOrigin) {
+ol.Tile = function(tileCoord) {
goog.base(this);
@@ -37,39 +35,10 @@ ol.Tile = function(tileCoord, src, crossOrigin) {
this.tileCoord = tileCoord;
/**
- * Image URI
- *
- * @private
- * @type {string}
- */
- this.src_ = src;
-
- /**
- * @private
+ * @protected
* @type {ol.TileState}
*/
- this.state_ = ol.TileState.IDLE;
-
- /**
- * @private
- * @type {Image}
- */
- this.image_ = new Image();
- if (!goog.isNull(crossOrigin)) {
- this.image_.crossOrigin = crossOrigin;
- }
-
- /**
- * @private
- * @type {Object.}
- */
- this.imageByContext_ = {};
-
- /**
- * @private
- * @type {Array.}
- */
- this.imageListenerKeys_ = null;
+ this.state = ol.TileState.IDLE;
};
goog.inherits(ol.Tile, goog.events.EventTarget);
@@ -85,24 +54,16 @@ ol.Tile.prototype.dispatchChangeEvent = function() {
/**
* @param {Object=} opt_context Object.
- * @return {Image} Image.
+ * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/
-ol.Tile.prototype.getImage = function(opt_context) {
- if (goog.isDef(opt_context)) {
- var image;
- var key = goog.getUid(opt_context);
- if (key in this.imageByContext_) {
- return this.imageByContext_[key];
- } else if (goog.object.isEmpty(this.imageByContext_)) {
- image = this.image_;
- } else {
- image = /** @type {Image} */ (this.image_.cloneNode(false));
- }
- this.imageByContext_[key] = image;
- return image;
- } else {
- return this.image_;
- }
+ol.Tile.prototype.getImage = goog.abstractMethod;
+
+
+/**
+ * @return {string} Key.
+ */
+ol.Tile.prototype.getKey = function() {
+ return goog.getUid(this).toString();
};
@@ -110,59 +71,10 @@ ol.Tile.prototype.getImage = function(opt_context) {
* @return {ol.TileState} State.
*/
ol.Tile.prototype.getState = function() {
- return this.state_;
+ return this.state;
};
/**
- * Tracks loading or read errors.
- *
- * @private
*/
-ol.Tile.prototype.handleImageError_ = function() {
- this.state_ = ol.TileState.ERROR;
- this.unlistenImage_();
-};
-
-
-/**
- * Tracks successful image load.
- *
- * @private
- */
-ol.Tile.prototype.handleImageLoad_ = function() {
- this.state_ = ol.TileState.LOADED;
- this.unlistenImage_();
- this.dispatchChangeEvent();
-};
-
-
-/**
- * Load not yet loaded URI.
- */
-ol.Tile.prototype.load = function() {
- if (this.state_ == ol.TileState.IDLE) {
- this.state_ = ol.TileState.LOADING;
- goog.asserts.assert(goog.isNull(this.imageListenerKeys_));
- this.imageListenerKeys_ = [
- goog.events.listenOnce(this.image_, goog.events.EventType.ERROR,
- this.handleImageError_, false, this),
- goog.events.listenOnce(this.image_, goog.events.EventType.LOAD,
- this.handleImageLoad_, false, this)
- ];
- this.image_.src = this.src_;
- }
-};
-
-
-/**
- * Discards event handlers which listen for load completion or errors.
- *
- * @private
- */
-ol.Tile.prototype.unlistenImage_ = function() {
- goog.asserts.assert(!goog.isNull(this.imageListenerKeys_));
- goog.array.forEach(this.imageListenerKeys_, goog.events.unlistenByKey);
- this.imageListenerKeys_ = null;
-};
-
+ol.Tile.prototype.load = goog.abstractMethod;