UNPKG

terriajs

Version:

Geospatial data visualization platform.

564 lines 23.2 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 flatten from "lodash-es/flatten"; import { action, computed, isObservableArray, runInAction, makeObservable, override } from "mobx"; import URI from "urijs"; import filterOutUndefined from "../../../Core/filterOutUndefined"; import isDefined from "../../../Core/isDefined"; import TerriaError, { networkRequestError } from "../../../Core/TerriaError"; import Reproject from "../../../Map/Vector/Reproject"; import CatalogFunctionMixin from "../../../ModelMixins/CatalogFunctionMixin"; import XmlRequestMixin from "../../../ModelMixins/XmlRequestMixin"; import xml2json from "../../../ThirdParty/xml2json"; import WebProcessingServiceCatalogFunctionTraits from "../../../Traits/TraitsClasses/WebProcessingServiceCatalogFunctionTraits"; import CommonStrata from "../../Definition/CommonStrata"; import CreateModel from "../../Definition/CreateModel"; import LoadableStratum from "../../Definition/LoadableStratum"; import StratumOrder from "../../Definition/StratumOrder"; import updateModelFromJson from "../../Definition/updateModelFromJson"; import BooleanParameter from "../../FunctionParameters/BooleanParameter"; import DateParameter from "../../FunctionParameters/DateParameter"; import DateTimeParameter from "../../FunctionParameters/DateTimeParameter"; import EnumerationParameter from "../../FunctionParameters/EnumerationParameter"; import GeoJsonParameter, { isGeoJsonFunctionParameter } from "../../FunctionParameters/GeoJsonParameter"; import LineParameter from "../../FunctionParameters/LineParameter"; import PointParameter from "../../FunctionParameters/PointParameter"; import PolygonParameter from "../../FunctionParameters/PolygonParameter"; import RectangleParameter from "../../FunctionParameters/RectangleParameter"; import RegionParameter from "../../FunctionParameters/RegionParameter"; import RegionTypeParameter from "../../FunctionParameters/RegionTypeParameter"; import StringParameter from "../../FunctionParameters/StringParameter"; import proxyCatalogItemUrl from "../proxyCatalogItemUrl"; import WebProcessingServiceCatalogFunctionJob from "./WebProcessingServiceCatalogFunctionJob"; import NumberParameter from "../../FunctionParameters/NumberParameter"; class WpsLoadableStratum extends LoadableStratum(WebProcessingServiceCatalogFunctionTraits) { item; processDescription; static stratumName = "wpsLoadable"; constructor(item, processDescription) { super(); this.item = item; this.processDescription = processDescription; makeObservable(this); } duplicateLoadableStratum(newModel) { return new WpsLoadableStratum(newModel, this.processDescription); } static async load(item) { if (!isDefined(item.describeProcessUrl)) { return; } const xml = await item.getXml(item.describeProcessUrl); if (!isDefined(xml) || !isDefined(xml.documentElement) || xml.documentElement.localName !== "ProcessDescriptions") { throwInvalidWpsServerError(item, "DescribeProcess"); } const json = xml2json(xml); if (!isDefined(json.ProcessDescription)) { throw networkRequestError({ sender: this, title: i18next.t("models.webProcessingService.processDescriptionErrorTitle"), message: i18next.t("models.webProcessingService.processDescriptionErrorMessage") }); } return new WpsLoadableStratum(item, json.ProcessDescription); } /** * Return the inputs in the processDescription */ get inputs() { if (!isDefined(this.processDescription)) { return []; } const dataInputs = this.processDescription.DataInputs; if (!isDefined(dataInputs) || !isDefined(dataInputs.Input)) { throw networkRequestError({ sender: this, title: i18next.t("models.webProcessingService.processInputErrorTitle"), message: i18next.t("models.webProcessingService.processInputErrorMessage") }); } const inputs = Array.isArray(dataInputs.Input) || isObservableArray(dataInputs.Input) ? dataInputs.Input : [dataInputs.Input]; return inputs; } get storeSupported() { const value = this.processDescription.storeSupported?.toLowerCase(); return value === "true" ? true : value === "false" ? false : undefined; } get statusSupported() { const value = this.processDescription.statusSupported?.toLowerCase(); return value === "true" ? true : value === "false" ? false : undefined; } } __decorate([ computed ], WpsLoadableStratum.prototype, "inputs", null); __decorate([ action ], WpsLoadableStratum, "load", null); StratumOrder.addLoadStratum(WpsLoadableStratum.stratumName); export default class WebProcessingServiceCatalogFunction extends XmlRequestMixin(CatalogFunctionMixin(CreateModel(WebProcessingServiceCatalogFunctionTraits))) { static type = "wps"; constructor(...args) { super(...args); makeObservable(this); } get type() { return WebProcessingServiceCatalogFunction.type; } get typeName() { return "Web Processing Service (WPS)"; } get cacheDuration() { if (isDefined(super.cacheDuration)) { return super.cacheDuration; } return "0d"; } /** * Returns the proxied URL for the DescribeProcess endpoint. */ get describeProcessUrl() { if (!isDefined(this.url) || !isDefined(this.identifier)) { return; } const uri = new URI(this.url).query({ service: "WPS", request: "DescribeProcess", version: "1.0.0", Identifier: this.identifier }); return proxyCatalogItemUrl(this, uri.toString()); } async forceLoadMetadata() { if (!this.strata.has(WpsLoadableStratum.stratumName)) { const stratum = await WpsLoadableStratum.load(this); if (isDefined(stratum)) { runInAction(() => { this.strata.set(WpsLoadableStratum.stratumName, stratum); }); } } } /** * Must be kept alive due to `subtype` observable property of GeoJsonFunctionParameter */ get functionParameters() { const stratum = this.strata.get(WpsLoadableStratum.stratumName); if (!isDefined(stratum)) return []; return stratum.inputs.map((input) => { const parameter = this.convertInputToParameter(this, input); if (isDefined(parameter)) { return parameter; } throw new TerriaError({ sender: this, title: "Unsupported parameter type", message: `The parameter '${input.Identifier}' is not a supported type of parameter.` }); }); } async createJob(id) { const job = new WebProcessingServiceCatalogFunctionJob(id, this.terria); const dataInputs = filterOutUndefined(await Promise.all(this.functionParameters .filter((p) => isDefined(p.value) && p.value !== null) .map((p) => this.convertParameterToInput(p)))); runInAction(() => updateModelFromJson(job, CommonStrata.user, { name: `WPS: ${this.name || this.identifier || this.uniqueId} result ${new Date().toISOString()}`, geojsonFeatures: flatten(this.functionParameters .map((param) => isGeoJsonFunctionParameter(param) ? param.geoJsonFeature : undefined) .filter(isDefined)), url: this.url, identifier: this.identifier, executeWithHttpGet: this.executeWithHttpGet, statusSupported: this.statusSupported, storeSupported: this.storeSupported, wpsParameters: dataInputs, forceConvertResultsToV8: this.forceConvertResultsToV8 })).raiseError(this.terria, "Error ocurred while updating job model JSON"); return job; } convertInputToParameter(catalogFunction, input) { if (!isDefined(input.Identifier)) { return; } const isRequired = isDefined(input.minOccurs) && input.minOccurs > 0; for (let i = 0; i < parameterConverters.length; i++) { const converter = parameterConverters[i]; const parameter = converter.inputToParameter(catalogFunction, input, { id: input.Identifier, name: input.Title, description: input.Abstract, isRequired }); if (isDefined(parameter)) { return parameter; } } } async convertParameterToInput(parameter) { const converter = parameterTypeToConverter(parameter); const result = converter?.parameterToInput(parameter); if (!isDefined(result)) { return; } const inputValue = await Promise.resolve(result.inputValue); if (!isDefined(inputValue)) { return; } return { inputIdentifier: parameter.id, inputValue: inputValue, inputType: result.inputType }; } } __decorate([ override ], WebProcessingServiceCatalogFunction.prototype, "cacheDuration", null); __decorate([ computed ], WebProcessingServiceCatalogFunction.prototype, "describeProcessUrl", null); __decorate([ computed({ keepAlive: true }) ], WebProcessingServiceCatalogFunction.prototype, "functionParameters", null); const LiteralDataConverter = { inputToParameter: function (catalogFunction, input, options) { if (!isDefined(input.LiteralData)) { return; } const allowedValues = input.LiteralData.AllowedValues || input.LiteralData.AllowedValue; if (isDefined(allowedValues) && isDefined(allowedValues.Value)) { return new EnumerationParameter(catalogFunction, { ...options, options: (Array.isArray(allowedValues.Value) || isObservableArray(allowedValues.Value) ? allowedValues.Value : [allowedValues.Value]).map((id) => { return { id }; }) }); } else if (isDefined(allowedValues) && isDefined(allowedValues.Range)) { const np = new NumberParameter(catalogFunction, { ...options }); np.minimum = isDefined(allowedValues.Range.MinimumValue) ? allowedValues.Range.MinimumValue : np.minimum; np.maximum = allowedValues.Range.MaximumValue; np.defaultValue = isDefined(input.LiteralData.DefaultValue) ? input.LiteralData.DefaultValue : np.minimum; return np; } else if (isDefined(input.LiteralData.AnyValue)) { let dtype = null; if (isDefined(input.LiteralData["dataType"])) { dtype = input.LiteralData["dataType"]; } else if (isDefined(input.LiteralData.DataType)) { if (typeof input.LiteralData.DataType === "string") { dtype = input.LiteralData.DataType; } else if (isDefined(input.LiteralData.DataType["ows:reference"])) { dtype = input.LiteralData.DataType["ows:reference"]; } else if (isDefined(input.LiteralData.DataType.text)) { dtype = input.LiteralData.DataType.text; } } if (dtype !== null) { if (dtype.indexOf("http://www.w3.org/TR/xmlschema-2/#") === 0) { dtype = dtype.substring(dtype.lastIndexOf("#") + 1).toLowerCase(); } } if (dtype === "string") { return new StringParameter(catalogFunction, { ...options }); } else if (dtype === "boolean") { return new BooleanParameter(catalogFunction, { ...options }); } else if (dtype === "date") { const dt = new DateParameter(catalogFunction, { ...options, clock: catalogFunction.terria.timelineClock }); dt.variant = "literal"; return dt; } else if (dtype?.toLowerCase() === "datetime") { const dt = new DateTimeParameter(catalogFunction, { ...options, clock: catalogFunction.terria.timelineClock }); dt.variant = "literal"; return dt; } // Assume its a string, if no literal datatype given return new StringParameter(catalogFunction, { ...options }); } }, parameterToInput: function (parameter) { return { inputValue: parameter.value, inputType: "LiteralData" }; } }; const ComplexDateConverter = { inputToParameter: function (catalogFunction, input, options) { if (!isDefined(input.ComplexData) || !isDefined(input.ComplexData.Default) || !isDefined(input.ComplexData.Default.Format) || !isDefined(input.ComplexData.Default.Format.Schema)) { return undefined; } const schema = input.ComplexData.Default.Format.Schema; if (schema !== "http://www.w3.org/TR/xmlschema-2/#date") { return undefined; } const dparam = new DateParameter(catalogFunction, { ...options, clock: catalogFunction.terria.timelineClock }); dparam.variant = "complex"; return dparam; }, parameterToInput: function (parameter) { return { inputType: "ComplexData", inputValue: DateParameter.formatValueForUrl(parameter?.value?.toString() || "") }; } }; const ComplexDateTimeConverter = { inputToParameter: function (catalogFunction, input, options) { if (!isDefined(input.ComplexData) || !isDefined(input.ComplexData.Default) || !isDefined(input.ComplexData.Default.Format) || !isDefined(input.ComplexData.Default.Format.Schema)) { return undefined; } const schema = input.ComplexData.Default.Format.Schema; if (schema !== "http://www.w3.org/TR/xmlschema-2/#dateTime") { return undefined; } const dt = new DateTimeParameter(catalogFunction, { ...options, clock: catalogFunction.terria.timelineClock }); dt.variant = "complex"; return dt; }, parameterToInput: function (parameter) { return { inputType: "ComplexData", inputValue: DateTimeParameter.formatValueForUrl(parameter?.value?.toString() || "") }; } }; export const PointConverter = simpleGeoJsonDataConverter("point", PointParameter); const LineConverter = simpleGeoJsonDataConverter("linestring", LineParameter); const PolygonConverter = simpleGeoJsonDataConverter("polygon", PolygonParameter); const RectangleConverter = { inputToParameter: function (catalogFunction, input, options) { if (!isDefined(input.BoundingBoxData) || !isDefined(input.BoundingBoxData.Default) || !isDefined(input.BoundingBoxData.Default.CRS)) { return undefined; } let code = Reproject.crsStringToCode(input.BoundingBoxData.Default.CRS); let usedCrs = input.BoundingBoxData.Default.CRS; // Find out if Terria's CRS is supported. if (code !== Reproject.TERRIA_CRS && isDefined(input.BoundingBoxData.Supported) && isDefined(input.BoundingBoxData.Supported.CRS)) { for (let i = 0; i < input.BoundingBoxData.Supported.CRS.length; i++) { if (Reproject.crsStringToCode(input.BoundingBoxData.Supported.CRS[i]) === Reproject.TERRIA_CRS) { code = Reproject.TERRIA_CRS; usedCrs = input.BoundingBoxData.Supported.CRS[i]; break; } } } // We are currently only supporting Terria's CRS, because if we reproject we don't know the URI or whether // the bounding box order is lat-long or long-lat. if (!isDefined(code)) { return undefined; } return new RectangleParameter(catalogFunction, { ...options, crs: usedCrs }); }, parameterToInput: function (functionParameter) { const parameter = functionParameter; const value = parameter.value; if (!isDefined(value)) { return; } let bboxMinCoord1, bboxMinCoord2, bboxMaxCoord1, bboxMaxCoord2, urn; // We only support CRS84 and EPSG:4326 if (parameter.crs.indexOf("crs84") !== -1) { // CRS84 uses long, lat rather that lat, long order. bboxMinCoord1 = value.west; bboxMinCoord2 = value.south; bboxMaxCoord1 = value.east; bboxMaxCoord2 = value.north; // Comfortingly known as WGS 84 longitude-latitude according to Table 3 in OGC 07-092r1. urn = "urn:ogc:def:crs:OGC:1.3:CRS84"; } else { // The URN value urn:ogc:def:crs:EPSG:6.6:4326 shall mean the Coordinate Reference System (CRS) with code // 4326 specified in version 6.6 of the EPSG database available at http://www.epsg.org/. That CRS specifies // the axis order as Latitude followed by Longitude. // We don't know about other URN versions, so are going to return 6.6 regardless of what was requested. bboxMinCoord1 = value.south; bboxMinCoord2 = value.west; bboxMaxCoord1 = value.north; bboxMaxCoord2 = value.east; urn = "urn:ogc:def:crs:EPSG:6.6:4326"; } return { inputType: "BoundingBoxData", inputValue: bboxMinCoord1 + "," + bboxMinCoord2 + "," + bboxMaxCoord1 + "," + bboxMaxCoord2 + "," + urn }; } }; const GeoJsonGeometryConverter = { inputToParameter: function (catalogFunction, input, options) { if (!isDefined(input.ComplexData) || !isDefined(input.ComplexData.Default) || !isDefined(input.ComplexData.Default.Format) || !isDefined(input.ComplexData.Default.Format.Schema)) { return; } const schema = input.ComplexData.Default.Format.Schema; if (schema.indexOf("http://geojson.org/geojson-spec.html#") !== 0) { return undefined; } const regionTypeParameter = new RegionTypeParameter(catalogFunction, { id: "regionType", name: "Region Type", description: "The type of region to analyze." }); const regionParameter = new RegionParameter(catalogFunction, { id: "regionParameter", name: "Region Parameter", regionProvider: regionTypeParameter }); return new GeoJsonParameter(catalogFunction, { ...options, regionParameter }); }, parameterToInput: function (parameter) { if (!isDefined(parameter.value) || parameter.value === null) { return; } return parameter.getProcessedValue(parameter.value); } }; function simpleGeoJsonDataConverter(schemaType, klass) { return { inputToParameter: function (catalogFunction, input, options) { if (!isDefined(input.ComplexData) || !isDefined(input.ComplexData.Default) || !isDefined(input.ComplexData.Default.Format) || !isDefined(input.ComplexData.Default.Format.Schema)) { return undefined; } const schema = input.ComplexData.Default.Format.Schema; if (schema.indexOf("http://geojson.org/geojson-spec.html#") !== 0) { return undefined; } if (schema.substring(schema.lastIndexOf("#") + 1) !== schemaType) { return undefined; } return new klass(catalogFunction, options); }, parameterToInput: function (parameter) { return { inputType: "ComplexData", inputValue: klass.formatValueForUrl(parameter.value) }; } }; } function parameterTypeToConverter(parameter) { switch (parameter.type) { case BooleanParameter.type: case StringParameter.type: case EnumerationParameter.type: return LiteralDataConverter; case DateParameter.type: return parameter.variant === "literal" ? LiteralDataConverter : ComplexDateConverter; case DateTimeParameter.type: return parameter.variant === "literal" ? LiteralDataConverter : ComplexDateTimeConverter; case PointParameter.type: return PointConverter; case LineParameter.type: return LineConverter; case PolygonParameter.type: return PolygonConverter; case RectangleParameter.type: return RectangleConverter; case GeoJsonParameter.type: return GeoJsonGeometryConverter; default: break; } } const parameterConverters = [ LiteralDataConverter, ComplexDateConverter, ComplexDateTimeConverter, PointConverter, LineConverter, PolygonConverter, RectangleConverter, GeoJsonGeometryConverter ]; function throwInvalidWpsServerError(wps, endpoint) { throw networkRequestError({ title: i18next.t("models.webProcessingService.invalidWPSServerTitle"), message: i18next.t("models.webProcessingService.invalidWPSServerMessage", { name: wps.name, endpoint }) }); } //# sourceMappingURL=WebProcessingServiceCatalogFunction.js.map