UNPKG

@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
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