UNPKG

@giro3d/giro3d

Version:

A JS/WebGL framework for 3D geospatial data visualization

142 lines (118 loc) 4.79 kB
/* * Copyright (c) 2015-2018, IGN France. * Copyright (c) 2018-2026, Giro3D team. * SPDX-License-Identifier: MIT */ import { CanvasTexture, Color } from 'three'; import type CoordinateSystem from '../core/geographic/CoordinateSystem'; import type Extent from '../core/geographic/Extent'; import type { GridExtent } from '../core/geographic/Extent'; import type { CustomContainsFn, GetImageOptions, ImageResponse } from './ImageSource'; import PromiseUtils from '../utils/PromiseUtils'; import ImageSource, { ImageResult } from './ImageSource'; export default class DebugSource extends ImageSource { public readonly isDebugSource: boolean = true as const; public override readonly type = 'DebugSource' as const; private readonly _delay: () => number; private readonly _extent: Extent; private readonly _opacity: number; private readonly _subdivisions: number; private readonly _color: Color | ((options: GetImageOptions) => Color); /** * @param options - options */ public constructor(options: { /** The extent. */ extent: Extent; /** The delay before loading the images, in milliseconds. */ delay?: number | (() => number); /** The opacity of the images. */ opacity?: number; /** The color of the images. */ color?: Color | ((options: GetImageOptions) => Color); /** How many images per tile are served. */ subdivisions?: number; /** The custom function to test if a given extent is contained in this source. */ containsFn?: CustomContainsFn; }) { super(options); const { delay, subdivisions, opacity, extent, color } = options; if (delay != null) { if (typeof delay === 'function') { this._delay = delay; } else if (typeof delay === 'number') { this._delay = (): number => delay; } else { this._delay = (): number => 0; } } else { this._delay = (): number => 0; } this._extent = options.extent; this._opacity = opacity ?? 1; this._subdivisions = subdivisions ?? 1; this._color = color ?? new Color(1, 1, 1); this._extent = extent; } public override adjustExtentAndPixelSize( requestExtent: Extent, requestWidth: number, requestHeight: number, ): GridExtent | null { // To help visualize the images, we don't adjust their extent at all. return { extent: requestExtent, height: requestHeight, width: requestWidth, }; } private getImage(width: number, height: number, id: string, color: Color): CanvasTexture { const canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; const context = canvas.getContext('2d', { willReadFrequently: true }); if (!context) { throw new Error('could not acquire 2d context'); } const prefix = id.substring(0, 10); context.fillStyle = `#${color.getHexString()}`; context.globalAlpha = this._opacity ?? 1; context.fillRect(0, 0, width, height); context.globalAlpha = 1; context.strokeStyle = `#${color.getHexString()}`; context.lineWidth = 16; context.strokeRect(0, 0, width, height); context.fillStyle = 'black'; const margin = 20; context.fillText(prefix, margin, margin); const texture = new CanvasTexture(canvas); return texture; } public getCrs(): CoordinateSystem { return this._extent.crs; } public getExtent(): Extent { return this._extent; } public getImages(options: GetImageOptions): ImageResponse[] { const { extent, width, height, signal, id } = options; const subdivs = this._subdivisions; const extents = extent.split(subdivs, subdivs); const requests = []; const w = Math.round(width / subdivs); const h = Math.round(height / subdivs); for (let i = 0; i < extents.length; i++) { const ex = extents[i]; const imageId = `${id}-${i}`; const color = typeof this._color === 'function' ? this._color(options) : this._color; const request = (): Promise<ImageResult> => PromiseUtils.delay(this._delay()).then(() => { signal?.throwIfAborted(); const texture = this.getImage(w, h, imageId, color); return new ImageResult({ extent: ex, texture, id: imageId }); }); requests.push({ id: imageId, request }); } return requests; } }