UNPKG

terriajs

Version:

Geospatial data visualization platform.

208 lines 9.01 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import i18next from "i18next"; import { computed, makeObservable, observable, onBecomeObserved, onBecomeUnobserved, runInAction } from "mobx"; import GeographicTilingScheme from "terriajs-cesium/Source/Core/GeographicTilingScheme"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import WebMercatorTilingScheme from "terriajs-cesium/Source/Core/WebMercatorTilingScheme"; import CatalogMemberMixin from "../../../ModelMixins/CatalogMemberMixin"; import MappableMixin from "../../../ModelMixins/MappableMixin"; import CogCatalogItemTraits from "../../../Traits/TraitsClasses/CogCatalogItemTraits"; import CreateModel from "../../Definition/CreateModel"; import LoadableStratum from "../../Definition/LoadableStratum"; import StratumOrder from "../../Definition/StratumOrder"; import proxyCatalogItemUrl from "../proxyCatalogItemUrl"; /** * Loadable stratum for overriding CogCatalogItem traits */ class CogLoadableStratum extends LoadableStratum(CogCatalogItemTraits) { model; static stratumName = "cog-loadable-stratum"; constructor(model) { super(); this.model = model; makeObservable(this); } duplicateLoadableStratum(model) { return new CogLoadableStratum(model); } get shortReport() { return this.model.terria.currentViewer.type === "Leaflet" ? // Warn for 2D mode i18next.t("models.commonModelErrors.3dTypeIn2dMode", this) : this.model._imageryProvider?.tilingScheme && // Show warning for experimental reprojection freature if not using EPSG 3857 or 4326 isCustomTilingScheme(this.model._imageryProvider?.tilingScheme) ? i18next.t("models.cogCatalogItem.experimentalReprojectionWarning", this) : undefined; } get rectangle() { const rectangle = this.model._imageryProvider?.rectangle; if (!rectangle) { return; } const { west, south, east, north } = rectangle; return { west: CesiumMath.toDegrees(west), south: CesiumMath.toDegrees(south), east: CesiumMath.toDegrees(east), north: CesiumMath.toDegrees(north) }; } } __decorate([ computed ], CogLoadableStratum.prototype, "shortReport", null); __decorate([ computed ], CogLoadableStratum.prototype, "rectangle", null); StratumOrder.addLoadStratum(CogLoadableStratum.stratumName); /** * Creates a Cloud Optimised Geotiff catalog item. * * Currently it can render EPSG 4326/3857 COG files. There is experimental * support for other projections, however it is less performant and could have * unknown issues. */ export default class CogCatalogItem extends MappableMixin(CatalogMemberMixin(CreateModel(CogCatalogItemTraits))) { static type = "cog"; /** * Private imageryProvider instance. This is set once forceLoadMapItems is * called. */ _imageryProvider; /** * The reprojector function to use for reprojecting non native projections * * Exposed here as instance variable for stubbing in specs. */ reprojector = reprojector; get type() { return CogCatalogItem.type; } constructor(id, terria, sourceReference) { super(id, terria, sourceReference); makeObservable(this); this.strata.set(CogLoadableStratum.stratumName, new CogLoadableStratum(this)); // Destroy the imageryProvider when `mapItems` is no longer consumed. This // is so that the webworkers and other resources created by the // imageryProvider can be freed. Ideally, there would be a more explicit // `destroy()` method in Terria life-cycle so that we don't have to rely on // mapItems becoming observed or unobserved. onBecomeUnobserved(this, "mapItems", () => { if (this._imageryProvider) { this._imageryProvider.destroy(); this._imageryProvider = undefined; } }); // Re-create the imageryProvider if `mapItems` is consumed again after we // destroyed it onBecomeObserved(this, "mapItems", () => { if (!this._imageryProvider && !this.isLoadingMapItems) { this.loadMapItems(true); } }); } async forceLoadMapItems() { if (!this.url) { return; } const url = proxyCatalogItemUrl(this, this.url); const imageryProvider = await this.createImageryProvider(url); runInAction(() => { this._imageryProvider = imageryProvider; }); } get mapItems() { const imageryProvider = this._imageryProvider; if (!imageryProvider) { return []; } return [ { show: this.show, alpha: this.opacity, // The 'requestImage' method in Cesium's ImageryProvider has a return type that is stricter than necessary. // In our custom ImageryProvider, we return ImageData, which is also a valid return type. // However, since the current Cesium type definitions do not reflect this flexibility, we use a TypeScript ignore comment ('@ts-ignore') // to suppress the type checking error. This is a temporary solution until the type definitions in Cesium are updated to accommodate ImageData. // @ts-expect-error - The return type of 'requestImage' method in our custom ImageryProvider can be ImageData, which is not currently allowed in Cesium's type definitions, but is fine. imageryProvider, clippingRectangle: this.cesiumRectangle } ]; } /** * Create TIFFImageryProvider for the given url. */ async createImageryProvider(url) { // lazy load the imagery provider, only when needed const [{ default: TIFFImageryProvider }, { default: proj4 }] = await Promise.all([ import("terriajs-tiff-imagery-provider"), import("proj4-fully-loaded") ]); return runInAction(() => TIFFImageryProvider.fromUrl(url, { credit: this.credit, tileSize: this.tileSize, maximumLevel: this.maximumLevel, minimumLevel: this.minimumLevel, enablePickFeatures: this.allowFeaturePicking, hasAlphaChannel: this.hasAlphaChannel, // used for reprojecting from an unknown projection to 4326/3857 // note that this is experimental and could be slow as it runs on the main thread projFunc: this.reprojector(proj4), // make sure we omit `undefined` options so as not to override the library defaults renderOptions: omitUndefined({ nodata: this.renderOptions.nodata, convertToRGB: this.renderOptions.convertToRGB, resampleMethod: this.renderOptions.resampleMethod }) })); } } __decorate([ observable ], CogCatalogItem.prototype, "_imageryProvider", void 0); __decorate([ computed ], CogCatalogItem.prototype, "mapItems", null); /** * Function returning a custom reprojector */ function reprojector(proj4) { return (code) => { if (![4326, 3857, 900913].includes(code)) { try { const prj = proj4("EPSG:4326", `EPSG:${code}`); if (prj) return { project: prj.forward, unproject: prj.inverse }; } catch (e) { console.error(e); } } }; } /** * Returns true if the tilingScheme is custom */ function isCustomTilingScheme(tilingScheme) { // The upstream library defines a TIFFImageryTillingScheme but it is not // exported so we have to check if it is not one of the standard Cesium // tiling schemes. Also, because TIFFImageryTillingScheme derives from // WebMercatorTilingScheme, we cannot simply do an `instanceof` check, we // compare the exact constructor instead. return (tilingScheme.constructor !== WebMercatorTilingScheme && tilingScheme.constructor !== GeographicTilingScheme); } function omitUndefined(obj) { return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== undefined)); } //# sourceMappingURL=CogCatalogItem.js.map