PyWPS 3.2.7 Documentation

Documentation for PyWPS. PyWPS is implementation of OGC Web Processing Service (WPS), version 1.0.0.

PyWPS is written in Python. It is possible to run it as CGI, Mod_python environemnt, as well as Java servlet via jython.

PyWPS should be between server-side application and WPS clients. The application can be written with Python, Java or executabed from the command line. PyWPS was written with direct support for GRASS GIS.

Contents:

Installation of PyWPS

Prerequirements

  • python – currently 2.5, 2.6 are supported, 2.4 is deprecated
  • python-xml

Installation the quick ‘n’ dirty way

For installing PyWPS to your server quickly, simply unpack the archive to some directory. You can also use current repository version.:

1 - cd to target directory:

$ cd /usr/local/

2 - unpack pywps:

$ tar xvzf /tmp/pywps-VERSION.tar.gz

Post-installation steps

You have to change the write access of pywps/Templates/*WPS_VERSION*/ directory, so the web server can compile the templates:

chmod 777 /usr/local/pywps-VERSION/pywps/Templates/1_0_0

Installation the ‘clean’ way

Unpack the package

$ tar -xzf pywps-VERSION.tar.gz

and run

$ python setup.py install

Installation using prebuild distribution packages

PyWPS provides packages for Debian and RPM based Linux Distributions.

Note

The packages are not maintained properly and until we don’t find packagers, we recommend to use any other approach, described earlier in this section.

Testing the installation

If PyWPS has been successfully installed, you can test this with running it without any input or further configuration.

First you need to find the cgiwps.py script, which is in the root of pywps installation directory (if you used the Quick and dirty way), or it should be located in /usr/bin directory, if you used the clean way of installation.

Run cgiwps.py on the command line:

$ ./cgiwps.py

And you should get result like this (which is a mixture of standard output and standard error):

PyWPS NoApplicableCode: Locator: None; Value: No query string found.
Content-type: text/xml

<?xml version="1.0" encoding="utf-8"?>
<ExceptionReport version="1.0.0" xmlns="http://www.opengis.net/ows" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <Exception exceptionCode="NoApplicableCode">
                <ExceptionText>
                        No query string found.
                </ExceptionText>
        </Exception>
</ExceptionReport>

In this case, you have installed PyWPS correctly and you are ready to proceed to configuration.

Configuration

Note

Before you start to tune your PyWPS installation, you can download OpenGIS(R) Web Processing Service document (OGC 05-007r7) version 1.0.0 http://www.opengeospatial.org/standards/wps or later, for reference.

Setting up the PyWPS instance

PyWPS can be installed once on your server, but it be configured for many WPS servers (instances). Each WPS server needs a set of processes (stored in one directory) and a configuration file. Processes are stored together as python programs in one directory as follows:

1 - create processes directory – directory, where you store all

processes for particular PyWPS instance:

$ mkdir -p /usr/local/wps/processes
2 - copy template of the configuration file to some location, and

configure your PyWPS installation (see below):

$ cp pywps-VERSION/pywps/default.cfg /usr/local/wps/pywps.cfg
$ $EDITOR /usr/locap/wps/pywps.cfg
3 - create any process(es) in the processes directory. You can start with

the example processes, stored in pywps-VERSION/examples/processes directory. See how-to-write-custom-process for how to write custom processes.:

$ cp pywps-VERSION/examples/ultimatequestionprocess.py /usr/local/wps/processes/
6 - Each process in the processes directory must be

registered in the __init__.py file. The file has to contain at least:

__all__ = ["ultimatequestionprocess"]

Where __all__ represents list of processes (file names) within the processes directory.

Accepted environment variables

The following environment variables are accepted by a PyWPS instance:

PYWPS_CFG
Configuration file location
PYWPS_PROCESSES
Directory, where the processes are stored
PYWPS_TEMPLATES
Templates directory (structure should be similar to file:pywps/Templates)

Setting up the Web Server

PyWPS can run as CGI application or in mod_python mode. CGI is easier to setup, where mod_python is less demanding on server resources, since after the first run, PyWPS and Python itself are loaded into memory.

PyWPS as CGI

CGI configuration is a simple appraoch, without any additional server configuration.

To configure PyWPS via CGI, copy the PyWPS CGI wrapper script to your cgi-bin directory and edit the variables:

$ cp pywps/resources/pywps.cgi /usr/lib/cgi-bin
$ $EDITOR /usr/lib/cgi-bin/pywps.cgi

Note

Windows users must create a either a .bat file or Python wrapper. This example is written as UNIX shell script.

Note

This script is to be used only via HTTP (with e.g. Apache). If you want to run PyWPS from the command line, use wps.py directly.

Below is a sample wrapper:

#!/bin/sh

# Author: Jachym Cepicky
# Purpose: CGI script for wrapping PyWPS script
# Licence: GNU/GPL
# Usage: Put this script to your web server cgi-bin directory, e.g.
# /usr/lib/cgi-bin/ and make it executable (chmod 755 pywps.cgi)

# NOTE: tested on linux/apache

export PYWPS_CFG=/usr/local/wps/pywps.cfg
export PYWPS_PROCESSES=/usr/local/wps/processes/

/usr/local/pywps-VERSION/cgiwps.py

You can also configure HTTP environment variables using standard Apache server configuration file (see mod_python) for example.

PyWPS in mod_python

Overall, PyWPS has better performance via mod_python. All necessary libraries are pre-loaded into memory and response times should be faster in some cases.

1 - Install necessary packages, on debian, it is libapache2-mod-python 2 - Configure Apache HTTP server (see Mod Python documentation).

1 - Create python directory (preferably outside htdocs directory):

$ mkdir /var/www/wps/

2 - Add this to your HTTP configuration file:

<Directory /var/www/wps>
    SetEnv PYWPS_PROCESSES /usr/local/wps/processes
    SetEnv PYWPS_CFG /usr/local/wps/pywps.cfg
    SetHandler python-program
    PythonHandler pywps
    PythonDebug On
    PythonPath "sys.path+['/usr/local/pywps-VERSION/']"
    PythonAutoReload On
</Directory>

or you can copy resources/.htaccess to /var/www/wps – depending on what level of access you are provided by your system administrator.

3 - Copy resources/pywps.py to /var/www/wps

PyWPS configuration files

Configuration file for PyWPS can be located in several places. There are global and local PyWPS configuration files. Local configurations override global configurations.

Global PyWPS configuration files

  1. File /etc/pywps.cfg (on Linux/Unix)
  2. File /usr/local/pywps-VERSION/etc/pywps.cfg, which means the file pywps.cfg in directory etc, located in PyWPS install location.

And one special file:

File /usr/local/pywps-VERSION/pywps/default.cfg, which means the file default.cfg in directory pywps, located in PyWPS install location. This is the default configuration file.

Note

Never rewrite or remove this file. Use it only as template for your custom configuration files.

Local PyWPS configuration file

The local configuration file is used for the particular PyWPS instance only. It is the file, stored in PYWPS_CFG environment variable. This can be set either via web server configuration or with any wrapper script (see resources/pywps.cgi for example).

Make a copy of pywps/default.cfg to /usr/local/wps/pywps.cfg and customize the file as per below.

Configuration of PyWPS instance

Several sections are in the configuration file. The sections contain key value pairs of configuration options (see the example at the end of this section). If you do not set these options, they will be taken from the default configuration file.

WPS

The [wps] section contains general WPS instance settings, which are:

encoding
Language encoding (utf-8, iso-8859-2, windows-1250, dots)
title
Server title
version
WPS version (1.0.0)
abstract
Server abstract
fees
Possible fees
constraints
Possible constraints
serveraddress
WPS script address: http://foo/bar/pywps.py or http://foo/bar/cgi-bin/pywps.cgi
keywords
Comma-separated list of keywords realted to this server instance
lang
Comma-separated list of supported server languages. Default is ‘eng’.

Provider

The [provider] section contains information about you, your organization and so on:

providerName
Name of your company
individualName
Your name
positionName
At which position you are working
role
What your role is
deliveryPoint
Street
city
City
postalCode
Postal code or Zip code
country
Country name
electronicMailAddress
E-mail address
providerSite
Web site of your organization
phoneVoice
Telephone number
phoneFacsimile
Fax number
administrativeArea
State, province, territory or administrative area
hoursofservice
Hours of service to contact the provider
contactinstructions
Instructions on how to contact the provider

Server

The [server] section contains server settings, constraints, safety configuration and so on:

maxoperations
Maximum number of parallel running processes. If set to 0, then there is no limit.
maxinputparamlength
Maximum length of string input parameter (number of characters).
maxfilesize
Maximum input file size (raster or vector). The size can be determined as follows: 1GB, 5MB, 3kB, 1000b.
tempPath
Directory for temporary files (e.g. /tmp/pywps). PyWPS will create temporary directories in this directory, and after the calculation is performed, they should be deleted again.
outputPath
Path where output files are stored on the server. This should point to the outputUrl parameter (described below). For example http://foo/bar/wpsputputs. If outputPath starts with ftp:// it’s assumed that FTP support shall be used.
outputUrl
Url where the outputs are stored for client access. On Debian, it would be for example /var/www/wpsoutputs
ftplogin

FTP user login, if empty, anonymous login is used.

Note

FTP support is activated by ftp:// in outputPath

ftppasswd
FTP user password
ftpport
Default FTP port 21 is used if variable not defined.
debug

true/false - makes the logs for verbose

Note

This option is not used so wildly, as it should maybe be.

Note

Deprecated since 3.2. Use logLevel instead

processesPath

path to your processes. Default is pywps/processes.

Note

You can also set the PYWPS_PROCESSES environment variable with the same result, as described earlier on this page.

logFile
(since 3.0.1) File where all PyWPS logs go to. If not set, default error.log from Web Server configuration is used. Sometimes, this can cause problem for the asynchronous calls.
logLevel
(since 3.2) one of DEBUG, INFO, WARNING, ERROR and CRITICAL, default is INFO

GRASS

The [grass] section is specifically for GRASS GIS settings (optional):

path
PATH environment variable, e.g. /usr/lib/grass/bin:/usr/lib/grass/scripts
addonPath
GRASS_ADDONS environment variable
version
GRASS version
gui
Should be “text”
gisbase
Path to GRASS GIS_BASE directory (/usr/lib/grass)
ldLibraryPath
Path of GRASS Libs (/usr/lib/grass/lib)
gisdbase
Full path to GRASS database directory, where Locations are stored (/home/foo/grassdata)

Configuration file example

[wps]
encoding=utf-8
title=PyWPS Server
version=1.0.0
abstract=See http://pywps.wald.intevation.org and http://www.opengeospatial.org/standards/wps
fees=None
constraints=none
serveraddress=http://localhost/cgi-bin/wps
keywords=GRASS,GIS,WPS
lang=eng

[provider]
providerName=Your Company Name
individualName=Your Name
positionName=Your Position
role=Your role
deliveryPoint=Street
city=City
postalCode=000 00
country=eu
electronicMailAddress=login@server.org
providerSite=http://foo.bar
phoneVoice=False
phoneFacsimile=False
administrativeArea=False

[server]
maxoperations=3
maxinputparamlength=1024
maxfilesize=3mb
tempPath=/tmp
processesPath=
outputUrl=http://localhost/wps/wpsoutputs
outputPath=/var/www/wps/wpsoutputs
debug=true
logFile=/var/log/pywps.log

[grass]
path=/usr/lib/grass/bin/:/usr/lib/grass/scripts/
addonPath=
version=6.2.1
gui=text
gisbase=/usr/lib/grass/
ldLibraryPath=/usr/lib/grass/lib
gisdbase=/home/foo/datagrass

Notes for Windows users

Windows users do have to adjust their paths to what is standard on this platform. E.g. instead of using “:” as delemiter “;” is supposed to be used. Also usage of slash “/” and backslash “" can be tricky.

Generally speaking, it’s good to start by installing GRASS (if needed) and all the required geospatial packages using OSGeo4W tool.

Having GRASS and PyWPS is possible and was successfuly tested. You have to adjust especially PATH variable. Example of relevant configuration parts follows:

[server]
maxoperations=30
maxinputparamlength=1024
maxfilesize=10mb
tempPath=c:\\\\tmp
processesPath=
outputUrl=http://localhost/tmp/wpsoutputs
outputPath=c:\OSGeo4W\apache\htdocs\tmp\wpsoutputs\
debug=true # deprecated since 3.2, use logLevel instead
logFile=
logLevel=INFO

[grass]
path=c:\\\\osgeo4w/apps/grass/grass-7.0.0/lib;c:\\\\osgeo4w/apps/grass/grass-7.0.0/bin;c:\\\\c/Users/jachym/AppData/Roaming/GRASS7/addons/bin;c:\\\\usr/bin;c:\\\\osgeo4w/bin;c:\\\\c/Windows/system32;c:\\\\c/Windows;c:\\\\c/Windows/WBem;c:\\\\usr/bin;c:\\\\osgeo4w/apps/Python27/Scripts
addonPath=
version=7.0.0
gui=text
gisbase=c:\\\\OSGeo4W\\\\apps\\\\grass\\\\grass-7.0.0
ldLibraryPath=c:\OSGeo4W\apps\grass\grass-7.0.0\lib
gisdbase=c:\Users\jachym\src\vugtk\grassdata\
home=c:\Users\jachym

FOr the configuration of Apache web server, you can directly use wps.py binary from the root of PyWPS source code and use it. Example of relevant httpd.conf file follows (it can of course be used on Unix as well):

# wps.py was copied from pywps-source/wps.py
Alias /wps C:\OSGeo4W/bin/wps.py
<Location /wps
        SetHandler cgi-script
        Options ExecCGI
        SetEnv PYWPS_CFG C:\path/to/your/configuration/pywps.cfg
        SetEnv PYWPS_PROCESSES C:\path/to/your/processes
</Location>

Testing PyWPS

Testing PyWPS can be done on the command line – it is the easier way, how to get both – standard error and standard output – at once. Testing in the web server environment can be done later.

Before we start to test, be aware that we assume the following:

1 - PyWPS is installed properly, see installation 2 - Configuration file is stored in /usr/local/wps/pywps.cfg,

3 - At least one process is stored in the
/usr/local/wps/processes directory.
4 - There is a /usr/local/wps/processes/__init__.py file, with at

least:

__all__ = ['yourProcess']

text in it. For testing purposes, we assume that yourProcess is ultimatequestionprocess. For further reading about how to setup custom processes, see custom-processes.

For testing, we are using HTTP GET KVP encoding of OGC WPS request parameters. If you require clarification of WPS request parameters, please consult the OGC WPS 1.0.0 standard.

Note

Be aware that this document describes PyWPS, which is a server implementation of OGC WPS. There is some graphical user interface to the server (WPS Clients), but for testing purposes, they are not suitable. That is the reason, why following section will use command line tools and direct XML outputs.

Testing PyWPS installation

Find the location of cgiwps.py and run it without any further configuration.:

$ ./cgiwps.py

And you should get result like this (which is a mixture of standard output and standard error):

Content-type: text/xml

PyWPS NoApplicableCode: Locator: None; Value: No query string found.
<?xml version="1.0" encoding="utf-8"?>
<ExceptionReport version="1.0.0" xmlns="http://www.opengis.net/ows" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <Exception exceptionCode="NoApplicableCode">
                <ExceptionText>
                        No query string found.
                </ExceptionText>
        </Exception>
</ExceptionReport>

Testing PyWPS configuration

Now we have to export two environment variables: location of the configuration file and location of processes directory:

$ export PYWPS_CFG=/usr/local/wps/pywps.cfg
$ export PYWPS_PROCESSES=/usr/local/wps/processes

Afterwards, you can run the PyWPS CGI script. We will use HTTP GET requests, because they are easy to follow and faster to construct.

GetCapabilities

On the command line:

$ ./cgiwps.py "service=wps&request=getcapabilities"

You should obtain a Capabilities response:

Content-Type: text/xml

<?xml version="1.0" encoding="utf-8"?>
<wps:Capabilities service="WPS" version="1.0.0" xml:lang="eng" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:wps="http://www.opengis.net/wps/1.0.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wps/1.0.0 http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_response.xsd" updateSequence="1">
        <ows:ServiceIdentification>
        [...]

Note

Have a more detailed look at the <wps:ProcessOfferings>…</wps:ProcessOfferings> part of the output XML. There should be at least `Process

DescribeProcess

On the command line:

$ ./cgiwps.py "service=wps&version=1.0.0&request=describeprocess&identifier=Process"

You should obtain a ProcessDescriptions response:

<?xml version="1.0" encoding="utf-8"?>
<wps:ProcessDescriptions 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 http://schemas.opengis.net/wps/1.0.0/wpsDescribeProcess_response.xsd" service="WPS" version="1.0.0" xml:lang="eng">
    <ProcessDescription wps:processVersion="0.2" storeSupported="True" statusSupported="True">
        <ows:Identifier>ultimatequestionprocess</ows:Identifier>
        <ows:Title>The numerical answer to Life, Universe and Everything</ows:Title>
        [...]

Execute

On the command line:

$ ./cgiwps.py "service=wps&version=1.0.0&request=execute&identifier=ultimatequestionprocess"

You should obtain an ExecuteResponse response (this may take some time):

<?xml version="1.0" encoding="utf-8"?>
<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 http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_response.xsd" service="WPS" version="1.0.0" xml:lang="eng" serviceInstance="http://78.156.32.132/cgi-bin/wps?service=WPS&amp;request=GetCapabilities&amp;version=1.0.0" statusLocation="http://78.156.32.132/tmp/pywps/pywps-126450573849.xml">
    <wps:Process wps:processVersion="2.0">
        <ows:Identifier>ultimatequestionprocess</ows:Identifier>
        <ows:Title>Answer to Life, the Universe and Everything</ows:Title>
        <ows:Abstract>Numerical solution that is the answer to Life, Universe and Everything. The process is an improvement to Deep Tought computer (therefore version 2.0) since it no longer takes 7.5 milion years, but only a few seconds to give a response, with an update of status every 10 seconds.</ows:Abstract>
    </wps:Process>
    <wps:Status creationTime="Tue Jan 26 12:37:18 2010">
        <wps:ProcessSucceeded>PyWPS Process ultimatequestionprocess successfully calculated</wps:ProcessSucceeded>
    </wps:Status>
    <wps:ProcessOutputs>
        <wps:Output>
            <ows:Identifier>answer</ows:Identifier>
            <ows:Title>The numerical answer to Life, Universe and Everything</ows:Title>
            <wps:Data>
                <wps:LiteralData dataType="integer">42</wps:LiteralData>
            </wps:Data>
        </wps:Output>
    </wps:ProcessOutputs>
</wps:ExecuteResponse>

Issues

Note

A list of known problems follows. If you have seen something different, please let us know via the mailing list.

Note

Every error you get, should have standard error and standard output part, but they are mixed together. We describe here the most important part, the general error description.

Could not store file in compiled form: [Errno 13] Permission denied: ‘pywps/Templates/1_0_0/GetCapabilities.tmplc’
PyWPS tries to store precompiled templates to Templates directory and does not have rights for it (or the user, under which PyWPS is running, does not have the rights, e.g. www-data). Change permissions of the directory, so that other users can write in it as well.

Process executed. Failed to build final response for output [los]: [Errno 13] Permission denied: ‘/var/tmp/pywps/los-6165.tif’ Process executed. Failed to build final response for output [los]: [Errno 2] No such file or directory: ‘/var/tmp/pywpsx/los-6217.tif’

PyWPS probably successfully calculated the process, but when it tried to store result file to output directory, it failed. Try to set read-write access to directory with output files or create the output directory.

[Errno 2] No such file or directory: ‘/tmp/’ [Errno 13] Permission denied: ‘/tmp/’

PyWPS did not find some directory or file, configured in the configuration file, or the appropriate permissions are not set.
No process in ProcessOfferings listed

The PYWPS_PROCESSES is not set properly or there is no:

__all__ = ['ultimatequestionprocess']

in the __init__.py in the PYWPS_PROCESSES directory.

PyWPS Process

Processes directory

A PyWPS process can be thought of as a Python module. All PyWPS processes are stored in one directory Python Package. The __init__.py file must contain the list of processes in __all__ array.

Default location for processes

The default location of PyWPS processes is located in the pywps/processes directory, in the installation location of PyWPS.

Configuration via PYWPS_PROCESSES environment variable

Usually, you will overwrite this with the PYWPS_PROCESSES environment variable in the Configuration.

Configuration via configuration file

Alternatively you can set the processesPath variable in the configuration file.

Logging

PyWPS uses Python module logging for logging purposes. If there is something you need to log (activity, variable content for debug or anything else), just import the module and use accordingly:

import logging
LOGGER = logging.getLogger(__name__)
...

LOGGER.info("Just information message")
LOGGER.debug("Variable content: %s" % variable)
LOGGER.error("Error occured: %s" % e)

The logs are printed to standard error, or to a file set in the configuration file Configuration. Log level is set with logLevel option, also in the configuration file.

Process structure

In the file containing the process, there must be one class with the name Process, which is instance of pywps.Process.WPSProcess class. Each process must contain at least two methods: pywps.Process.WPSProcess.__init__() and pywps.Process.WPSProcess.execute() .

Process initialization: __init__ method

This method is the constructor for the actual process. It has to invoke the pywps.Process.WPSProcess.__init__() method of the superior WPSProcess class with process configuration options, described in pywps.Process.WPSProcess in more detail.

The process can then define several pywps.Process.InAndOutputs.Input and pywps.Process.InAndOutputs.Output instances. Several methods can be used for this, namely pywps.Process.WPSProcess.addLiteralInput(), pywps.Process.WPSProcess.addComplexInput(), pywps.Process.WPSProcess.addBBoxInput() for inputs and pywps.Process.WPSProcess.addLiteralOutput(), pywps.Process.WPSProcess.addComplexOutput(), pywps.Process.WPSProcess.addBBoxOutput() for outputs.

Process execution: execute method

The pywps.Process.WPSProcess.execute() method, which is originally empty, is called by PyWPS for process execution. The actual calculation is to be done here. When the process returns any text, it is handled as an error message. When a process is successfully calculated, this method returns None.

Example of PyWPS process

After adding “returner” string to __all__ array, in the __init__.py file in the PyWPS Processes directory, we can try GetCapabilities, DescribeProcess and Execute requests.

Process Inputs and Outputs

Process inputs and outputs are of three types:

ComplexValue
Usually used for raster or vector data
LiteralValue
Used for simple text strings
BoundingBoxValue
Two coordinate pairs of lower-left and upper-right corners in defined coordinate sytem.

Inputs and outputs should usually be defined in the __init__ method of the process.

ComplexValue input and Output

ComplexValue inputs and outputs are used in WPS to send larger sets of data (usually raster or vector data) into the process or from the process back to the user. The pywps.Process.WPSProcess.addComplexInput() method returns an instance of pywps.Process.InAndOutputs.ComplexInput for inputs. For outputs, they are called with pywps.Process.WPSProcess.addComplexOutput(), which returns pywps.Process.InAndOutputs.ComplexOutput.

The pywps.Process.InAndOutputs.ComplexInput.value and pywps.Process.InAndOutputs.ComplexOutput.value attributes contain the file name of the raster or vector file.

For inputs, consider using the pywps.Process.InAndOutputs.ComplexInput.getValue() method, for getting the value of the input, which can be returned as file object or file name.

For outputs, you should definitely use the pywps.Process.InAndOutputs.ComplexOutput.setValue() method for setting the results file name. The method accepts a file object as well as a file name.

Sometimes, users are sending the data as reference to some URL (e.g. OGC WFS or WCS service). PyWPS downloads the data for you and stores them to a local file. If the client requires reference to the output data, PyWPS will create this for you. PyWPS is able to setup a MapServer instance for you, and return OGC WFS or WCS URLs back to the client. For more on this topic, see using-mapserver.

Even you can (and should) define support data mimetypes (pywps.Process.InAndOutputs.ComplexInput.formats), mimetype only is checked. PyWPS does not care about valid schemas or anything else. This should be handled by Your process.

Vector data values

Vectors are usually handled as GML files. You can send any other file format as well, such as GeoJSON, KML or any other vector data. Only condition is: the file should be in text form (so it can fit into XML correctly), if you want to append it as part of the input XML request and everything should be stored in one file.

Vectors are the default pywps.Process.InAndOutputs.ComplexInput.format of ComplexValue input or output – text/xml (GML) is expected.

Note

Some users do want to send ESRI Shapfiles. This is in general not to advisable. Shapefiles are a binary format, which is hard to be used with XML, and it consists out of at least three files shp, shx and dbf.

If you still want to handle shapefiles, you have either to zip everything in one file or define three separate complex inputs.

Example of simple input vector data:

self.inputVector = self.addComplexOutput(identifier="in",title="Input file")

Example of more complex input vector data:

self.gmlOrSimilarIn = self.addComplexInput(identifier="input",
                        title="Input file",
                        abstract="Input vector file, usually in GML format",
                        formats = [
                                    # gml
                                    {mimeType: 'text/xml',
                                    encoding:'utf-8',
                                    schema:'http://schemas.opengis.net/gml/3.2.1/gml.xsd'},
                                    # json
                                    {mimeType: 'text/plain',
                                    encoding: 'iso-8859-2',
                                    schema: None
                                    },

                                    # kml
                                    {mimeType: 'text/xml',
                                    encoding: 'windows-1250',
                                    schema: 'http://schemas.opengis.net/kml/2.2.0/ogckml22.xsd'}
                                    ],
                        # we need at least TWO input files, maximal 5
                        minOccurs: 2,
                        maxOccurs: 5,
                        metadata: {'foo':'bar','spam':'eggs'}
                    )
Raster data values

Sometimes, you need to work with raster data. You have to set the proper pywps.Process.InAndOutputs.ComplexInput.formats attribute of supported raster file format. Since rasters are usually in binary format, you would usually have to send the data always as reference. Fortunately, this is not the case. PyWPS can handle the input data, encoded in Base64 format and once PyWPS needs to send raster data out as part of Execute response XML, they are encoded with Base64 as well.

Example of simple output raster data:

self.dataIn = self.addComplexOutput(identifier="raster",
                    title="Raster out",
                    formats=[{"mimeType":"image/tiff"}])
LiteralValue input and Output

With literal input, you can obtain or send any type of character string. You will obtain an instance of pywps.Process.InAndOutputs.LiteralInput or pywps.Process.InAndOutputs.LiteralOutput.

Literal value Inputs can be more complex. You can define a list of allowed values, type of the literal input, spacing and so on.

Note

Spacing is not supported, so you can not currently define the step in allowed values row.

Type

For type settings, you can either use the types module, or the Python type() function. The default type is type(0) – Integer. PyWPS will check if the input value type matches allowed type.

Note

If you need the String type of literal input, PyWPS will always remove everything behind “#”, “;”, “!”, “&” and similar characters. Try to avoid usage of LiteralValue input directly as input for e.g. SQL database or command line programs. This could cause a serious system compromise.

Allowed Values

PyWPS lets you define a list of allowed input values. These can be string, integer or float types. Default values are defined in the list. Ranges are defined as two-items filed in form of (minimum,maximum). For example, we would like to allow values 1,2,3, 5 to 7, and ‘spam’, the pywps.Process.InAndOutputs.LiteralInput.values value would look like:

[1,2,3,[5,7],'spam']

Default is “*”, which means all values.

Simple example of LiteralValue output:

self.widthOut = self.addLiteralOutput(identifier = "width",
                     title = "Width")

Complex example of LiteralValue input:

self.litIn = self.addLiteralInput(identifier = "eggs",
                title = "Eggs",
                abstract = "Eggs with spam and sausages",
                minOccurs = 0,
                maxOccurs = 1,
                uoms = "m",
                dataType=type(0.0),
                default=1.1,
                values=[(0.0,10.1)])
BoundingBoxValue input and Output

BoundingBox are two pairs of coordinates, defined in some coordinate system (2D or 3D). In PyWPS, they are defined in pywps.Process.InAndOutputs.BoundingBoxInput and pywps.Process.InAndOutputs.BoundingBoxOutput. For getting them, use pywps.Process.WPSProcess.addBBoxInput() and pywps.Process.WPSProcess.addBBoxOutput() respectively.

The value is a list of four coordinates in (minx, miny, maxx, maxy) format.

Example of BoundingBoxValue input:

self.bbox = self.addBBoxOutput(identifier = "bbox",
                          title = "BBox")

Execution of the process

Each process has to define a pywps.Process.WPSProcess.execute() method, which makes the calculation and sets the output values. This method is called via a WPS Execute request.

In principle, you can do what ever you need within this method. It is advised, to use python bindings to popular GIS packages, like GRASS GIS, GDAL/OGR, PROJ4, or any other Python-GIS package.

In the special chapter, we give you a quick intro to some of these packages. Some examples are also distributed along with the PyWPS source.

If you need to run some shell programs, you should use the pywps.Process.WPSProcess.cmd() method.

Below is an example of a simple execute method which transforms a raster file from one coordinate system to another, using Python bindings to GDAL.:

from osgeo import gdal
from osgeo.gdalconst import *

...

def execute(self):
    """Convert input raster file to PNG using GDAL"""

    # create gdal input dataset
    indataset = gdal.Open( self.inputRaster.getValue())

    # get output driver for PNG format
    pngDriver = gdal.GetDriverByName("png")

    # define output gdal dataset
    outfile = "output.png"
    outdataset = pngDriver.CreateCopy(outfile, indataset)

    self.outputRaster.setValue(outfile)
    self.outputRaster.format = {'mimeType':"image/png"}

    return

Special PyWPS Topics

How to use PyWPS with other packages and projects

PyWPS and GRASS GIS

PyWPS was originally written with support for GRASS GIS. The processes can be executed within a temporary created GRASS Location or within an existing GRASS Location, within temporary created Mapset. If you are not familiar with this concepts, please review the GRASS documentation.

Configuring PyWPS

First you have to configure PyWPS configuration file, as described in Configuration.

Allowing Process to be executed in the GRASS environment

When you are initializing a new process (see process-initialization), you can add a pywps.Process.WPSProcess.grassLocation attribute to it.

The attribute can have the following values:

None

GRASS Location is not created, GRASS environment is not started (default):

WPSProcess.__init__(self, identifier = "foo)
True

Temporary GRASS Location is created in XY coordinate system. .. note:: In the future, GRASS Location will probably have a

coordinate system token from the input raster or vector file.:

WPSProcess.__init__(self, identifier = “foo”,
…, grassLocation = True)
String

Name of the GRASS Location within the configured grassdbase. If the name starts with “/”, the full path to the location is taken, without any other configuration.:

WPSProcess.__init__(self, identifier = "foo",
    ...
    grassLocation = "spearfish60")

or:

WPSProcess.__init__(self,
    identifier = "foo",
    ...
    grassLocation = "/foo/bar/grassdata/spearfish60")

Running GRASS modules from PyWPS

You have two options: either run GRASS modules as you would do in shell script (running the modules directly) or access the GRASS-python interface.

Running GRASS command line modules

Once the pywps.Process.WPSProcess.execute() method is executed, you can use the pywps.Process.WPSProcess.cmd() method for calling GRASS modules.

Using GRASS-Python interface

Since GRASS 6.4, Python bindings are supported. There are both a ctypes interface and GRASS Modules-Python interface. They are both described in the GRASS Wiki . There are grass.run_command(), grass.mapcalc() and other useful methods.

GRASS-Python interface example
from pywps.Process import WPSProcess

process =  WPSProcess(identifier="grassprocess",
                    title="GRASS Process")

def execute():
    from grass.script import core as grass

    ret = grass.run_command("d.his", h_map = "drap_map",
                                     i_map = "relief_map",
                                     brighten = 0)
    return

process.execute = execute

PyWPS and UMN MapServer

PyWPS can integrate MapServer to return results of ComplexData back to the client.

The idea is as follows: if the client requires pywps.Process.InAndOutputs.ComplexOutput to be returned, as reference, usually, a direct link to the produced file is returned. But with MapServer, a WFS, WMS or WCS URL could be returned.

The client can later parse the URL of the resulting ComplexValue file and e.g. instead of having a GeoTIFF file (result of the calculation), obtained from the WCS, it can request a PNG file via WMS.

Requirements

To support MapServer for ComplexOutputs in your PyWPS installation, the following packages have to be installed:

  • python-mapscript
  • python-gdal
  • python-pyproj

Usage

When you are initializing a new process (see process-initialization), you can set the pywps.Process.InAndOutputs.ComplexOutput.useMapscript attribute to True to get it run. Have a look at the pywps.Process.InAndOutputs.ComplexOutput documentation, also for other attributes, like projection or bbox (can be set automatically from georeferenced file). Required format (pywps.Process.InAndOutputs.ComplexOutput.format decides on the output service type (WCS for GeoTIFF and similar, WFS for xml or text, WMS for PNG, JPEG, GIF).:

self.outputMap = self.addComplexOutput(identifier = "map",
                title = "Resulting output map",
                formats = [
                        {"mimeType":"image/tiff"},
                        {"mimeType":"image/png"}
                ],
                useMapscript=True)

PyWPS and Mod Python

PyWPS and WSGI

For more detailed information about WSGI, please visit their website <http://wsgi.org/wsgi/>_. In general WSGI is preferred over mod_python mode.

  • Install mod_wsgi for Apache server (if you are using it)

  • Locate webservices/wsgi/wsgiwps.py which provides the WSGI interface

  • Configure Apache server to something similar as:

     SetEnv PYTHONPATH /usr/local/src/pywps/ # is not installed the 'clean' way
     SetEnv PYWPS_CFG /usr/local/src/pywpsworkdir/pywps.cfg
     SetEnv PYWPS_PROCESSES /usr/local/src/pywpsworkdir/processes
    <Directory /usr/local/src/pywps/>
        Order allow,deny
        Allow from all
    </Directory>
    WSGIScriptAlias /wps /usr/local/src/pywps/webservices/wsgi/wpswsgi.py
    

WPS Clients

In this chapter, several (Py)WPS clients will be described. Some of them are part of the PyWPS distribution, others can be found on the Internet.

PyWPS JavaScript client

Part of the PyWPS distribution includes a generic WPS client based on OpenLayers. The client does not show any results in a map, however it enables you to program the client easily. The client is located in pywps-source/webclient/WPS.js. In addition, OpenLayers must be included in the web page.

Initialization and GetCapabilities request

To initialize the WPS object, the service URL is required. This example can be found in wpsclient/01-init.html.

// set the proxy
OpenLayers.ProxyHost = "/cgi-bin/proxy.cgi?url=";

// set the url
var url = "http://foo/bar/wps.py";

// init the client
wps = new OpenLayers.WPS(url);

// run get capabilities
wps.getCapabilities(url);

Parsing GetCapabilities response

You must define a function to handle the GetCapabilities response.

wps = new OpenLayers.WPS(url, {onGotCapabilities: onGetCapabilities});

/**
 * This function is called, when GetCapabilities response
 * arrived and was parsed
 **/
function onGetCapabilities() {

    var capabilities = "<h3>"+wps.title+"</h3>";
    capabilities += "<h3>Abstract</h3>"+wps.abstract;
    capabilities += "<h3>Processes</h3><dl>";

    // for each process, get identifier, title and abstract
    for (var i = 0; i < wps.processes.length; i++) {
        var process = wps.processes[i];

        capabilities += "<dt>"+process.identifier+"</dt>";
        capabilities += "<dd>"+"<strong>"+process.title+"</strong><br />"+
                        process.abstract+"</dd>";
    }

    capabilities += "</dl>";

    document.getElementById("wps-result").innerHTML = capabilities;
};

Parsing DescribeProcess response

The DescribeProcess request requires the identifier of the process. You can obtain available processes from the GetCapabilities response (described previously). The onDescribedProcess() must be defined. This example can be found in wpsclient/02-describe.html.

wps = new OpenLayers.WPS(url, {onDescribedProcess: onDescribeProcess});

// run get capabilities
wps.describeProcess("dummyprocess");

/**
 * This function is called, when DescribeProcess response
 * arrived and was parsed
 **/
function onDescribeProcess(process) {

    var description = "<h3>"+process.title+"</h3>";
    description += "<h3>Abstract</h3>"+process.abstract;
    description += "<h3>Inputs</h3><dl>";

    // for each input
    for (var i = 0; i < process.inputs.length; i++) {
        var input = process.inputs[i];
        description += "<dt>"+input.identifier+"</dt>";
        description += "<dd>"+"<strong>"+input.title+"</strong><br />"+
                        input.abstract+"</dd>";
    }
    description += "</dl>";
    description += "<h3>Outputs</h3><dl>";

    // for each input
    for (var i = 0; i < process.outputs.length; i++) {
        var output = process.outputs[i];
        description += "<dt>"+output.identifier+"</dt>";
        description += "<dd>"+"<strong>"+output.title+"</strong><br />"+
                        output.abstract+"</dd>";
    }
    description += "</dl>";

    document.getElementById("wps-result").innerHTML = description;
};

Calling Execute request

The Execute request requires the identifier, inputs and outputs parameters. You can obtain available processes and their inputs and outputs from the GetCapabilities and DescribeProcessj response (described previously). The onSucceeded() must be defined.

Defining Inputs and Outputs for the process ‘by hand’

In this example, we will define Inputs and Outputs of the process “by hand”, instead of gathering this information automatically via GetCapabilities and DescribeProcess.

The ‘by hand’ process initialization consists of three steps:

  1. Definition of process Inputs and Outputs
  2. Definition of the Process itself
  3. Adding a process to the WPS instance

This example can be found in wpsclient/03-execute.html.

// WPS object
wps = new OpenLayers.WPS(url,{onSucceeded: onExecuted});

// define inputs of the 'dummyprocess'
var input1 = new OpenLayers.WPS.LiteralPut({identifier:"input1",value:1});
var input2 = new OpenLayers.WPS.LiteralPut({identifier:"input2",value:2});

// define outputs of the 'dummyprocess'
var output1 = new OpenLayers.WPS.LiteralPut({identifier:"output1"});
var output2 = new OpenLayers.WPS.LiteralPut({identifier:"output2"});

// define the process and append it to OpenLayers.WPS instance
var dummyprocess =  new
OpenLayers.WPS.Process({identifier:"dummyprocess",
                         inputs: [input1, input2],
                         outputs: [output1, output2]});

wps.addProcess(dummyprocess);

// run Execute
wps.execute("dummyprocess");

Of course, func:onExecuted has to be defined:

/**
 * This function is called, when DescribeProcess response
 * arrived and was parsed
 **/
function onExecuted(process) {
    var executed = "<h3>"+process.title+"</h3>";
    executed += "<h3>Abstract</h3>"+process.abstract;

    executed += "<h3>Outputs</h3><dl>";

    // for each output
    for (var i = 0; i < process.outputs.length; i++) {
        var output = process.outputs[i];
        executed += "<dt>"+output.identifier+"</dt>";
        executed += "<dd>Title: <strong>"+output.title+"</strong><br />"+
                        "Abstract: "+output.abstract+"</dd>";
        executed += "<dd>"+"<strong>Value:</strong> "+
                        output.getValue()+"</dd>";
    }
    executed += "</dl>";
    document.getElementById("wps-result").innerHTML = executed;

};
Defining Inputs and Outputs for the process automatically

In this example, we will define Inputs and Outputs of the process automatically, using the GetCapabilities and DescribeProcess requests.

This example can be found in wpsclient/04-execute-automatic.html.

Call DescribeProcess first:

// init the client
wps = new OpenLayers.WPS(url,{
            onDescribedProcess: onDescribeProcess,
            onSucceeded: onExecuted
        });

// run Execute
wps.describeProcess("dummyprocess");

/**
 * DescribeProcess and call the Execute response
 **/
function onDescribeProcess(process) {
    process.inputs[0].setValue(1);
    process.inputs[1].setValue(2);

    wps.execute("dummyprocess");
};

The rest was already defined before.

PyWPS API

The pywps package consists of several sub-packages and classes:

Module Exceptions

Module GRASS

Module Parser

Parser classes used by parsing of OGC WPS Requests

Particular request packages:

Module GetCapabilities

Class Post
Class Get

Module DescribeProcess

Class Post
Class Get

Module Execute

Class Post
Class Get

Module Parser

Module Get

Module Post

Module Process

Module Lang

Class Lang

Module InAndOutputs

Class Input
Class LiteralInput
Class ComplexInput
Class BoundingBoxInput
Class Output
Class LiteralOutput
Class ComplexOutput
Class BoundingBoxOutput

Class Status

Class WPSProcess

Module Soap

Module Wps

WPS GetCapabilities request handler

Class GetCapabilities

WPS DescribeProcess request handler

Class DescribeProcess

WPS Execute request handler

Class Execute

Class Request

Package pywps

Indices and tables