terriajs
Version:
Geospatial data visualization platform.
208 lines • 9.01 kB
JavaScript
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