Merge pull request #99 from twpayne/beyond-image-tiles

Beyond image tiles
This commit is contained in:
Tom Payne
2013-01-11 06:11:38 -08:00
13 changed files with 490 additions and 190 deletions
+38
View File
@@ -0,0 +1,38 @@
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<link rel="stylesheet" href="style.css" type="text/css">
<style type="text/css">
.map {
width: 400px;
height: 400px;
border: thin solid #cccccc;
margin: 1em;
}
</style>
<title>ol3 canvas tiles demo</title>
</head>
<body>
<h1 id="title">Canvas tiles example</h1>
<div id="shortdesc">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).</div>
<table>
<tr>
<th>DOM</th>
<th>WebGL</th>
</tr>
<tr>
<td><div id="domMap" class="map"></div></td>
<td><div id="webglMap" class="map"></div></td>
</tr>
</table>
<div id="docs">
<p>See the
<a href="canvas-tiles.js" target="_blank">canvas-tiles.js source</a>
to see how this is done.</p>
</div>
<div id="tags">layers, stamen, canvas</div>
<script src="loader.js?id=canvas-tiles" type="text/javascript"></script>
</body>
</html>
+43
View File
@@ -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);
+137
View File
@@ -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.<number, Image>}
*/
this.imageByContext_ = {};
/**
* @private
* @type {Array.<number>}
*/
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;
};
+12 -11
View File
@@ -17,6 +17,7 @@ goog.require('goog.events.EventType');
goog.require('goog.functions'); goog.require('goog.functions');
goog.require('goog.style'); goog.require('goog.style');
goog.require('goog.webgl'); goog.require('goog.webgl');
goog.require('ol.Tile');
goog.require('ol.layer.Layer'); goog.require('ol.layer.Layer');
goog.require('ol.layer.TileLayer'); goog.require('ol.layer.TileLayer');
goog.require('ol.renderer.webgl.FragmentShader'); 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} magFilter Mag filter.
* @param {number} minFilter Min filter. * @param {number} minFilter Min filter.
*/ */
ol.renderer.webgl.Map.prototype.bindImageTexture = ol.renderer.webgl.Map.prototype.bindTileTexture =
function(image, magFilter, minFilter) { function(tile, magFilter, minFilter) {
var gl = this.getGL(); var gl = this.getGL();
var imageKey = image.src; var tileKey = tile.getKey();
var textureCacheEntry = this.textureCache_[imageKey]; var textureCacheEntry = this.textureCache_[tileKey];
if (goog.isDef(textureCacheEntry)) { if (goog.isDef(textureCacheEntry)) {
gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture); gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture);
if (textureCacheEntry.magFilter != magFilter) { if (textureCacheEntry.magFilter != magFilter) {
@@ -244,7 +245,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
var texture = gl.createTexture(); var texture = gl.createTexture();
gl.bindTexture(goog.webgl.TEXTURE_2D, texture); gl.bindTexture(goog.webgl.TEXTURE_2D, texture);
gl.texImage2D(goog.webgl.TEXTURE_2D, 0, goog.webgl.RGBA, goog.webgl.RGBA, 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( gl.texParameteri(
goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter); goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter);
gl.texParameteri( gl.texParameteri(
@@ -253,7 +254,7 @@ ol.renderer.webgl.Map.prototype.bindImageTexture =
goog.webgl.CLAMP_TO_EDGE); goog.webgl.CLAMP_TO_EDGE);
gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T,
goog.webgl.CLAMP_TO_EDGE); goog.webgl.CLAMP_TO_EDGE);
this.textureCache_[imageKey] = { this.textureCache_[tileKey] = {
texture: texture, texture: texture,
magFilter: magFilter, magFilter: magFilter,
minFilter: minFilter minFilter: minFilter
@@ -485,11 +486,11 @@ ol.renderer.webgl.Map.prototype.initializeGL_ = function() {
/** /**
* @param {Image} image Image. * @param {ol.Tile} tile Tile.
* @return {boolean} Is image texture loaded. * @return {boolean} Is tile texture loaded.
*/ */
ol.renderer.webgl.Map.prototype.isImageTextureLoaded = function(image) { ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) {
return image.src in this.textureCache_; return tile.getKey() in this.textureCache_;
}; };
+12 -14
View File
@@ -386,9 +386,9 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
var tilesToDrawByZ = {}; var tilesToDrawByZ = {};
/** /**
* @type {Array.<Image>} * @type {Array.<ol.Tile>}
*/ */
var imagesToLoad = []; var tilesToLoad = [];
var allTilesLoaded = true; var allTilesLoaded = true;
@@ -405,12 +405,11 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
if (tileState == ol.TileState.IDLE) { if (tileState == ol.TileState.IDLE) {
tile.load(); tile.load();
} else if (tileState == ol.TileState.LOADED) { } else if (tileState == ol.TileState.LOADED) {
var image = tile.getImage(); if (mapRenderer.isTileTextureLoaded(tile)) {
if (mapRenderer.isImageTextureLoaded(image)) {
tilesToDrawByZ[z][tileCoord.toString()] = tile; tilesToDrawByZ[z][tileCoord.toString()] = tile;
return; return;
} else { } else {
imagesToLoad.push(image); tilesToLoad.push(tile);
} }
} else if (tileState == ol.TileState.ERROR) { } else if (tileState == ol.TileState.ERROR) {
return; return;
@@ -459,29 +458,28 @@ ol.renderer.webgl.TileLayer.prototype.renderFrame = function(time) {
framebufferExtentSize.height - 1; framebufferExtentSize.height - 1;
goog.vec.Vec4.setFromValues(uTileOffset, sx, sy, tx, ty); goog.vec.Vec4.setFromValues(uTileOffset, sx, sy, tx, ty);
gl.uniform4fv(this.locations_.uTileOffset, uTileOffset); gl.uniform4fv(this.locations_.uTileOffset, uTileOffset);
mapRenderer.bindImageTexture( mapRenderer.bindTileTexture(tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
tile.getImage(), goog.webgl.LINEAR, goog.webgl.LINEAR);
gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4); gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4);
}, this); }, this);
}, this); }, this);
if (!goog.array.isEmpty(imagesToLoad)) { if (!goog.array.isEmpty(tilesToLoad)) {
goog.events.listenOnce( goog.events.listenOnce(
map, map,
ol.MapEventType.POSTRENDER, ol.MapEventType.POSTRENDER,
goog.partial(function(mapRenderer, imagesToLoad) { goog.partial(function(mapRenderer, tilesToLoad) {
if (goog.DEBUG) { if (goog.DEBUG) {
this.logger.info( this.logger.info(
'uploading ' + imagesToLoad.length + ' textures'); 'uploading ' + tilesToLoad.length + ' textures');
} }
goog.array.forEach(imagesToLoad, function(image) { goog.array.forEach(tilesToLoad, function(tile) {
mapRenderer.bindImageTexture( mapRenderer.bindTileTexture(
image, goog.webgl.LINEAR, goog.webgl.LINEAR); tile, goog.webgl.LINEAR, goog.webgl.LINEAR);
}); });
if (goog.DEBUG) { if (goog.DEBUG) {
this.logger.info('uploaded textures'); this.logger.info('uploaded textures');
} }
}, mapRenderer, imagesToLoad)); }, mapRenderer, tilesToLoad));
} }
if (allTilesLoaded) { if (allTilesLoaded) {
+3 -3
View File
@@ -6,7 +6,7 @@ goog.require('goog.events');
goog.require('goog.events.EventType'); goog.require('goog.events.EventType');
goog.require('goog.net.Jsonp'); goog.require('goog.net.Jsonp');
goog.require('ol.TileCoverageArea'); goog.require('ol.TileCoverageArea');
goog.require('ol.source.TileSource'); goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ'); goog.require('ol.tilegrid.XYZ');
@@ -25,7 +25,7 @@ ol.BingMapsStyle = {
/** /**
* @constructor * @constructor
* @extends {ol.source.TileSource} * @extends {ol.source.ImageTileSource}
* @param {ol.source.BingMapsOptions} bingMapsOptions Bing Maps options. * @param {ol.source.BingMapsOptions} bingMapsOptions Bing Maps options.
*/ */
ol.source.BingMaps = function(bingMapsOptions) { ol.source.BingMaps = function(bingMapsOptions) {
@@ -57,7 +57,7 @@ ol.source.BingMaps = function(bingMapsOptions) {
}, goog.bind(this.handleImageryMetadataResponse, this)); }, goog.bind(this.handleImageryMetadataResponse, this));
}; };
goog.inherits(ol.source.BingMaps, ol.source.TileSource); goog.inherits(ol.source.BingMaps, ol.source.ImageTileSource);
/** /**
+125
View File
@@ -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.<number, HTMLCanvasElement>}
*/
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.<string, ol.DebugTile_>}
* 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;
}
};
+95
View File
@@ -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.<ol.Attribution>|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.<string, ol.ImageTile>}
* 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);
};
+3 -3
View File
@@ -10,14 +10,14 @@ goog.require('ol.Attribution');
goog.require('ol.Projection'); goog.require('ol.Projection');
goog.require('ol.TileCoord'); goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction'); goog.require('ol.TileUrlFunction');
goog.require('ol.source.TileSource'); goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.TileGrid'); goog.require('ol.tilegrid.TileGrid');
/** /**
* @constructor * @constructor
* @extends {ol.source.TileSource} * @extends {ol.source.ImageTileSource}
* @param {ol.source.TiledWMSOptions} tiledWMSOptions options. * @param {ol.source.TiledWMSOptions} tiledWMSOptions options.
*/ */
ol.source.TiledWMS = function(tiledWMSOptions) { 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);
+3 -3
View File
@@ -16,7 +16,7 @@ goog.require('goog.string');
goog.require('ol.Projection'); goog.require('ol.Projection');
goog.require('ol.TileCoverageArea'); goog.require('ol.TileCoverageArea');
goog.require('ol.TileUrlFunction'); goog.require('ol.TileUrlFunction');
goog.require('ol.source.TileSource'); goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ'); goog.require('ol.tilegrid.XYZ');
@@ -45,7 +45,7 @@ goog.exportSymbol('grid', grid);
/** /**
* @constructor * @constructor
* @extends {ol.source.TileSource} * @extends {ol.source.ImageTileSource}
* @param {ol.source.TileJSONOptions} tileJsonOptions TileJSON optios. * @param {ol.source.TileJSONOptions} tileJsonOptions TileJSON optios.
*/ */
ol.source.TileJSON = function(tileJsonOptions) { ol.source.TileJSON = function(tileJsonOptions) {
@@ -69,7 +69,7 @@ ol.source.TileJSON = function(tileJsonOptions) {
this.deferred_.addCallback(this.handleTileJSONResponse, this); this.deferred_.addCallback(this.handleTileJSONResponse, this);
}; };
goog.inherits(ol.source.TileJSON, ol.source.TileSource); goog.inherits(ol.source.TileJSON, ol.source.ImageTileSource);
/** /**
+2 -51
View File
@@ -14,11 +14,9 @@ goog.require('ol.tilegrid.TileGrid');
/** /**
* @typedef {{attributions: (Array.<ol.Attribution>|undefined), * @typedef {{attributions: (Array.<ol.Attribution>|undefined),
* crossOrigin: (null|string|undefined),
* extent: (ol.Extent|undefined), * extent: (ol.Extent|undefined),
* projection: (ol.Projection|undefined), * projection: (ol.Projection|undefined),
* tileGrid: (ol.tilegrid.TileGrid|undefined), * tileGrid: (ol.tilegrid.TileGrid|undefined)}}
* tileUrlFunction: (ol.TileUrlFunctionType|undefined)}}
*/ */
ol.source.TileSourceOptions; ol.source.TileSourceOptions;
@@ -44,29 +42,6 @@ ol.source.TileSource = function(tileSourceOptions) {
this.tileGrid = goog.isDef(tileSourceOptions.tileGrid) ? this.tileGrid = goog.isDef(tileSourceOptions.tileGrid) ?
tileSourceOptions.tileGrid : null; 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.<string, ol.Tile>}
* 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); goog.inherits(ol.source.TileSource, ol.source.Source);
@@ -83,31 +58,7 @@ ol.source.TileSource.prototype.getResolutions = function() {
* @param {ol.TileCoord} tileCoord Tile coordinate. * @param {ol.TileCoord} tileCoord Tile coordinate.
* @return {ol.Tile} Tile. * @return {ol.Tile} Tile.
*/ */
ol.source.TileSource.prototype.getTile = function(tileCoord) { ol.source.TileSource.prototype.getTile = goog.abstractMethod;
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);
};
/** /**
+3 -3
View File
@@ -11,7 +11,7 @@ goog.require('ol.Size');
goog.require('ol.TileCoord'); goog.require('ol.TileCoord');
goog.require('ol.TileUrlFunction'); goog.require('ol.TileUrlFunction');
goog.require('ol.TileUrlFunctionType'); goog.require('ol.TileUrlFunctionType');
goog.require('ol.source.TileSource'); goog.require('ol.source.ImageTileSource');
goog.require('ol.tilegrid.XYZ'); goog.require('ol.tilegrid.XYZ');
@@ -31,7 +31,7 @@ ol.source.XYZOptions;
/** /**
* @constructor * @constructor
* @extends {ol.source.TileSource} * @extends {ol.source.ImageTileSource}
* @param {ol.source.XYZOptions} xyzOptions XYZ options. * @param {ol.source.XYZOptions} xyzOptions XYZ options.
*/ */
ol.source.XYZ = function(xyzOptions) { 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);
+14 -102
View File
@@ -24,10 +24,8 @@ ol.TileState = {
* @constructor * @constructor
* @extends {goog.events.EventTarget} * @extends {goog.events.EventTarget}
* @param {ol.TileCoord} tileCoord Tile coordinate. * @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); goog.base(this);
@@ -37,39 +35,10 @@ ol.Tile = function(tileCoord, src, crossOrigin) {
this.tileCoord = tileCoord; this.tileCoord = tileCoord;
/** /**
* Image URI * @protected
*
* @private
* @type {string}
*/
this.src_ = src;
/**
* @private
* @type {ol.TileState} * @type {ol.TileState}
*/ */
this.state_ = ol.TileState.IDLE; this.state = ol.TileState.IDLE;
/**
* @private
* @type {Image}
*/
this.image_ = new Image();
if (!goog.isNull(crossOrigin)) {
this.image_.crossOrigin = crossOrigin;
}
/**
* @private
* @type {Object.<number, Image>}
*/
this.imageByContext_ = {};
/**
* @private
* @type {Array.<number>}
*/
this.imageListenerKeys_ = null;
}; };
goog.inherits(ol.Tile, goog.events.EventTarget); goog.inherits(ol.Tile, goog.events.EventTarget);
@@ -85,24 +54,16 @@ ol.Tile.prototype.dispatchChangeEvent = function() {
/** /**
* @param {Object=} opt_context Object. * @param {Object=} opt_context Object.
* @return {Image} Image. * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image.
*/ */
ol.Tile.prototype.getImage = function(opt_context) { ol.Tile.prototype.getImage = goog.abstractMethod;
if (goog.isDef(opt_context)) {
var image;
var key = goog.getUid(opt_context); /**
if (key in this.imageByContext_) { * @return {string} Key.
return this.imageByContext_[key]; */
} else if (goog.object.isEmpty(this.imageByContext_)) { ol.Tile.prototype.getKey = function() {
image = this.image_; return goog.getUid(this).toString();
} else {
image = /** @type {Image} */ (this.image_.cloneNode(false));
}
this.imageByContext_[key] = image;
return image;
} else {
return this.image_;
}
}; };
@@ -110,59 +71,10 @@ ol.Tile.prototype.getImage = function(opt_context) {
* @return {ol.TileState} State. * @return {ol.TileState} State.
*/ */
ol.Tile.prototype.getState = function() { ol.Tile.prototype.getState = function() {
return this.state_; return this.state;
}; };
/** /**
* Tracks loading or read errors.
*
* @private
*/ */
ol.Tile.prototype.handleImageError_ = function() { ol.Tile.prototype.load = goog.abstractMethod;
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;
};