/** * @module ol/Tile */ import TileState from './TileState.js'; import {easeIn} from './easing.js'; import EventType from './events/EventType.js'; import EventTarget from './events/Target.js'; import {abstract} from './util.js'; /** * A function that takes a {@link module:ol/Tile~Tile} for the tile and a * `{string}` for the url as arguments. The default is * ```js * source.setTileLoadFunction(function(tile, src) { * tile.getImage().src = src; * }); * ``` * For more fine grained control, the load function can use fetch or XMLHttpRequest and involve * error handling: * * ```js * import TileState from 'ol/TileState.js'; * * source.setTileLoadFunction(function(tile, src) { * const xhr = new XMLHttpRequest(); * xhr.responseType = 'blob'; * xhr.addEventListener('loadend', function (evt) { * const data = this.response; * if (data !== undefined) { * tile.getImage().src = URL.createObjectURL(data); * } else { * tile.setState(TileState.ERROR); * } * }); * xhr.addEventListener('error', function () { * tile.setState(TileState.ERROR); * }); * xhr.open('GET', src); * xhr.send(); * }); * ``` * * @typedef {function(Tile, string): void} LoadFunction * @api */ /** * {@link module:ol/source/Tile~TileSource} sources use a function of this type to get * the url that provides a tile for a given tile coordinate. * * This function takes a {@link module:ol/tilecoord~TileCoord} for the tile * coordinate, a `{number}` representing the pixel ratio and a * {@link module:ol/proj/Projection~Projection} for the projection as arguments * and returns a `{string}` representing the tile URL, or undefined if no tile * should be requested for the passed tile coordinate. * * @typedef {function(import("./tilecoord.js").TileCoord, number, * import("./proj/Projection.js").default): (string|undefined)} UrlFunction * @api */ /** * @typedef {Object} Options * @property {number} [transition=250] A duration for tile opacity * transitions in milliseconds. A duration of 0 disables the opacity transition. * @property {boolean} [interpolate=false] Use interpolated values when resampling. By default, * the nearest neighbor is used when resampling. * @api */ /** * @classdesc * Base class for tiles. * * @abstract */ class Tile extends EventTarget { /** * @param {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate. * @param {import("./TileState.js").default} state State. * @param {Options} [options] Tile options. */ constructor(tileCoord, state, options) { super(); options = options ? options : {}; /** * @type {import("./tilecoord.js").TileCoord} */ this.tileCoord = tileCoord; /** * @protected * @type {import("./TileState.js").default} */ this.state = state; /** * A key assigned to the tile. This is used in conjunction with a source key * to determine if a cached version of this tile may be used by the renderer. * @type {string} */ this.key = ''; /** * The duration for the opacity transition. * @private * @type {number} */ this.transition_ = options.transition === undefined ? 250 : options.transition; /** * Lookup of start times for rendering transitions. If the start time is * equal to -1, the transition is complete. * @private * @type {Object} */ this.transitionStarts_ = {}; /** * @type {boolean} */ this.interpolate = !!options.interpolate; } /** * @protected */ changed() { this.dispatchEvent(EventType.CHANGE); } /** * Called by the tile cache when the tile is removed from the cache due to expiry */ release() { // to remove the `change` listener on this tile in `ol/TileQueue#handleTileChange` this.setState(TileState.EMPTY); } /** * @return {string} Key. */ getKey() { return this.key + '/' + this.tileCoord; } /** * Get the tile coordinate for this tile. * @return {import("./tilecoord.js").TileCoord} The tile coordinate. * @api */ getTileCoord() { return this.tileCoord; } /** * @return {import("./TileState.js").default} State. */ getState() { return this.state; } /** * Sets the state of this tile. If you write your own {@link module:ol/Tile~LoadFunction tileLoadFunction} , * it is important to set the state correctly to {@link module:ol/TileState~ERROR} * when the tile cannot be loaded. Otherwise the tile cannot be removed from * the tile queue and will block other requests. * @param {import("./TileState.js").default} state State. * @api */ setState(state) { if (this.state === TileState.EMPTY) { // no more state changes return; } if (this.state !== TileState.ERROR && this.state > state) { throw new Error('Tile load sequence violation'); } this.state = state; this.changed(); } /** * Load the image or retry if loading previously failed. * Loading is taken care of by the tile queue, and calling this method is * only needed for preloading or for reloading in case of an error. * @abstract * @api */ load() { abstract(); } /** * Get the alpha value for rendering. * @param {string} id An id for the renderer. * @param {number} time The render frame time. * @return {number} A number between 0 and 1. */ getAlpha(id, time) { if (!this.transition_) { return 1; } let start = this.transitionStarts_[id]; if (!start) { start = time; this.transitionStarts_[id] = start; } else if (start === -1) { return 1; } const delta = time - start + 1000 / 60; // avoid rendering at 0 if (delta >= this.transition_) { return 1; } return easeIn(delta / this.transition_); } /** * Determine if a tile is in an alpha transition. A tile is considered in * transition if tile.getAlpha() has not yet been called or has been called * and returned 1. * @param {string} id An id for the renderer. * @return {boolean} The tile is in transition. */ inTransition(id) { if (!this.transition_) { return false; } return this.transitionStarts_[id] !== -1; } /** * Mark a transition as complete. * @param {string} id An id for the renderer. */ endTransition(id) { if (this.transition_) { this.transitionStarts_[id] = -1; } } /** * @override */ disposeInternal() { this.release(); super.disposeInternal(); } } export default Tile;