UNPKG

ol

Version:

OpenLayers mapping library

211 lines (187 loc) • 7.1 kB
/** * @module ol/source/ImageTile */ import {expandUrl, pickUrl, renderXYZTemplate} from '../uri.js'; import DataTileSource from './DataTile.js'; /** * Image tile loading function. The function is called with z, x, and y tile coordinates and * returns an {@link import("../DataTile.js").ImageLike image} or a promise for the same. * * @typedef {function(number, number, number, import("./DataTile.js").LoaderOptions):(import("../DataTile.js").ImageLike|Promise<import("../DataTile.js").ImageLike>)} Loader */ /** * @typedef {function(number, number, number, import("./DataTile.js").LoaderOptions):string} UrlGetter */ /** * @typedef {string | Array<string> | UrlGetter} UrlLike */ /** * @typedef {Object} Options * @property {UrlLike} [url] The image URL template. In addition to a single URL template, an array of URL templates or a function * can be provided. If a function is provided, it will be called with z, x, y tile coordinates and loader options and should * return a URL. * @property {Loader} [loader] Data loader. Called with z, x, and y tile coordinates. * Returns an {@link import("../DataTile.js").ImageLike image} for a tile or a promise for the same. * The promise should not resolve until the image is loaded. If the `url` option is provided, a loader will be created. * @property {import("./Source.js").AttributionLike} [attributions] Attributions. * @property {boolean} [attributionsCollapsible=true] Attributions are collapsible. * @property {number} [maxZoom=42] Optional max zoom level. Not used if `tileGrid` is provided. * @property {number} [minZoom=0] Optional min zoom level. Not used if `tileGrid` is provided. * @property {number|import("../size.js").Size} [tileSize=[256, 256]] The pixel width and height of the source tiles. * This may be different than the rendered pixel size if a `tileGrid` is provided. * @property {number} [gutter=0] The size in pixels of the gutter around data tiles to ignore. * This allows artifacts of rendering at tile edges to be ignored. * Supported data should be wider and taller than the tile size by a value of `2 x gutter`. * @property {number} [maxResolution] Optional tile grid resolution at level zero. Not used if `tileGrid` is provided. * @property {import("../proj.js").ProjectionLike} [projection='EPSG:3857'] Tile projection. * @property {import("../tilegrid/TileGrid.js").default} [tileGrid] Tile grid. * @property {import("./Source.js").State} [state] The source state. * @property {boolean} [wrapX=true] Render tiles beyond the antimeridian. * @property {number} [transition] Transition time when fading in new tiles (in miliseconds). * @property {boolean} [interpolate=true] Use interpolated values when resampling. * @property {import('./DataTile.js').CrossOriginAttribute} [crossOrigin='anonymous'] The crossOrigin property to pass to loaders for image data. * @property {number|import("../array.js").NearestDirectionFunction} [zDirection=0] * Choose whether to use tiles with a higher or lower zoom level when between integer * zoom levels. See {@link module:ol/tilegrid/TileGrid~TileGrid#getZForResolution}. */ const loadError = new Error('Image failed to load'); /** * @param {string} template The image url template. * @param {number} z The tile z coordinate. * @param {number} x The tile x coordinate. * @param {number} y The tile y coordinate. * @param {import('./DataTile.js').LoaderOptions} options The loader options. * @return {Promise<HTMLImageElement>} Resolves with a loaded image. */ function loadImage(template, z, x, y, options) { return new Promise((resolve, reject) => { const image = new Image(); image.crossOrigin = options.crossOrigin ?? null; image.addEventListener('load', () => resolve(image)); image.addEventListener('error', () => reject(loadError)); image.src = renderXYZTemplate(template, z, x, y, options.maxY); }); } /** * @param {Array<string>} templates The url templates. * @return {Loader} The image loader. */ function makeLoaderFromTemplates(templates) { return function (z, x, y, options) { const template = pickUrl(templates, z, x, y); return loadImage(template, z, x, y, options); }; } /** * @param {UrlGetter} getter The url getter. * @return {Loader} The image loader. */ function makeLoaderFromGetter(getter) { return function (z, x, y, options) { const url = getter(z, x, y, options); return loadImage(url, z, x, y, options); }; } /** * @param {UrlLike} url The URL-like option. * @return {Loader} The tile loader. */ function makeLoaderFromUrlLike(url) { /** * @type {Loader} */ let loader; if (Array.isArray(url)) { loader = makeLoaderFromTemplates(url); } else if (typeof url === 'string') { const urls = expandUrl(url); loader = makeLoaderFromTemplates(urls); } else if (typeof url === 'function') { loader = makeLoaderFromGetter(url); } else { throw new Error( 'The url option must be a single template, an array of templates, or a function for getting a URL', ); } return loader; } let keyCount = 0; /** * @param {UrlLike} url The URL-like option. * @return {string} A key for the URL. */ function keyFromUrlLike(url) { if (Array.isArray(url)) { return url.join('\n'); } if (typeof url === 'string') { return url; } ++keyCount; return 'url-function-key-' + keyCount; } /** * @classdesc * A source for typed array data tiles. * * @extends DataTileSource<import("../ImageTile.js").default> * @fires import("./Tile.js").TileSourceEvent * @api */ class ImageTileSource extends DataTileSource { /** * @param {Options} [options] DataTile source options. */ constructor(options) { options = options || {}; /** * @type {Loader} */ let loader = options.loader; /** * @type {string} */ let key; if (options.url) { loader = makeLoaderFromUrlLike(options.url); key = keyFromUrlLike(options.url); } /** * @type {import('./Source.js').State} */ const state = !loader ? 'loading' : options.state; const wrapX = options.wrapX === undefined ? true : options.wrapX; super({ loader: loader, key: key, attributions: options.attributions, attributionsCollapsible: options.attributionsCollapsible, maxZoom: options.maxZoom, minZoom: options.minZoom, tileSize: options.tileSize, gutter: options.gutter, maxResolution: options.maxResolution, projection: options.projection, tileGrid: options.tileGrid, state: state, wrapX: wrapX, transition: options.transition, interpolate: options.interpolate !== false, crossOrigin: options.crossOrigin, zDirection: options.zDirection, }); } /** * @param {UrlLike} url The new URL. * @api */ setUrl(url) { const loader = makeLoaderFromUrlLike(url); this.setLoader(loader); this.setKey(keyFromUrlLike(url)); if (this.getState() !== 'ready') { this.setState('ready'); } } } export default ImageTileSource;