package.DataTile.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ol Show documentation
Show all versions of ol Show documentation
OpenLayers mapping library
The newest version!
/**
* @module ol/DataTile
*/
import Tile from './Tile.js';
import TileState from './TileState.js';
import {createCanvasContext2D} from './dom.js';
/**
* @typedef {HTMLImageElement|HTMLCanvasElement|HTMLVideoElement|ImageBitmap} ImageLike
*/
/**
* @typedef {Uint8Array|Uint8ClampedArray|Float32Array|DataView} ArrayLike
*/
/**
* Data that can be used with a DataTile.
* @typedef {ArrayLike|ImageLike} Data
*/
/**
* @param {Data} data Tile data.
* @return {ImageLike|null} The image-like data.
*/
export function asImageLike(data) {
return data instanceof Image ||
data instanceof HTMLCanvasElement ||
data instanceof HTMLVideoElement ||
data instanceof ImageBitmap
? data
: null;
}
/**
* @param {Data} data Tile data.
* @return {ArrayLike|null} The array-like data.
*/
export function asArrayLike(data) {
return data instanceof Uint8Array ||
data instanceof Uint8ClampedArray ||
data instanceof Float32Array ||
data instanceof DataView
? data
: null;
}
/**
* This is set as the cancellation reason when a tile is disposed.
*/
export const disposedError = new Error('disposed');
/**
* @type {CanvasRenderingContext2D|null}
*/
let sharedContext = null;
/**
* @param {ImageLike} image The image.
* @return {Uint8ClampedArray} The data.
*/
export function toArray(image) {
if (!sharedContext) {
sharedContext = createCanvasContext2D(
image.width,
image.height,
undefined,
{willReadFrequently: true},
);
}
const canvas = sharedContext.canvas;
const width = image.width;
if (canvas.width !== width) {
canvas.width = width;
}
const height = image.height;
if (canvas.height !== height) {
canvas.height = height;
}
sharedContext.clearRect(0, 0, width, height);
sharedContext.drawImage(image, 0, 0);
return sharedContext.getImageData(0, 0, width, height).data;
}
/**
* @type {import('./size.js').Size}
*/
const defaultSize = [256, 256];
/**
* @typedef {Object} Options
* @property {import("./tilecoord.js").TileCoord} tileCoord Tile coordinate.
* @property {function(): Promise} loader Data loader. For loaders that generate images,
* the promise should not resolve until the image is loaded.
* @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.
* @property {import('./size.js').Size} [size=[256, 256]] Tile size.
* @property {AbortController} [controller] An abort controller.
* @api
*/
class DataTile extends Tile {
/**
* @param {Options} options Tile options.
*/
constructor(options) {
const state = TileState.IDLE;
super(options.tileCoord, state, {
transition: options.transition,
interpolate: options.interpolate,
});
/**
* @type {function(): Promise}
* @private
*/
this.loader_ = options.loader;
/**
* @type {Data}
* @private
*/
this.data_ = null;
/**
* @type {Error}
* @private
*/
this.error_ = null;
/**
* @type {import('./size.js').Size|null}
* @private
*/
this.size_ = options.size || null;
/**
* @type {AbortController|null}
* @private
*/
this.controller_ = options.controller || null;
}
/**
* Get the tile size.
* @return {import('./size.js').Size} Tile size.
*/
getSize() {
if (this.size_) {
return this.size_;
}
const imageData = asImageLike(this.data_);
if (imageData) {
return [imageData.width, imageData.height];
}
return defaultSize;
}
/**
* Get the data for the tile.
* @return {Data} Tile data.
* @api
*/
getData() {
return this.data_;
}
/**
* Get any loading error.
* @return {Error} Loading error.
* @api
*/
getError() {
return this.error_;
}
/**
* Load the tile data.
* @api
* @override
*/
load() {
if (this.state !== TileState.IDLE && this.state !== TileState.ERROR) {
return;
}
this.state = TileState.LOADING;
this.changed();
const self = this;
this.loader_()
.then(function (data) {
self.data_ = data;
self.state = TileState.LOADED;
self.changed();
})
.catch(function (error) {
self.error_ = error;
self.state = TileState.ERROR;
self.changed();
});
}
/**
* Clean up.
* @override
*/
disposeInternal() {
if (this.controller_) {
this.controller_.abort(disposedError);
this.controller_ = null;
}
super.disposeInternal();
}
}
export default DataTile;