过程

4.0.0 新版功能.

待处理

  • 输入验证

  • IOHandler

pywps与流程和服务一起工作。进程是python Class 包含一个 handler 方法和输入输出列表。pywps服务实例就是所选进程的集合。

pywps不附带任何预定义的进程——它由您作为pywps的用户来设置您选择的进程。pywps可以帮助您在web上发布出色的地理空间操作—它负责通信和安全,然后您必须添加内容。

注解

PyWPS-Flask 项目。

编写进程

注解

在这个地方,你应该为期末考试准备环境 部署到生产服务器 . 至少,您应该创建一个包含进程的目录,该目录通常命名为 processes ::

$ mkdir processes

在这个目录中,我们将创建包含进程的单个python脚本。

可以定位进程 系统中的任何地方 只要他们的位置在 PYTHONPATH 环境变量,可以导入到最终服务器实例中。

进程被编码为继承自 Process . 在 PyWPS-Flask 服务器它们保存在 过程 文件夹,通常在分开的文件中。

一个 过程 需要配置以下属性:

标识符

进程的唯一标识符

标题

对应标题

输入

过程输入清单

输出

过程输出清单

处理程序

接收方法 pywps.app.WPSRequestpywps.response.WPSResponse 作为输入。

向量缓冲过程示例

例如,我们将创建 缓冲区 进程-它将以矢量文件作为输入,在数据周围创建指定的缓冲区(使用 Shapely ,并返回结果。

因此,该过程将有两个输入:

  • ComplexData 输入-矢量文件

  • LiteralData 输入-缓冲区大小

它将有一个输出:

  • ComplexData 输出-最终缓冲区

这个过程可以称为 demobuffer 现在我们可以开始编码了:

$ cd processes
$ $EDITOR demobuffer.py

首先,我们必须导入所需的类和模块

下面是一个非常基本的例子:

28
29
30
31
from pywps import Process, LiteralInput, ComplexOutput, ComplexInput, Format
from pywps.app.Common import Metadata
from pywps.validator.mode import MODE
from pywps.inout.formats import FORMATS

下一步,我们将定义一个输入列表。第一个输入是 pywps.ComplexInput 带有标识符 vector 标题 Vector map 只有一种允许的格式:gml。

下一个输入是 pywps.LiteralInput ,带有标识符 size 数据类型设置为 float

33
34
35
36
37
38
39
40

inpt_vector = ComplexInput(
    'vector',
    'Vector map',
    supported_formats=[Format('application/gml+xml')],
    mode=MODE.STRICT
)

接下来我们定义输出 output 作为 pywps.ComplexOutput . 此输出仅支持GML格式。

42
43
44
45
46

out_output = ComplexOutput(
    'output',
    'HelloWorld Output',
    supported_formats=[Format('application/gml+xml')]

接下来,我们为输入和输出创建一个新的列表变量。

48
49

inputs = [inpt_vector, inpt_size]

接下来我们定义 处理程序 方法。在里面, 可能会进行地理空间分析 . 这个方法得到一个 pywps.app.WPSRequest 和A pywps.response.WPSResponse 对象作为参数。在我们的例子中,我们使用 GDAL/OGR library . 我们不会详细介绍,您应该注意的是如何从 pywps.app.WPSRequest 对象以及如何将数据设置为 pywps.response.WPSResponse 对象。

 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
@staticmethod
def _handler(request, response):
    """Handler method - this method obtains request object and response
    object and creates the buffer
    """

    from osgeo import ogr

    # obtaining input with identifier 'vector' as file name
    input_file = request.inputs['vector'][0].file

    # obtaining input with identifier 'size' as data directly
    size = request.inputs['size'][0].data

    # open file the "gdal way"
    input_source = ogr.Open(input_file)
    input_layer = input_source.GetLayer()
    layer_name = input_layer.GetName()

    # create output file
    driver = ogr.GetDriverByName('GML')
    output_source = driver.CreateDataSource(
        layer_name,
        ["XSISCHEMAURI=http://schemas.opengis.net/gml/2.1.2/feature.xsd"])
    output_layer = output_source.CreateLayer(layer_name, None, ogr.wkbUnknown)

    # get feature count
    count = input_layer.GetFeatureCount()
    index = 0

    # make buffer for each feature
    while index < count:

        response._update_status(WPS_STATUS.STARTED, 'Buffering feature {}'.format(index), float(index) / count)

        # get the geometry
        input_feature = input_layer.GetNextFeature()
        input_geometry = input_feature.GetGeometryRef()

        # make the buffer
        buffer_geometry = input_geometry.Buffer(float(size))

        # create output feature to the file
        output_feature = ogr.Feature(feature_def=output_layer.GetLayerDefn())
        output_feature.SetGeometryDirectly(buffer_geometry)
        output_layer.CreateFeature(output_feature)
        output_feature.Destroy()
        index += 1

    # set output format
    response.outputs['output'].data_format = FORMATS.GML

    # set output data as file name
    response.outputs['output'].file = layer_name

    return response

最后,我们把所有的东西放在一起,创造了一个新的 DemoBuffer 使用处理程序、输入和输出初始化。它基于 pywps.Process

51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class DemoBuffer(Process):
    def __init__(self):

        super(DemoBuffer, self).__init__(
            _handler,
            identifier='demobuffer',
            version='1.0.0',
            title='Buffer',
            abstract='This process demonstrates, how to create any process in PyWPS environment',
            metadata=[Metadata('process metadata 1', 'http://example.org/1'),
                      Metadata('process metadata 2', 'http://example.org/2')],
            inputs=inputs,
            outputs=outputs,
            store_supported=True,
            status_supported=True
        )

声明输入和输出

客户需要知道流程需要哪些输入。它们可以声明为 pywps.Input 中的对象 Process 类声明:

from pywps import Process, LiteralInput, LiteralOutput

class FooProcess(Process):
    def __init__(self):
        inputs = [
            LiteralInput('foo', data_type='string'),
            ComplexInput('bar', [Format('text/xml')])
        ]
        outputs = [
            LiteralOutput('foo_output', data_type='string'),
            ComplexOutput('bar_output', [Format('JSON')])
        ]

        super(FooProcess, self).__init__(
            ...
            inputs=inputs,
            outputs=outputs
        )
        ...

注解

更一般的描述可以在 ogc web处理服务(ogc wps) 章。

LiteralData

请求中嵌入的简单值。第一个参数是名称。第二个参数是 stringfloatintegerboolean .

ComplexData

大数据对象,例如层。complexdata确实有 format 属性作为其关键属性之一。它可以是受支持格式的列表,也可以是单个(已选定)格式。它将是 pywps.inout.formats.Format 班级。

ComplexData Format 和输入验证

complexdata需要一个支持的数据格式列表作为其参数之一。它们来自 Format 班级。一 Format 实例需要 mime_type 参数,A validate 方法(用于输入数据验证)和 mode 参数——定义验证的严格程度(请参见 pywps.validator.mode.MODE

这个 Validate 方法取决于您、用户和代码。它需要两个输入参数- data_input (A) ComplexInput 对象) mode . 这个方法必须返回 boolean 值,该值指示输入数据对于给定的 mode . 你可以从 pywps.validator.complexvalidator.validategml() 方法。

好消息是:已经有了使用GDAL/OGR为esri shapefile、gml和geojson格式预定义的验证方法。还有一个XML模式验证和一个JSON模式验证-您只需从 pywps.inout.formats.FORMATS 列出并将验证模式设置为 ComplexInput 对象。

更好的消息是:您可以根据需要定义自定义验证函数和验证输入数据。

BoundingBoxData

boundingboxdata包含有关所需区域和坐标参考系的边界框的信息。boundingboxdata的有趣属性是:

crs

当前坐标系

dimensions

尺寸的数量

ll

左下角的一对坐标(或三元组)

ur

右上角的一对坐标(或三元组)

访问中的输入和输出 handler 方法

处理程序作为输入参数接收 WPSRequest 对象。输入值位于 inputs 字典:

@staticmethod
def _handler(request, response):
    name = request.inputs['name'][0].data
    response.outputs['output'].data = 'Hello world %s!' % name
    return response

inputs 是一本普通的python字典。大多数输入和输出来自 IOHandler 班级。这使用户能够以四种不同的方式访问数据:

input.file

返回文件名-您可以使用存储在硬盘上的文件名访问数据。

input.url

使用 file://http:// 方案。在通过 filedatastream 属性。

input.data

是数据本身的直接链接。不需要在硬盘上创建文件对象,也不需要打开并关闭文件-pywps将为您做任何事情。

input.stream

提供数据的iostream。不需要打开文件,你只要 read() 数据。

由于可能有多个具有相同标识符的输入值,因此可以使用索引访问这些输入。例如::

request.inputs['file_input'][0].file
request.inputs['data_input'][0].data
request.inputs['stream_input'][0].stream
url_input = request.inputs['url_input'][0]

如前所述,如果输入是指向远程文件的链接(一个 http 地址),访问 url 属性只返回url的字符串,但访问任何其他属性都会触发文件的下载:

url_input.url # returns the link as a string (no download)
url_input.file # downloads target and returns the local path
url_input.data # returns the content of the local copy

pywps将持久地将输入(和输出)数据转换为所需的格式。您还可以设置 Output 类物体 output.data = 1output.file = "myfile.json" -工作原理是一样的。但是,一旦设置了源类型,就不能更改它。也就是说,A ComplexOutput 谁的 data 属性一旦设置为对其他三个属性的只读访问 (filestreamurldata 属性可以自由修改。

进度和状态报告

ogc wps标准支持异步流程执行调用,这在流程执行需要较长时间时尤其有用-流程实例设置为background,wps使用 ProcessAccepted 消息立即返回给客户。客户必须检查 statusLocation 部署当前状态报告的url,例如每隔n秒或n分钟(取决于计算时间)。回复的内容通常是 percentDone 有关进展的信息 statusMessage 文本信息,当前正在发生的事情。

您可以在 handler 使用 WPSResponse.update_status() 功能。

返回大数据

wps允许一种聪明的方法返回一个大数据文件:它可以单独保存,而不是将数据嵌入到响应中,并从可以下载数据的位置返回url。在当前实现中,pywps将文件保存在服务传递的配置中指定的文件夹中(或在默认位置)。返回的url嵌入在xml响应中。

可以使用get:请求此行为:

...ResponseDocument=output=@asReference=true...

或者一个请求:

...
<wps:ResponseForm>
    <wps:ResponseDocument>
        <wps:Output asReference="true">
            <ows:Identifier>output</ows:Identifier>
            <ows:Title>Some Output</ows:Title>
        </wps:Output>
    </wps:ResponseDocument>
</wps:ResponseForm>
...

输出 是用户希望从url存储和访问的输出的标识符。用户可以根据需要通过引用请求尽可能多的输出,但仅限于 one 可以原始格式请求。

返回多个文件

当进程接受可变数量的输入时,返回可变数量的输出通常是有意义的。然而,wps标准并不容易适应这一点。一个实用的解决方案是将文件压缩到一个单独的输出归档文件中(例如zip文件),但是当输出实际上只是对资源(url)的引用时,这会很尴尬。在这种情况下,另一个实用的解决方案是返回一个存储引用列表的简单文本文件。其中一个问题是,它为客户机提供的关于文件内容的元数据非常少。

尽管定义一个存储多个文件的属性和url的json输出文件相当容易,但它需要在客户端上使用一个特殊的实现来解析json并提取url元数据。幸运的是 metalink 标准已经存在,可以将引用绑定到多个文件。

metalink文件是收集一组远程文件的xml文档。它最初设计用于描述存储在多个镜像或对等网络上的大型文件的位置。如果下载期间某个位置出现故障,metalink客户端可以切换到另一个镜像。此外,大文件可以分成多个片段,并从不同的位置同时下载,从而加快下载速度。metalink还可以描述为不同操作系统和语言创建的文件的位置,客户机会自动选择最合适的文件。

pywps中的metalink支持包括:

  • pywps.FORMATS.METALINK and pywps.FORMATS.META4

  • 帮助程序类 MetaFileMetaLinkMetaLink4

  • 使用xml模式验证生成的metalink文件

  • metalink文档中每个文件的大小(字节)和校验和(sha-256)

要在进程中使用metalink,请定义 ComplexOutput 用金属油墨模版。然后在处理程序生成文件列表后,实例化一个 MetaFile 对象,并将它们附加到 MetaLinkMetaLink4 实例。最后,将输出的数据属性设置为 xml 性质 MetaLink 实例。

注解

MetaLink 使用Metalink标准版本3.0,而 MetaLink4 使用版本4.0。

示例过程

from pywps import Process, LiteralInput, ComplexOutput, FORMATS
from pywps.inout.outputs import MetaLink4, MetaFile


class MultipleOutputs(Process):
    def __init__(self):
        inputs = [
            LiteralInput('count', 'Number of output files',
                         abstract='The number of generated output files.',
                         data_type='integer',
                         default=2)]
        outputs = [
            ComplexOutput('output', 'Metalink4 output',
                          abstract='A metalink file storing URIs to multiple files',
                          as_reference=True,
                          supported_formats=[FORMATS.META4])
        ]

        super(MultipleOutputs, self).__init__(
            self._handler,
            identifier='multiple-outputs',
            title='Multiple Outputs',
            abstract='Produces multiple files and returns a document'
                     ' with references to these files.',
            inputs=inputs,
            outputs=outputs,
            store_supported=True,
            status_supported=True
        )

    def _handler(self, request, response):
        max_outputs = request.inputs['count'][0].data

        ml = MetaLink4('test-ml-1', 'MetaLink with links to text files.', workdir=self.workdir)
        for i in range(max_outputs):
            # Create a MetaFile instance, which instantiates a ComplexOutput object.
            mf = MetaFile('output_{}'.format(i), 'Test output', format=FORMATS.TEXT)
            mf.data = 'output: {}'.format(i)  # or mf.file = <path to file> or mf.url = <url>
            ml.append(mf)

        # The `xml` property of the Metalink4 class returns the metalink content.
        response.outputs['output'].data = ml.xml
        return response

进程异常

进程执行中的任何未修补异常都将由pywps处理,并使用 ows:Exception . pywps将只记录回溯并报告一条常见错误消息,如:

进程失败,请检查服务器错误日志。

此稀疏错误消息用于通过以不受控制的方式提供内部服务信息来避免安全问题。

但在某些情况下,您希望提供一个用户友好的错误消息,以向用户提示处理作业的错误。在这种情况下,您可以使用 pywps.app.exceptions.ProcessError 例外。错误消息将发送给封装为 ows:Exception . 这个 pywps.app.exceptions.ProcessError 验证错误消息以确保它不太长并且不包含任何可疑字符。

注解

默认情况下,有效的错误消息的长度必须介于3到144个字符之间。只允许字母数字字符和一些特殊字符。允许的特殊字符为:“.”、“:”、“!”“?”,“=”,“,”,“-”.

注解

在流程开发过程中,您可能希望获得如下所示的回溯 ows:Exception . 这可以通过在调试模式下运行pywps来实现。在 pywps.cfg 配置文件集:

[logging]
level=DEBUG

示例过程

from pywps import Process, LiteralInput
from pywps.app.Common import Metadata
from pywps.app.exceptions import ProcessError

import logging
LOGGER = logging.getLogger("PYWPS")


class ShowError(Process):
    def __init__(self):
        inputs = [
            LiteralInput('message', 'Error Message', data_type='string',
                         abstract='Enter an error message that will be returned.',
                         default="This process failed intentionally.",
                         min_occurs=1,)]

        super(ShowError, self).__init__(
            self._handler,
            identifier='show_error',
            title='Show a WPS Error',
            abstract='This process will fail intentionally with a WPS error message.',
            metadata=[
                Metadata('User Guide', 'https://pywps.org/')],
            version='1.0',
            inputs=inputs,
            # outputs=outputs,
            store_supported=True,
            status_supported=True
        )

    @staticmethod
    def _handler(request, response):
        response.update_status('PyWPS Process started.', 0)

        LOGGER.info("Raise intentionally an error ...")
        raise ProcessError(request.inputs['message'][0].data)

流程部署

为了让客户机调用进程,pywps Service 类必须具有侦听请求的能力。必须创建此类的实例,接收所有所需进程类的实例。

示例服务 Service 类实例是在 Server 班级。 Server 是一个依赖于 Flask . 过程的发布被封装在 demo.py ,其中main方法将进程实例列表传递给 Server 班级:

from pywps import Service
from processes.helloworld import HelloWorld
from processes.demobuffer import DemoBuffer

...
processes = [ DemoBuffer(), ... ]

server = Server(processes=processes)

...

运行开发服务器

这个 Flask 服务及其处理示例 服务器是 WSGI application 接受传入的 Execute 请求并调用适当的进程来处理它们。它也回答了 GetCapabilitiesDescribeProcess 基于进程标识符及其输入和输出的请求。

主机、端口、配置文件和进程可以作为参数传递给 Server 建造师。 hostport优先考虑 如果传递给构造函数,则为配置文件的内容 (pywps.cfg )使用。

使用 run 启动服务器的方法:

...
s = Server(host='localhost', processes=processes, config_file=config_file)
s.run()
...

若要使服务器在另一台计算机上可见,请替换 localhost 具有 0.0.0.0 .

支持多种语言

支持多种语言需要:

  • 设置 language 属性(请参阅 [服务器]

  • 将翻译添加到 Process ,输入和输出对象

预期的翻译格式总是相同的。第一个键是RFC 4646语言代码,嵌套映射包含可由字符串属性访问的翻译字符串:

from pywps import Process, LiteralInput, LiteralOutput


class SayHello(Process):
    def __init__(self):
        inputs = [
            LiteralInput(
                'name',
                title='Input name',
                abstract='The name to say hello to.',
                translations={"fr-CA": {"abstract": "Le nom à saluer."}}
            )
        ],
        outputs=[
            LiteralOutput(
                'response',
                title='Output response',
                abstract='The complete output message.',
                translations={"fr-CA": {
                    "title": "La réponse",
                    "abstract": "Le message complet."
                }}
            )
        ],

        super().__init__(
            self._handler,
            identifier='say_hello',
            title='Process Say Hello',
            abstract='Returns a literal string output with Hello plus the inputed name',
            version='1.0',
            inputs=inputs,
            outputs=outputs,
            store_supported=True,
            status_supported=True,
            translations={"fr-CA": {
                "title": "Processus Dire Bonjour",
                "abstract": "Retourne une chaine de caractères qui dit bonjour au nom fournit en entrée."
            }},
        )

    def _handler(self, request, response):
        ...

如果未在中提供键,则转换将默认为基对象的未翻译属性 translations 措辞。

自动化过程文档

A Process 可自动记录 Sphinx 使用 autoprocess 指令。这个 Process 对象被实例化并检查其内容,以便在后台创建numpy格式的docstring。这使得开发人员可以将文档直接嵌入到代码中,而不必手动描述每个过程。例如::

.. autoprocess:: pywps.tests.DocExampleProcess
   :docstring:
   :skiplines: 1

会屈服

class pywps.tests.DocExampleProcess[源代码]

doc_example_process_identifier 进程标题(v4.0)

多行进程摘要。

参数
  • literal_input (integer, optional, units:[meters, feet]) -- 文字输入值抽象。

  • date_input ({'2000-01-01', '2018-01-01'}) -- 如果没有提供摘要,则显示标题。

  • complex_input (application/json, application/x-netcdf) -- 复杂输入抽象。

  • bb_input ([EPSG:4326]) -- 边框输入标题 (EPSG.io

返回

  • literal_output布尔 )--布尔输出抽象。

  • complex_output (text/plain) -- Complex output

  • bb_output ( [[EPSG:4326 ]] )--边框输出标题

引用

提示

这是可以按照numpy docstring约定添加的附加文档。

这个 docstring 选项获取 Process docstring并将其附加到引用节之后。可以使用 skiplines 选择权。

使用 autoprocess 指令,首次添加 'sphinx.ext.napoleon''pywps.ext_autodoc' 到sphinx配置文件中的扩展名列表 conf.py . 然后,插入 autoprocess 文档源文件中的指令,就像使用 autoclass 指示,并生成文档。

注意,对于输入和输出参数, title 仅在没有时显示 abstract 定义。换句话说,如果两者都是 titleabstract 只有 abstract 将包含在文档中以避免冗余。