@dlr-eoc/utils-ogc
Version:
This library bundles our clients for OGC standards. The long-term-strategy is to make all services in `@dlr-eoc/services-ogc` independent of angular and move them here.
1,106 lines (1,094 loc) • 43.2 kB
JavaScript
import * as xmlserializer from 'xmlserializer';
import { forkJoin, timer, of } from 'rxjs';
import { tap, map, mergeMap, retry, switchMap, share } from 'rxjs/operators';
import * as XLink_1_0_Factory from 'w3c-schemas/lib/XLink_1_0';
import * as OWS_1_1_0_Factory from 'ogc-schemas/lib/OWS_1_1_0';
import * as OWS_2_0_Factory from 'ogc-schemas/lib/OWS_2_0';
import * as WPS_1_0_0_Factory from 'ogc-schemas/lib/WPS_1_0_0';
import * as WPS_2_0_Factory from 'ogc-schemas/lib/WPS_2_0';
import * as JsonixFactory from 'jsonix';
import { WMSCapabilities } from 'ol/format';
class WpsMarshaller100 {
constructor() { }
getCapabilitiesUrl(baseurl) {
return `${baseurl}?service=WPS&request=GetCapabilities&version=1.0.0`;
}
getDescribeProcessUrl(baseurl, processId) {
return `${baseurl}?service=WPS&request=DescribeProcess&version=1.0.0&Identifier=${processId}`;
}
executeUrl(baseurl, processId) {
return `${baseurl}?service=WPS&request=Execute&version=1.0.0&identifier=${processId}`;
}
unmarshalCapabilities(capabilities) {
const out = [];
capabilities.processOfferings.process.forEach(process => {
out.push({
id: process.identifier.value
});
});
return out;
}
unmarshalProcessDescription(processDescriptionJson) {
const description = processDescriptionJson.processDescription[0];
const inputs = [];
for (const dataInput of description.dataInputs.input) {
inputs.push({
description: this.unmarshalInputDescription(dataInput),
value: null
});
}
const outputs = [];
for (const processOutput of description.processOutputs.output) {
outputs.push({
description: this.unmarshalOutputDescription(processOutput),
value: null
});
}
return {
id: description.identifier.value,
processVersion: description.processVersion,
description: description._abstract?.value,
title: description.title.value,
inputs: inputs,
outputs: outputs,
};
}
unmarshalSyncExecuteResponse(responseJson, url, processId, inputs, outputDescriptions) {
const out = [];
if (responseJson.value.status.processFailed) { // Failure?
out.push({
description: {
id: responseJson.value.process.identifier.value,
title: responseJson.value.process.title.value,
reference: true,
type: 'error'
},
value: responseJson.value.statusLocation
});
}
else if (responseJson.value.processOutputs) { // synchronous request?
for (const output of responseJson.value.processOutputs.output) {
const isReference = output.reference ? true : false;
let datatype;
let data;
let format;
if (output.reference) {
datatype = 'complex';
data = output.reference.href || null;
format = output.reference.mimeType;
}
else {
if (output.data && output.data.literalData) {
datatype = 'literal';
format = output.data.literalData.dataType;
}
else if (output.data && output.data.complexData) {
datatype = 'complex';
format = output.data.complexData.mimeType;
}
else {
datatype = 'bbox';
format = undefined;
}
// @ts-ignore
data = this.unmarshalOutputData(output.data);
}
out.push({
description: {
id: output.identifier.value,
title: output.title.value,
format,
reference: isReference,
type: datatype
},
value: data,
});
}
}
else if (responseJson.value.statusLocation) { // asynchronous request?
out.push({
description: {
id: responseJson.value.process.identifier.value,
title: responseJson.value.process.title.value,
reference: true,
type: 'status'
},
value: this.unmarshalGetStateResponse(responseJson, url, processId, inputs, outputDescriptions)
});
}
return out;
}
unmarshalOutputData(data) {
if (data.complexData) {
if (data.complexData.encoding === 'base64') {
if (typeof module !== 'undefined' && module.exports) { // node
data.complexData.content.map(c => Buffer.from(c, 'base64').toString('ascii'));
}
else { // browser
data.complexData.content.map(c => atob(c));
}
}
switch (data.complexData.mimeType) {
case 'application/vnd.geo+json':
case 'application/json':
return data.complexData.content.map(cont => JSON.parse(cont));
case 'application/WMS':
return data.complexData.content;
case 'text/xml':
case 'application/xml':
return xmlserializer.serializeToString(data.complexData.content[0]); // @TODO: better: handle actual xml-data
default:
throw new Error(`Cannot unmarshal data of format ${data.complexData.mimeType}`);
}
}
else if (data.literalData) {
switch (data.literalData.dataType) {
case 'string':
default:
return data.literalData.value;
}
}
throw new Error(`Not yet implemented: ${data}`);
}
unmarshalInputDescription(data) {
if (data.boundingBoxData) {
return {
id: data.identifier.value,
title: data.title.value,
reference: false,
type: 'bbox',
description: data._abstract?.value,
format: 'text/plain',
};
}
else if (data.complexData) {
return {
id: data.identifier.value,
title: data.title.value,
reference: data.storeSupported,
type: 'complex',
description: data._abstract?.value,
format: data.complexData._default.format.mimeType
};
}
else if (data.literalData) {
return {
id: data.identifier.value,
title: data.title.value,
reference: false,
type: 'literal',
description: data._abstract?.value,
defaultValue: data.literalData.defaultValue,
options: data.literalData.allowedValues?.valueOrRange.map(v => v.value),
format: 'text/plain'
};
}
else {
throw new Error(`Cannot unmarshal the input-description for ${data.identifier.value}`);
}
}
unmarshalOutputDescription(data) {
if (data.complexOutput) {
return {
id: data.identifier.value,
title: data.title.value,
reference: true,
type: 'complex',
format: data.complexOutput._default.format.mimeType
};
}
else if (data.boundingBoxOutput) {
return {
id: data.identifier.value,
title: data.title.value,
reference: false,
type: 'bbox',
};
}
else if (data.literalOutput) {
return {
id: data.identifier.value,
title: data.title.value,
reference: false,
type: 'literal',
};
}
else {
throw new Error(`Cannot unmarshal the input-description for ${data.identifier.value}`);
}
}
unmarshalAsyncExecuteResponse(responseJson, url, processId, inputs, outputDescriptions) {
return this.unmarshalGetStateResponse(responseJson, url, processId, inputs, outputDescriptions);
}
unmarshalGetStateResponse(responseJson, serverUrl, processId, inputs, outputDescriptions) {
const response = responseJson.value;
const status = response.status.processSucceeded ? 'Succeeded' :
response.status.processAccepted ? 'Accepted' :
response.status.processStarted ? 'Running' :
response.status.processFailed ? 'Failed' :
'Failed';
const state = {
status,
statusLocation: response.statusLocation,
};
if (response.processOutputs && response.processOutputs.output) {
state.results = responseJson; // this.unmarshalSyncExecuteResponse(responseJson, serverUrl, processId, inputs, outputDescriptions);
}
return state;
}
marshalExecBody(processId, inputs, outputs, async) {
const wps1Inputs = this.marshalInputs(inputs);
const wps1ResponseForm = this.marshalResponseForm(outputs, async);
const bodyValue = {
dataInputs: wps1Inputs,
identifier: processId,
responseForm: wps1ResponseForm,
service: 'WPS',
version: '1.0.0'
};
const body = {
name: {
key: '{http://www.opengis.net/wps/1.0.0}Execute',
localPart: 'Execute',
namespaceURI: 'http://www.opengis.net/wps/1.0.0',
prefix: 'wps',
string: '{http://www.opengis.net/wps/1.0.0}wps:Execute'
},
value: bodyValue
};
return body;
}
marshalResponseForm(outputs, async = false) {
const outputDefinitions = [];
for (const output of outputs) {
let defType;
switch (output.type) {
case 'literal':
defType = {
identifier: { value: output.id },
asReference: output.reference,
mimeType: output.format
};
break;
case 'complex':
defType = {
identifier: { value: output.id },
asReference: output.reference,
mimeType: output.format
};
break;
default:
throw new Error(`This Wps-outputtype has not been implemented yet! ${output} `);
}
outputDefinitions.push(defType);
}
const responseDocument = {
output: outputDefinitions,
status: async ? true : false,
storeExecuteResponse: async ? true : false
};
const form = {
responseDocument
};
return form;
}
marshalInputs(inputArr) {
const theInputs = [];
for (const inp of inputArr) {
if (inp.value === null || inp.value === undefined) {
throw new Error(`Value for input ${inp.description.id} is not set`);
}
const marshalledInput = this.marshalInput(inp);
theInputs.push(marshalledInput);
}
const inputs = {
input: theInputs
};
return inputs;
}
marshalInput(input) {
const id = input.description.id;
const title = input.description.id;
const abstract = '';
const inputType = {
identifier: { value: id },
title: { value: title },
_abstract: { value: abstract }
};
if (input.description.reference) {
inputType.reference = this.marshalReferenceInput(input);
}
else {
inputType.data = this.marshalDataInput(input);
}
return inputType;
}
marshalDataInput(input) {
let data;
switch (input.description.type) {
case 'literal':
data = {
literalData: { value: String(input.value) }
};
break;
case 'bbox':
const values = input.value;
data = {
boundingBoxData: {
lowerCorner: [values.lllat, values.lllon],
upperCorner: [values.urlat, values.urlon]
}
};
break;
case 'complex':
switch (input.description.format) {
case 'text/xml':
data = {
complexData: {
content: [input.value], // @TODO: we assume here that text/xml-data is already stringified
mimeType: input.description.format
}
};
break;
default:
data = {
complexData: {
content: [JSON.stringify(input.value)],
mimeType: input.description.format
}
};
}
break;
default:
throw Error(`This input is of type ${input.description.type}. We can only marshal input of type literal, bbox or complex.`);
}
return data;
}
marshalReferenceInput(input) {
const ref = {
href: input.value,
method: 'GET',
mimeType: input.description.format
};
return ref;
}
marshallGetStatusBody(serverUrl, processId, statusId) {
// WPS-1.0 does not send a body with a GetStatus request.
return {};
}
marshallGetResultBody(serverUrl, processId, jobID) {
// WPS-1.0 does not send a body with a GetStatus request.
return {};
}
dismissUrl(serverUrl, processId, jobId) {
/** this does only work in geoserver:
* return `${serverUrl}?service=WPS&version=1.0.0&request=Dismiss&executionId=${jobId}`;
*/
throw new Error('Wps 1.0 does not support Dismiss-operations.');
}
marshalDismissBody(processId) {
throw new Error('Wps 1.0 does not support Dismiss-operations.');
}
unmarshalDismissResponse(jsonResponse, serverUrl, processId) {
throw new Error('Wps 1.0 does not support Dismiss-operations.');
}
}
const isStatusInfo = (obj) => {
return obj.hasOwnProperty('jobID')
&& obj.hasOwnProperty('status');
};
const isDataOutputType = (obj) => {
return obj.hasOwnProperty('id') &&
(obj.hasOwnProperty('data') || obj.hasOwnProperty('reference') || obj.hasOwnProperty('output'));
};
const isResult = (obj) => {
return (obj.hasOwnProperty('output'));
};
class WpsMarshaller200 {
constructor() { }
getCapabilitiesUrl(baseurl) {
return `${baseurl}?service=WPS&request=GetCapabilities&version=2.0.0`;
}
getDescribeProcessUrl(baseurl, processId) {
return `${baseurl}?service=WPS&request=DescribeProcess&version=2.0.0&Identifier=${processId}`;
}
unmarshalProcessDescription(processDescriptionJson) {
const description = processDescriptionJson.processOffering[0];
const inputs = [];
for (const dataInput of description.process.input) {
inputs.push({
description: this.unmarshalInputDescription(dataInput),
value: null
});
}
const outputs = [];
for (const processOutput of description.process.output) {
outputs.push({
description: this.unmarshalOutputDescription(processOutput),
value: null
});
}
return {
id: description.process.identifier.value,
processVersion: description.processVersion || '',
description: description.process._abstract?.value,
title: description.process.title?.value || '',
inputs: inputs,
outputs: outputs,
};
}
unmarshalInputDescription(dataInput) {
if (dataInput.dataDescription.name.localPart === 'BoundingBoxData') {
return {
id: dataInput.identifier.value,
title: dataInput.title.value,
reference: false,
type: 'bbox',
description: dataInput.abstract?.value
};
}
else if (dataInput.dataDescription.name.localPart === 'LiteralData') {
return {
id: dataInput.identifier.value,
title: dataInput.title.value,
reference: false,
type: 'literal',
defaultValue: dataInput.dataDescription.value.literalDataDomain?.defaultValue?.value,
options: dataInput.dataDescription.value.literalDataDomain?.allowedValues.valueOrRange.map(v => v.value),
description: dataInput.abstract?.value
};
}
else if (dataInput.dataDescription.name.localPart === 'ComplexData') {
return {
id: dataInput.identifier.value,
title: dataInput.title.value,
reference: true,
type: 'complex',
format: dataInput.dataDescription.value.format[0].mimeType,
description: dataInput.abstract?.value
};
}
else {
throw new Error(`Cannot unmarshal input-description ${dataInput.identifier.value}`);
}
}
unmarshalOutputDescription(processOutput) {
if (processOutput.dataDescription.name.localPart === 'BoundingBoxData') {
return {
id: processOutput.identifier.value,
title: processOutput.title.value,
reference: false,
type: 'bbox',
description: processOutput.abstract?.value
};
}
else if (processOutput.dataDescription.name.localPart === 'LiteralData') {
return {
id: processOutput.identifier.value,
title: processOutput.title.value,
reference: false,
type: 'literal',
description: processOutput.abstract?.value
};
}
else if (processOutput.dataDescription.name.localPart === 'ComplexData') {
return {
id: processOutput.identifier.value,
title: processOutput.title.value,
reference: true,
type: 'complex',
format: processOutput.dataDescription.value.format[0].mimeType,
description: processOutput.abstract?.value
};
}
else {
throw new Error(`Cannot unmarshal input-description ${processOutput.identifier.value}`);
}
}
executeUrl(baseurl, processId) {
// return `${baseurl}?service=WPS&request=Execute&version=2.0.0&identifier=${processId}`;
return baseurl;
}
unmarshalCapabilities(capabilities) {
const out = [];
capabilities.contents.processSummary.forEach(summary => {
out.push({
id: summary.identifier.value
});
});
return out;
}
unmarshalSyncExecuteResponse(responseJson, url, processId, inputs, outputDescriptions) {
const out = [];
if (isResult(responseJson.value)) {
for (const output of responseJson.value.output) {
const outputDescription = outputDescriptions.find(od => od.id === output.id);
if (!outputDescription) {
throw new Error(`Could not find an output-description for the parameter ${output.id}.`);
}
const isReference = outputDescription.reference;
const datatype = outputDescription.type;
const format = outputDescription.format;
let data;
if (output.reference) {
data = output.reference.href || null;
}
else if (output.data) {
data = this.unmarshalOutputData(output.data, outputDescription);
}
else {
throw new Error(`Output has neither reference nor data field.`);
}
out.push({
description: {
id: output.id,
title: outputDescription.title,
format,
reference: isReference,
type: datatype
},
value: data,
});
}
}
else if (isStatusInfo(responseJson.value)) {
const state = {
status: responseJson.value.status,
jobID: responseJson.value.jobID,
percentCompleted: responseJson.value.percentCompleted
};
out.push({
description: {
id: processId,
title: '',
reference: true,
type: 'status'
},
value: state
});
}
return out;
}
unmarshalOutputData(data, description) {
if (description.type === 'complex') {
if (data.encoding === 'base64') {
if (typeof module !== 'undefined' && module.exports) { // node
data.content.map(c => Buffer.from(c, 'base64').toString('ascii'));
}
else { // browser
data.content.map((c) => atob(c));
}
}
switch (data.mimeType) {
case 'application/vnd.geo+json':
case 'application/json':
return data.content.map((cont) => JSON.parse(cont));
case 'application/WMS':
return data.content;
case 'text/xml':
case 'application/xml':
return xmlserializer.serializeToString(data.content[0]); // @TODO: better: handle actual xml-data
default:
throw new Error(`Cannot unmarshal complex data of format ${data.mimeType}`);
}
}
else if (description.type === 'literal') {
return data.content;
}
throw new Error(`Not yet implemented: ${data}`);
}
unmarshalAsyncExecuteResponse(responseJson, url, processId, inputs, outputDescriptions) {
return this.unmarshalGetStateResponse(responseJson, url, processId, inputs, outputDescriptions);
}
unmarshalGetStateResponse(responseJson, serverUrl, processId, inputs, outputDescriptions) {
if (isStatusInfo(responseJson.value)) {
const state = {
status: responseJson.value.status,
jobID: responseJson.value.jobID,
percentCompleted: responseJson.value.percentCompleted
};
return state;
}
else {
throw new Error(`Not a status-info: ${responseJson}`);
}
}
marshalExecBody(processId, inputs, outputs, async) {
const inputsMarshalled = this.marshalInputs(inputs);
const outputsMarshalled = this.marshalOutputs(outputs);
const bodyValue = {
TYPE_NAME: 'WPS_2_0.ExecuteRequestType',
service: 'WPS',
version: '2.0.0',
identifier: { value: processId },
input: inputsMarshalled,
output: outputsMarshalled,
mode: async ? 'async' : 'sync',
response: 'document'
};
const body = {
name: {
key: '{http://www.opengis.net/wps/2.0}Execute',
localPart: 'Execute',
namespaceURI: 'http://www.opengis.net/wps/2.0',
prefix: 'wps',
string: '{http://www.opengis.net/wps/2.0}wps:Execute'
},
value: bodyValue
};
return body;
}
marshalInputs(inputs) {
return inputs.map(i => {
if (i.description.reference) {
return {
id: i.description.id,
reference: {
href: i.value,
mimeType: i.description.format,
schema: i.description.schema,
encoding: i.description.encoding || "UTF-8"
}
};
}
else {
return {
id: i.description.id,
data: {
content: this.marshalInput(i),
mimeType: i.description.format,
// schema: i.description.schema,
// encoding: i.description.encoding || "UTF-8"
}
};
}
});
}
marshalInput(i) {
if (i.description.type === 'literal') {
return [i.value];
}
else {
return [JSON.stringify(i.value)];
}
}
marshalOutputs(outputs) {
return outputs.map(o => {
return {
id: o.id,
mimeType: o.format,
transmission: o.reference ? 'reference' : 'value' // @TODO: maybe just comment out this line?
};
});
}
marshallGetStatusBody(serverUrl, processId, statusId) {
const request = {
name: {
key: '{http://www.opengis.net/wps/2.0}GetStatus',
localPart: 'GetStatus',
namespaceURI: 'http://www.opengis.net/wps/2.0',
prefix: 'wps',
string: '{http://www.opengis.net/wps/2.0}wps:GetStatus'
},
value: {
jobID: statusId,
service: 'WPS',
version: '2.0.0'
}
};
return request;
}
marshallGetResultBody(serverUrl, processId, jobID) {
const request = {
name: {
key: '{http://www.opengis.net/wps/2.0}GetResult',
localPart: 'GetResult',
namespaceURI: 'http://www.opengis.net/wps/2.0',
prefix: 'wps',
string: '{http://www.opengis.net/wps/2.0}wps:GetResult'
},
value: {
service: 'WPS',
version: '2.0.0',
jobID
}
};
return request;
}
dismissUrl(serverUrl, processId, jobId) {
return serverUrl;
}
marshalDismissBody(jobId) {
const body = {
name: {
key: '{http://www.opengis.net/wps/2.0}Dismiss',
localPart: 'Dismiss',
namespaceURI: 'http://www.opengis.net/wps/2.0',
prefix: 'wps',
string: '{http://www.opengis.net/wps/2.0}wps:Dismiss'
},
value: {
jobID: jobId,
service: 'WPS',
version: '2.0.0'
}
};
return body;
}
unmarshalDismissResponse(jsonResponse, serverUrl, processId) {
const state = {
status: jsonResponse.value.status,
jobID: jsonResponse.value.jobID
};
return state;
}
}
function pollUntil(task$, predicate, doWhile, minWaitTime = 1000) {
if (doWhile) {
doWhile(null);
}
const tappedTask$ = task$.pipe(tap((r) => {
if (doWhile) {
doWhile(r);
}
}));
const requestTakesAtLeast$ = forkJoin([tappedTask$, timer(minWaitTime)]).pipe(map(r => r[0]));
const polledRequest$ = requestTakesAtLeast$.pipe(mergeMap((response) => {
if (predicate(response)) {
// console.log(`obtained correct answer ${response}`);
return of(response);
}
else {
// console.log(`obtained false answer ${response}. trying again...`);
return polledRequest$;
}
}));
return polledRequest$;
}
function delayedRetry(delayMs, maxRetries = 3) {
return (src$) => {
return src$.pipe(
// If an error occurs ...
retry({
count: maxRetries, delay: (error, retryCount) => {
if (error.status && error.status === 400) {
// In case of a server error, repeating won't help.
throw error;
}
else if (retryCount <= maxRetries) {
console.log('http-error. Retrying ...');
return timer(delayMs); // Adding a timer from RxJS to return observable to delay param.
}
else {
console.log(`Persistent http-errors after ${retryCount} attempts. Giving up.`);
throw error; // an error causes request to be given up on.
}
}
}));
};
}
class FakeCache {
set(input, output) {
return of(true);
}
get(input) {
return of(null);
}
}
const XLink_1_0 = XLink_1_0_Factory.XLink_1_0;
const OWS_1_1_0 = OWS_1_1_0_Factory.OWS_1_1_0;
const OWS_2_0 = OWS_2_0_Factory.OWS_2_0;
const WPS_1_0_0 = WPS_1_0_0_Factory.WPS_1_0_0;
const WPS_2_0 = WPS_2_0_Factory.WPS_2_0;
const Jsonix = JsonixFactory.Jsonix;
/**
* The Wps-client abstracts away the differences between Wps1.0.0 and Wps2.0.0
* There are two layers of marshalling:
* - the Wps-marshaller marshals user-facing data to wps-specific types
* - Jsonix marshals wps-specific data to xml.
* user-facing data -> wpsmarshaller -> Wps-type-specific data -> Jsonix-marhsaller -> XML ->
* -> webclient -> WPS -> XML -> Jsonix-unmarshaller -> Wps-type-specific data -> wpsmarshaller -> user-facing data
*/
class WpsClient {
constructor(version = '1.0.0', webclient, cache) {
this.webclient = webclient;
this.cache = new FakeCache();
this.version = version;
if (cache)
this.cache = cache;
let context;
if (this.version === '1.0.0') {
this.wpsmarshaller = new WpsMarshaller100();
context = new Jsonix.Context([XLink_1_0, OWS_1_1_0, WPS_1_0_0]);
}
else if (this.version === '2.0.0') {
this.wpsmarshaller = new WpsMarshaller200();
context = new Jsonix.Context([XLink_1_0, OWS_2_0, WPS_2_0]);
}
else {
throw new Error('You entered a WPS version other than 1.0.0 or 2.0.0.');
}
this.xmlunmarshaller = context.createUnmarshaller();
this.xmlmarshaller = context.createMarshaller();
}
getCapabilities(url) {
const getCapabilitiesUrl = this.wpsmarshaller.getCapabilitiesUrl(url);
return this.getRaw(getCapabilitiesUrl).pipe(map((response) => {
const responseJson = this.xmlunmarshaller.unmarshalString(response);
return this.wpsmarshaller.unmarshalCapabilities(responseJson.value);
}) // @TODO: handle case when instead of WpsCapabilites an ExceptionReport is returned
);
}
describeProcess(url, processId) {
const describeProcessUrl = this.wpsmarshaller.getDescribeProcessUrl(url, processId);
return this.getRaw(describeProcessUrl).pipe(map((response) => {
const responseJson = this.xmlunmarshaller.unmarshalString(response);
return this.wpsmarshaller.unmarshalProcessDescription(responseJson.value);
}));
}
executeAsync(url, processId, inputs, outputs, pollingRate = 1000, tapFunction, unmarshalFunction) {
const executeRequest$ = this.executeAsyncBasic(url, processId, inputs, outputs);
const query$ = executeRequest$.pipe(
// poll until succeeded
mergeMap((currentState) => {
const nextState$ = this.getNextState(currentState, url, processId, inputs, outputs);
const poll$ = pollUntil(nextState$, (response) => {
if (response.status === 'Failed') {
throw new Error(`Error during execution of process ${processId}: ` + response.statusLocation);
}
return response.status === 'Succeeded';
}, tapFunction, pollingRate);
return poll$;
}),
// fetch results
mergeMap((lastState) => {
return this.fetchResults(lastState, url, processId, inputs, outputs, unmarshalFunction);
}),
// In case of errors:
tap((response) => {
for (const result of response) {
if (result.description.type === 'error') {
console.log('server responded with 200, but body contained an error-result: ', result);
throw new Error(result.value);
}
}
}));
return this.cachedQuery(url, processId, inputs, outputs, query$);
}
cachedQuery(url, processId, inputs, outputs, query$) {
const cachedResponse$ = this.cache.get({ url, processId, inputs, outputs });
return cachedResponse$.pipe(switchMap((results) => {
if (results) {
return of(results);
}
else {
return query$.pipe(tap((response) => {
this.cache.set({ url, processId, inputs, outputs }, response).subscribe(success => {
console.log('set data in cache', success);
});
}));
}
}));
}
getNextState(currentState, serverUrl, processId, inputs, outputDescriptions) {
let request$;
if (this.version === '1.0.0') {
if (!currentState.statusLocation) {
throw Error('No status location');
}
request$ = this.getRaw(currentState.statusLocation);
}
else if (this.version === '2.0.0') {
if (!currentState.jobID) {
throw Error('No job-Id');
}
const execbody = this.wpsmarshaller.marshallGetStatusBody(serverUrl, processId, currentState.jobID);
const xmlExecbody = this.xmlmarshaller.marshalString(execbody);
request$ = this.postRaw(serverUrl, xmlExecbody);
}
else {
throw new Error(`'GetStatus' has not yet been implemented for this WPS-Version (${this.version}).`);
}
const request1$ = request$.pipe(delayedRetry(2000, 2), map((xmlResponse) => {
const jsonResponse = this.xmlunmarshaller.unmarshalString(xmlResponse);
const output = this.wpsmarshaller.unmarshalGetStateResponse(jsonResponse, serverUrl, processId, inputs, outputDescriptions);
return output;
}));
return request1$;
}
fetchResults(lastState, serverUrl, processId, inputs, outputDescriptions, unmarshalFunction) {
if (lastState.results) { // WPS 1.0: results should already be in last state
let output;
if (unmarshalFunction) {
output = unmarshalFunction(lastState.results);
}
else {
output = this.wpsmarshaller.unmarshalSyncExecuteResponse(lastState.results, serverUrl, processId, inputs, outputDescriptions);
}
return of(output);
}
else { // WPS 2.0: get results with post request
if (!lastState.jobID) {
throw new Error(`You want me to get a result, but I can't find a jobId. I don't know what to do now!`);
}
const execBody = this.wpsmarshaller.marshallGetResultBody(serverUrl, processId, lastState.jobID);
const xmlExecBody = this.xmlmarshaller.marshalString(execBody);
return this.postRaw(serverUrl, xmlExecBody).pipe(map((xmlResponse) => {
const jsonResponse = this.xmlunmarshaller.unmarshalString(xmlResponse);
let output;
if (unmarshalFunction) {
output = unmarshalFunction(jsonResponse);
}
else {
output = this.wpsmarshaller.unmarshalSyncExecuteResponse(jsonResponse, serverUrl, processId, inputs, outputDescriptions);
}
return output;
}));
}
}
executeAsyncBasic(url, processId, inputs, outputDescriptions) {
const executeUrl = this.wpsmarshaller.executeUrl(url, processId);
const execbody = this.wpsmarshaller.marshalExecBody(processId, inputs, outputDescriptions, true);
const xmlExecbody = this.xmlmarshaller.marshalString(execbody);
return this.postRaw(executeUrl, xmlExecbody).pipe(map((xmlResponse) => {
const jsonResponse = this.xmlunmarshaller.unmarshalString(xmlResponse);
const output = this.wpsmarshaller.unmarshalAsyncExecuteResponse(jsonResponse, url, processId, inputs, outputDescriptions);
return output;
}));
}
execute(url, processId, inputs, outputDescriptions, unmarshalFunction) {
const executeUrl = this.wpsmarshaller.executeUrl(url, processId);
const execbody = this.wpsmarshaller.marshalExecBody(processId, inputs, outputDescriptions, false);
const xmlExecbody = this.xmlmarshaller.marshalString(execbody);
return this.postRaw(executeUrl, xmlExecbody).pipe(map((xmlResponse) => {
const jsonResponse = this.xmlunmarshaller.unmarshalString(xmlResponse);
if (unmarshalFunction) {
return unmarshalFunction(jsonResponse);
}
const output = this.wpsmarshaller.unmarshalSyncExecuteResponse(jsonResponse, url, processId, inputs, outputDescriptions);
return output;
}));
}
dismiss(serverUrl, processId, jobId) {
const dismissUrl = this.wpsmarshaller.dismissUrl(serverUrl, processId, jobId);
const dismissBody = this.wpsmarshaller.marshalDismissBody(jobId);
const xmlDismissBody = this.xmlmarshaller.marshalString(dismissBody);
return this.postRaw(dismissUrl, xmlDismissBody).pipe(map((xmlResponse) => {
const jsonResponse = this.xmlunmarshaller.unmarshalString(xmlResponse);
const output = this.wpsmarshaller.unmarshalDismissResponse(jsonResponse, serverUrl, processId);
return output;
}));
}
postRaw(url, xmlBody) {
const paras = {
headers: {
'Content-Type': 'text/xml',
'Accept': 'text/xml, application/xml'
},
responseType: 'text'
};
return this.webclient.post(url, xmlBody, paras).pipe(delayedRetry(2000, 2), tap(r => { this.parseResponseForErrors(url, r); }), share() // turning hot: to make sure that multiple subscribers dont cause multiple requests
);
}
getRaw(url) {
const paras = {
headers: {
'Accept': 'text/xml, application/xml'
},
responseType: 'text'
};
return this.webclient.get(url, paras).pipe(delayedRetry(2000, 2), tap(r => { this.parseResponseForErrors(url, r); }));
}
parseResponseForErrors(url, response) {
if (response.match('<title>404 Not Found</title>') || response.match('ows:ExceptionReport')) {
throw new Error(`From ${url}: ` + response);
}
}
/**
* Use this method if you want to set the caching mechanism on a client-level
* (as opposed to on a app-wide level)
* @param cache : Cache (@dlr-eoc/services-ogc)
*/
setCache(cache) {
this.cache = cache;
}
}
const isBbox = (obj) => {
return (obj.hasOwnProperty('crs') &&
obj.hasOwnProperty('lllon') &&
obj.hasOwnProperty('lllat') &&
obj.hasOwnProperty('urlon') &&
obj.hasOwnProperty('urlat'));
};
function isWpsState(obj) {
return obj && obj.hasOwnProperty('status') && (obj.hasOwnProperty('jobID') || obj.hasOwnProperty('statusLocation'));
}
class WmsClient {
constructor(http) {
this.http = http;
this.parser = new WMSCapabilities();
}
getCapabilities(url, version = '1.1.0') {
const getCapabilitiesUrl = `${url}?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=${version}`;
return this.http.get(getCapabilitiesUrl, { headers: {
'Content-Type': 'text/xml',
'Accept': 'text/xml, application/xml'
}, responseType: 'text' }).pipe(map(response => {
return this.parser.read(response);
}));
}
/**
* Searches capabilities-document for a layer with given name
* @param name
* @param capabilities
*/
getLayerFromCapabilities(name, capabilities) {
/** http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd - The Layer Element */
const rootLayer = capabilities.Capability.Layer;
return this.getLayerFromCapabilitiesLayer(name, rootLayer);
}
/**
* @param name
* @param layer http://schemas.opengis.net/wms/1.3.0/capabilities_1_3_0.xsd - The Layer Element
* @returns any
*/
getLayerFromCapabilitiesLayer(name, layer) {
const rootLayer = layer;
if (rootLayer) {
if (rootLayer.Name && rootLayer.Name === name) {
return rootLayer;
}
else {
if (rootLayer.Layer && Array.isArray(rootLayer.Layer)) {
// find layer from array... recursive
return rootLayer.Layer.find(item => {
return this.getLayerFromCapabilitiesLayer(name, item);
});
}
else if (rootLayer.Layer && rootLayer.Layer.Name && rootLayer.Layer.Name === name) {
return rootLayer.Layer;
}
}
}
else {
return null;
}
}
/**
*
* @param layer
* @param wmsVersion
* @returns
*/
getTimeDimensionFromLayer(layer) {
return layer.Dimension?.find(d => d.name === 'time');
}
}
/*
* Public API Surface of utils-ogc
*/
/**
* Generated bundle index. Do not edit.
*/
export { FakeCache, WmsClient, WpsClient, delayedRetry, isBbox, isWpsState, pollUntil };
//# sourceMappingURL=dlr-eoc-utils-ogc.mjs.map