UNPKG

terriajs

Version:

Geospatial data visualization platform.

229 lines 10.7 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 { action, computed, observable, onBecomeObserved, makeObservable, override } from "mobx"; import Cartographic from "terriajs-cesium/Source/Core/Cartographic"; import Ellipsoid from "terriajs-cesium/Source/Core/Ellipsoid"; import JulianDate from "terriajs-cesium/Source/Core/JulianDate"; import CesiumMath from "terriajs-cesium/Source/Core/Math"; import filterOutUndefined from "../Core/filterOutUndefined"; import runLater from "../Core/runLater"; import CommonStrata from "../Models/Definition/CommonStrata"; import createStratumInstance from "../Models/Definition/createStratumInstance"; import TerriaFeature from "../Models/Feature/Feature"; import { TimeFilterCoordinates } from "../Traits/TraitsClasses/TimeFilterTraits"; import DiscretelyTimeVaryingMixin from "./DiscretelyTimeVaryingMixin"; import MappableMixin, { ImageryParts } from "./MappableMixin"; /** * A Mixin for filtering the dates for which imagery is available at a location * picked by the user * * When `timeFilterPropertyName` is set, we look for a property of that name in * the feature query response at the picked location. The property value should be * an array of dates for which imagery is available at the location. This * Mixin is used to implement the Location filter feature for Satellite * Imagery. */ function TimeFilterMixin(Base) { class TimeFilterMixin extends DiscretelyTimeVaryingMixin(Base) { _currentTimeFilterFeature; constructor(...args) { super(...args); makeObservable(this); // Try to resolve the timeFilterFeature from the co-ordinates that might // be stored in the traits. We only have to resolve the time filter // feature once to get the list of times. const disposeListener = onBecomeObserved(this, "mapItems", () => { runLater(action(async () => { if (!MappableMixin.isMixedInto(this)) { disposeListener(); return; } const coords = coordinatesFromTraits(this.timeFilterCoordinates); if (coords) { this.setTimeFilterFromLocation(coords); } disposeListener(); })); }); } /** * Loads the time filter by querying the imagery layers at the given * co-ordinates. * * @param coordinates.position The lat, lon, height of the location to pick in degrees * @param coordinates.tileCoords the x, y, level co-ordinates of the tile to pick * @returns Promise that fulfills when the picking is complete. * The promise resolves to `false` if the filter could not be set for some reason. */ async setTimeFilterFromLocation(coordinates) { const timeProperty = this.timeFilterPropertyName; if (this.terria.allowFeatureInfoRequests === false || timeProperty === undefined || !MappableMixin.isMixedInto(this)) { return false; } const pickTileCoordinates = coordinates.tileCoords; const pickCartographicPosition = Cartographic.fromDegrees(coordinates.position.longitude, coordinates.position.latitude, coordinates.position.height); // Pick all imagery provider for this item const imageryProviders = filterOutUndefined(this.mapItems.map((mapItem) => ImageryParts.is(mapItem) ? mapItem.imageryProvider : undefined)); const picks = await pickImageryProviders(imageryProviders, pickTileCoordinates, pickCartographicPosition); // Find the first imageryProvider and feature with a matching time property let timeFeature; let imageryUrl; for (let i = 0; i < picks.length; i++) { const pick = picks[i]; const imageryFeature = pick.imageryFeatures.find((f) => f.properties[timeProperty] !== undefined); imageryUrl = pick.imageryProvider.url; if (imageryFeature && imageryUrl) { imageryFeature.position = imageryFeature.position ?? pickCartographicPosition; timeFeature = TerriaFeature.fromImageryLayerFeatureInfo(imageryFeature); break; } } if (!timeFeature || !imageryUrl) { return false; } // Update time filter this.setTimeFilterFeature(timeFeature, { [imageryUrl]: { ...coordinates.position, ...coordinates.tileCoords } }); return true; } get hasTimeFilterMixin() { return true; } get canFilterTimeByFeature() { return this.timeFilterPropertyName !== undefined; } get imageryUrls() { if (!MappableMixin.isMixedInto(this)) return []; return filterOutUndefined(this.mapItems.map( // @ts-expect-error url attr (mapItem) => ImageryParts.is(mapItem) && mapItem.imageryProvider.url)); } get featureTimesAsJulianDates() { if (this._currentTimeFilterFeature === undefined || this._currentTimeFilterFeature.properties === undefined || this.timeFilterPropertyName === undefined) { return; } const featureTimes = this._currentTimeFilterFeature.properties[this.timeFilterPropertyName]?.getValue(this.currentTime); if (!Array.isArray(featureTimes)) { return; } return filterOutUndefined(featureTimes.map((s) => { try { return s === undefined ? undefined : JulianDate.fromIso8601(s); } catch { return undefined; } })); } get discreteTimesAsSortedJulianDates() { const featureTimes = this.featureTimesAsJulianDates; if (featureTimes === undefined) { return super.discreteTimesAsSortedJulianDates; } return super.discreteTimesAsSortedJulianDates?.filter((dt) => featureTimes.some((d) => d.equals(dt.time))); } get timeFilterFeature() { return this._currentTimeFilterFeature; } setTimeFilterFeature(feature, providerCoords) { if (!MappableMixin.isMixedInto(this) || providerCoords === undefined) return; this._currentTimeFilterFeature = feature; if (!this.currentTimeAsJulianDate) { return; } if (!feature.position) { return; } const position = feature.position.getValue(this.currentTimeAsJulianDate); if (position === undefined) return; const cartographic = Ellipsoid.WGS84.cartesianToCartographic(position); const featureImageryUrl = this.imageryUrls.find((url) => providerCoords[url]); const tileCoords = featureImageryUrl && providerCoords[featureImageryUrl]; if (!tileCoords) return; this.setTrait(CommonStrata.user, "timeFilterCoordinates", createStratumInstance(TimeFilterCoordinates, { tile: tileCoords, longitude: CesiumMath.toDegrees(cartographic.longitude), latitude: CesiumMath.toDegrees(cartographic.latitude), height: cartographic.height })); } removeTimeFilterFeature() { this._currentTimeFilterFeature = undefined; this.setTrait(CommonStrata.user, "timeFilterCoordinates", undefined); } } __decorate([ observable ], TimeFilterMixin.prototype, "_currentTimeFilterFeature", void 0); __decorate([ action ], TimeFilterMixin.prototype, "setTimeFilterFromLocation", null); __decorate([ computed ], TimeFilterMixin.prototype, "canFilterTimeByFeature", null); __decorate([ computed ], TimeFilterMixin.prototype, "imageryUrls", null); __decorate([ computed ], TimeFilterMixin.prototype, "featureTimesAsJulianDates", null); __decorate([ override ], TimeFilterMixin.prototype, "discreteTimesAsSortedJulianDates", null); __decorate([ computed ], TimeFilterMixin.prototype, "timeFilterFeature", null); __decorate([ action ], TimeFilterMixin.prototype, "setTimeFilterFeature", null); __decorate([ action ], TimeFilterMixin.prototype, "removeTimeFilterFeature", null); return TimeFilterMixin; } (function (TimeFilterMixin) { function isMixedInto(model) { return model && model.hasTimeFilterMixin; } TimeFilterMixin.isMixedInto = isMixedInto; })(TimeFilterMixin || (TimeFilterMixin = {})); /** * Picks all the imagery providers at the given coordinates and return a * promise that resolves to the (imageryProvider, imageryFeatures) pairs. */ async function pickImageryProviders(imageryProviders, pickCoordinates, pickPosition) { return filterOutUndefined(await Promise.all(imageryProviders.map((imageryProvider) => imageryProvider .pickFeatures(pickCoordinates.x, pickCoordinates.y, pickCoordinates.level, pickPosition.longitude, pickPosition.latitude) ?.then((imageryFeatures) => ({ imageryProvider, imageryFeatures }))))); } function coordinatesFromTraits(traits) { const { latitude, longitude, height, tile: { x, y, level } } = traits; if (latitude === undefined || longitude === undefined) return; if (x === undefined || y === undefined || level === undefined) return; return { position: { latitude, longitude, height }, tileCoords: { x, y, level } }; } export default TimeFilterMixin; //# sourceMappingURL=TimeFilterMixin.js.map