UNPKG

@giro3d/giro3d

Version:

A JS/WebGL framework for 3D geospatial data visualization

130 lines (108 loc) 3.79 kB
/* * Copyright (c) 2015-2018, IGN France. * Copyright (c) 2018-2026, Giro3D team. * SPDX-License-Identifier: MIT */ import type { TypedArray } from 'three'; import { fromBlob, Pool } from 'geotiff'; import { FloatType, UnsignedByteType } from 'three'; import type { DecodeOptions, DecodeResult } from './ImageFormat'; import TextureGenerator from '../utils/TextureGenerator'; import ImageFormat from './ImageFormat'; let geotiffWorkerPool: Pool; /** * Decoder for TIFF images. * */ class GeoTIFFFormat extends ImageFormat { public readonly isGeoTIFFFormat: boolean = true as const; public override readonly type = 'GeoTIFFFormat'; private readonly _enableWorkers: boolean; /** * @param options - Decoder options. */ public constructor(options?: { /** * Enables processing raster data in web workers. * @defaultValue true */ enableWorkers?: boolean; }) { super(true, FloatType); this._enableWorkers = options?.enableWorkers ?? true; } /** * Decode a tiff blob into a * [DataTexture](https://threejs.org/docs/?q=texture#api/en/textures/DataTexture) containing * the elevation data. * * @param blob - the data to decode * @param options - the decoding options */ public async decode(blob: Blob, options?: DecodeOptions): Promise<DecodeResult> { const tiff = await fromBlob(blob); const image = await tiff.getImage(); const height = image.getHeight(); const width = image.getWidth(); let dataType; const nodata = options?.noDataValue ?? image.getGDALNoData() ?? undefined; if (image.getBitsPerSample() === 8) { dataType = UnsignedByteType; } else { dataType = FloatType; } const spp = image.getSamplesPerPixel(); // Let's use web workers to decode TIFF in the background if (window.Worker != null && geotiffWorkerPool == null) { geotiffWorkerPool = new Pool(); } let inputBuffers: TypedArray[]; switch (spp) { case 1: { // grayscale const [v] = (await image.readRasters({ pool: geotiffWorkerPool, })) as TypedArray[]; inputBuffers = [v]; } break; case 2: { // grayscale with alpha const [v, a] = (await image.readRasters({ pool: geotiffWorkerPool, })) as TypedArray[]; inputBuffers = [v, a]; } break; case 3: { // RGB const [r, g, b] = (await image.readRasters({ pool: geotiffWorkerPool, })) as TypedArray[]; inputBuffers = [r, g, b]; } break; case 4: { // RGBA const [r, g, b, a] = (await image.readRasters({ pool: geotiffWorkerPool, })) as TypedArray[]; inputBuffers = [r, g, b, a]; } break; default: throw new Error(`unsupported channel count: ${spp}`); } const result = await TextureGenerator.createDataTextureAsync( { width, height, nodata, enableWorkers: this._enableWorkers }, dataType, ...inputBuffers, ); return { texture: result.texture, min: result.min, max: result.max }; } } export default GeoTIFFFormat;