Working with Web Processing Service with Python and OWSLib

In this notebook, you will interact with a Web Processing Service (WPS) server using the OWSLib library, a client for OGC Web Services. More specifically, you’ll learn how to list available processes, get information about a process, and execute a process. If you need more information than provided here, refer to the OWSLib documentation.

Connect to server and list processes

Here is how to establish a connection to a WPS server and list the available processes. We first connect to the server, then send a getCapabilities request to the server, which answers with a list of all the processes it hosts. This allows the client to populate its processes attribute.

from owslib.wps import WebProcessingService

# Connect to the server
wps = WebProcessingService('https://pavics.ouranos.ca/twitcher/ows/proxy/finch')

print(wps.identification.title)

# Send a `getCapabilities` request to populate the list of processes
wps.getcapabilities()

# Print out the identifiers and abstract of five processes
for process in wps.processes[:5]:
    print(f"{process.identifier} \t : {process.abstract} \n")
Finch
humidex 	 : The humidex indicates how hot the air feels to an average person, accounting for the effect of humidity. It can be loosely interpreted as the equivalent perceived temperature when the air is dry. 

heat_index 	 : Perceived temperature after relative humidity is taken into account ([Blazejczyk2012]_). The index is only valid for temperatures above 20degC. 

tg 	 : We assume a symmetrical distribution for the temperature and retrieve the average value as Tg = (Tx + Tn) / 2 

wind_speed_from_vector 	 : Computes the magnitude and angle of the wind vector from its northward and eastward components, following the meteorological convention that sets calm wind to a direction of 0deg and northerly wind to 360deg. 

wind_vector_from_speed 	 : Compute the eastward and northward wind components from the wind speed and direction. 

Get information about a single process

Assume we’re interested in using the humidex process, we first need to know which arguments this process expects. To get this information, we must first send a describeProcess request to the server for the process description.

process = wps.describeprocess('humidex')
print(process.title, " : ", process.abstract)
print ([x.identifier for x in process.dataInputs])
Humidex index.  :  The humidex indicates how hot the air feels to an average person, accounting for the effect of humidity. It can be loosely interpreted as the equivalent perceived temperature when the air is dry.
['tas', 'tdps', 'hurs', 'check_missing', 'missing_options', 'cf_compliance', 'data_validation', 'variable', 'output_name', 'output_format', 'csv_precision']

Each process has a list of dataInput objects, each with an individual title and abstract, a list of supported data types, constraints on the minimum and maximum of values it can take, and optionally a default value. For example, the tas argument is mandatory (minOccurs is set to 1).

tas = process.dataInputs[0]
print(tas.abstract)
print(tas.minOccurs)
NetCDF Files or archive (tar/zip) containing netCDF files. Mean surface temperature.
1

Similarly, each process’ has a list of processOutput object with descriptive fields, in addition to methods to retrieve the actual data from the server once the process has been executed.

from owslib.wps import ComplexDataInput

url = "https://pavics.ouranos.ca/twitcher/ows/proxy/thredds/fileServer/birdhouse/testdata/xclim/cmip5/tas_Amon_CanESM2_rcp85_r1i1p1_200701-200712.nc"

exec = wps.execute("tg_mean", inputs=[
         ("tas", ComplexDataInput(url)),
         ("data_validation", "log")],
         mode="sync")

print(exec.status)
ProcessSucceeded

Retrieving the execution results

In synchronous mode, once the execution has completed the client automatically fetches the response. The response is an XML document that either stores the results, or links to the results, depending on how the process and server are configured.

from lxml import etree

print(etree.tostring(exec.response).decode())
<wps:ExecuteResponse xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 ../wpsExecute_response.xsd" service="WPS" version="1.0.0" xml:lang="en-US" serviceInstance="http://localhost:5000/wps?request=GetCapabilities&amp;amp;service=WPS" statusLocation="">
    <wps:Process wps:processVersion="0.1">
        <ows:Identifier>tg_mean</ows:Identifier>
        <ows:Title>Mean of daily average temperature.</ows:Title>
        <ows:Abstract>Resample the original daily mean temperature series by taking the mean over each period.</ows:Abstract>
	</wps:Process>
    <wps:Status creationTime="2024-05-09T17:41:11Z">
        <wps:ProcessSucceeded>PyWPS Process Mean of daily average temperature. finished</wps:ProcessSucceeded>
	</wps:Status>
	<wps:ProcessOutputs>
		<wps:Output>
            <ows:Identifier>output</ows:Identifier>
            <ows:Title>Result</ows:Title>
            <ows:Abstract>The format depends on the 'output_format' input parameter.</ows:Abstract>
            <wps:Reference href="https://pavics.ouranos.ca/wpsoutputs/finch/51efed92-0e2b-11ef-b200-0242ac13000a/out.nc" mimeType="application/x-netcdf" encoding="base64" schema=""/>
		</wps:Output>
		<wps:Output>
            <ows:Identifier>output_log</ows:Identifier>
            <ows:Title>Logging information</ows:Title>
            <ows:Abstract>Collected logs during process run.</ows:Abstract>
            <wps:Reference href="https://pavics.ouranos.ca/wpsoutputs/finch/51efed92-0e2b-11ef-b200-0242ac13000a/log.txt" mimeType="text/plain" encoding="" schema=""/>
		</wps:Output>
		<wps:Output>
            <ows:Identifier>ref</ows:Identifier>
            <ows:Title>Link to all output files</ows:Title>
            <ows:Abstract>Metalink file storing all references to output files.</ows:Abstract>
			<wps:Data>
                    <wps:ComplexData mimeType="application/metalink+xml; version=4.0" encoding="" schema="metalink/4.0/metalink4.xsd">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;metalink xmlns="urn:ietf:params:xml:ns:metalink"&gt;
    &lt;published&gt;2024-05-09T17:41:11Z&lt;/published&gt;
    &lt;generator&gt;PyWPS/4.5.2&lt;/generator&gt;

    &lt;file name="out.nc"&gt;
        &lt;identity&gt;out&lt;/identity&gt;
        &lt;size&gt;91126&lt;/size&gt;
        &lt;metaurl mediatype="application/x-netcdf"&gt;https://pavics.ouranos.ca/wpsoutputs/finch/535f33ae-0e2b-11ef-b200-0242ac13000a/out.nc&lt;/metaurl&gt;
        &lt;publisher name="Finch" url="http://localhost:5000/wps"/&gt;
    &lt;/file&gt;

&lt;/metalink&gt;</wps:ComplexData>
			</wps:Data>
		</wps:Output>
	</wps:ProcessOutputs>
</wps:ExecuteResponse>

In the case of the tg_mean process, the results are stored in a netCDF file, which is returned as a ComplexDataOutput object. It is stored on the server, so the response only contains the URL to this output file.

out = exec.processOutputs[0]

# The link to the output stored on the server
print(out.reference)
https://pavics.ouranos.ca/wpsoutputs/finch/51efed92-0e2b-11ef-b200-0242ac13000a/out.nc

You may retrieve the data directly with out.retrieveData(), in the case above you’ll get the bytes of the netCDF result file, or save the results to a file on disk using exec.getOutput(out.identifier, filepath="path/to/file").