diff --git a/examples/tile-load-events.html b/examples/tile-load-events.html
new file mode 100644
index 0000000000..4bea910614
--- /dev/null
+++ b/examples/tile-load-events.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+ Tile load events example
+
+
+
+
+
+
+
+
+
+
+
+
+
Tile load events example
+
Example using tile load events.
+
+
tile, events, loading
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/tile-load-events.js b/examples/tile-load-events.js
new file mode 100644
index 0000000000..ea00420993
--- /dev/null
+++ b/examples/tile-load-events.js
@@ -0,0 +1,35 @@
+goog.require('ol.Map');
+goog.require('ol.View');
+goog.require('ol.control');
+goog.require('ol.layer.Tile');
+goog.require('ol.source.OSM');
+
+
+var source = new ol.source.OSM();
+source.on('tileloadstart', function(event) {
+ console.log('start', event.tile.getImage().src);
+});
+source.on('tileloadend', function(event) {
+ console.log('end', event.tile.getImage().src);
+});
+source.on('tileloaderror', function(event) {
+ console.log('error', event.tile.getImage().src);
+});
+
+
+var map = new ol.Map({
+ layers: [
+ new ol.layer.Tile({source: source})
+ ],
+ controls: ol.control.defaults({
+ attributionOptions: /** @type {olx.control.AttributionOptions} */ ({
+ collapsible: false
+ })
+ }),
+ renderer: exampleNS.getRendererFromQueryString(),
+ target: 'map',
+ view: new ol.View({
+ center: [0, 0],
+ zoom: 2
+ })
+});
diff --git a/externs/oli.js b/externs/oli.js
index 9ffb8877f3..a97117e207 100644
--- a/externs/oli.js
+++ b/externs/oli.js
@@ -210,6 +210,17 @@ oli.render.Event.prototype.vectorContext;
oli.source;
+/**
+ * @interface
+ */
+oli.source.TileEvent = function() {};
+
+
+/**
+ * @type {ol.Tile}
+ */
+oli.source.TileEvent.prototype.tile;
+
/**
* @interface
diff --git a/src/ol/source/tileimagesource.js b/src/ol/source/tileimagesource.js
index 600eb69313..1e53f22d10 100644
--- a/src/ol/source/tileimagesource.js
+++ b/src/ol/source/tileimagesource.js
@@ -1,6 +1,8 @@
goog.provide('ol.source.TileImage');
goog.require('goog.asserts');
+goog.require('goog.events');
+goog.require('goog.events.EventType');
goog.require('ol.ImageTile');
goog.require('ol.TileCache');
goog.require('ol.TileCoord');
@@ -17,6 +19,7 @@ goog.require('ol.source.Tile');
* Base class for sources providing images divided into a tile grid.
*
* @constructor
+ * @fires ol.source.TileEvent
* @extends {ol.source.Tile}
* @param {olx.source.TileImageOptions} options Image tile options.
* @api
@@ -118,6 +121,9 @@ ol.source.TileImage.prototype.getTile =
goog.isDef(tileUrl) ? tileUrl : '',
this.crossOrigin,
this.tileLoadFunction);
+ goog.events.listen(tile, goog.events.EventType.CHANGE,
+ this.handleTileChange_, false, this);
+
this.tileCache.set(tileCoordKey, tile);
return tile;
}
@@ -142,6 +148,30 @@ ol.source.TileImage.prototype.getTileUrlFunction = function() {
};
+/**
+ * Handle tile change events.
+ * @param {goog.events.Event} event Event.
+ * @private
+ */
+ol.source.TileImage.prototype.handleTileChange_ = function(event) {
+ var tile = /** @type {ol.Tile} */ (event.target);
+ switch (tile.getState()) {
+ case ol.TileState.LOADING:
+ this.dispatchEvent(
+ new ol.source.TileEvent(ol.source.TileEventType.TILELOADSTART, tile));
+ break;
+ case ol.TileState.LOADED:
+ this.dispatchEvent(
+ new ol.source.TileEvent(ol.source.TileEventType.TILELOADEND, tile));
+ break;
+ case ol.TileState.ERROR:
+ this.dispatchEvent(
+ new ol.source.TileEvent(ol.source.TileEventType.TILELOADERROR, tile));
+ break;
+ }
+};
+
+
/**
* @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
* @api
diff --git a/src/ol/source/tilesource.js b/src/ol/source/tilesource.js
index 3457b4cb13..504910ae84 100644
--- a/src/ol/source/tilesource.js
+++ b/src/ol/source/tilesource.js
@@ -1,6 +1,7 @@
goog.provide('ol.source.Tile');
goog.provide('ol.source.TileOptions');
+goog.require('goog.events.Event');
goog.require('goog.functions');
goog.require('ol.Attribution');
goog.require('ol.Extent');
@@ -206,3 +207,59 @@ ol.source.Tile.prototype.getTilePixelSize =
* @param {number} y Tile coordinate y.
*/
ol.source.Tile.prototype.useTile = goog.nullFunction;
+
+
+
+/**
+ * @classdesc
+ * Events emitted by {@link ol.source.Tile} instances are instances of this
+ * type.
+ *
+ * @constructor
+ * @extends {goog.events.Event}
+ * @implements {oli.source.TileEvent}
+ * @param {string} type Type.
+ * @param {ol.Tile} tile The tile.
+ */
+ol.source.TileEvent = function(type, tile) {
+
+ goog.base(this, type);
+
+ /**
+ * The tile related to the event.
+ * @type {ol.Tile}
+ * @api
+ */
+ this.tile = tile;
+
+};
+goog.inherits(ol.source.TileEvent, goog.events.Event);
+
+
+/**
+ * @enum {string}
+ */
+ol.source.TileEventType = {
+
+ /**
+ * Triggered when a tile starts loading.
+ * @event ol.source.TileEvent#tileloadstart
+ * @api
+ */
+ TILELOADSTART: 'tileloadstart',
+
+ /**
+ * Triggered when a tile finishes loading.
+ * @event ol.source.TileEvent#tileloadend
+ * @api
+ */
+ TILELOADEND: 'tileloadend',
+
+ /**
+ * Triggered if tile loading results in an error.
+ * @event ol.source.TileEvent#tileloaderror
+ * @api
+ */
+ TILELOADERROR: 'tileloaderror'
+
+};