自定义pygeoapi:插件

在本节中,我们将解释pygeoapi如何为数据提供者、格式化程序和进程提供插件架构。

插件开发需要了解如何用Python编程以及Python的包/模块系统。

概述

pygeoapi提供了一个健壮的插件架构,使开发人员能够扩展功能。事实上,pygeoapi本身实现了许多格式、数据提供者和过程功能作为插件。

pygeoapi体系结构支持以下子系统:

  • 数据提供者

  • 输出格式

  • 过程

  • 流程管理器

核心pygeoapi插件注册表位于 pygeoapi.plugin.PLUGINS .

每个插件类型实现其相关基类作为API协定:

  • 数据提供程序: pygeoapi.provider.base

  • 输出格式: pygeoapi.formatter.base

  • 过程: pygeoapi.process.base

  • 进程管理器(_M): pygeoapi.process.manager.base

待处理

将插件链接到API文档

插件可以在pygeoapi代码库之外开发,并通过pygeoapi配置动态加载。这使您的自定义插件可以在pygeoapi之外运行,以便于软件更新的维护。

备注

建议将pygeoapi插件存储在pygeoapi之外,以便于软件更新和包管理

将插件连接到Pourgeapi

以下方法是将插件连接到pygeapi的选项:

Option 1 :在pygeapi外部实现并添加到配置中(推荐)

  • 使用插件代码创建一个Python包(请参见 Cookiecutter 作为一个例子)

  • 将此Python包安装到您的系统 (python3 setup.py install )。此时,您的新程序包应该位于 PYTHONPATH 您的Pygeapi安装的

  • 将主插件类指定为 name 在pygeapi配置中的相关类型。例如,对于新的矢量数据提供程序:

providers:
    - type: feature
      # name may refer to an external Python class, that is loaded by pygeoapi at runtime
      name: mycooldatapackage.mycoolvectordata.MyCoolVectorDataProvider
      data: /path/to/file
      id_field: stn_id

指定自定义的pyGeoapi CLI命令

第三方插件也可以提供定制的CLI命令。这可以通过另外两个步骤来完成:

  1. 使用单击创建您的CLI命令

  2. 在您的插件中 setup.pypyproject.toml 文件中,请为 pygeoapi 指向您的Click CLI命令或组的组。

作为一个简单的例子,让我们假设您开发了一个名为 myplugin ,它有一个 cli.py 包含以下内容的模块:

# module: myplugin.cli
import click

@click.command(name="super-command")
def my_cli_command():
    print("Hello, this is my custom pygeoapi CLI command!")

然后,在您的插件中 setup.py 文件中,指定入口点部分:

# file: setup.py
entry_points={
    'pygeoapi': ['my-plugin = myplugin.cli:my_cli_command']
}

或者,如果使用 pyproject.toml 而不是文件:

# file: pyproject.toml
# Noter that this example uses poetry, other Python projects may differ in
# how they expect entry_points to be specified
[tool.poetry.plugins."pygeoapi"]
my-plugin = 'myplugin.cli:my_cli_command'

安装此插件后,您现在应该能够通过运行以下命令来调用CLI命令:

$ pygeoapi plugins super-command
Hello, this is my custom pygeoapi CLI command!

备注

美国地质调查局(United States Geological Survey)创建了一个Cookiecutter项目,用于创建PyGeoapi插件。请参阅 pygeoapi-plugin-cookiecutter 项目开始。

Option 2 :更新核心比利亚皮:

  • 将您的插件代码复制到pygeapi源代码目录中-例如,如果它是提供程序插件,则将其复制到 pygeoapi/provider

  • 更新中的插件注册表 pygeoapi/plugin.py:PLUGINS['provider'] 使用插件的短名称(例如 MyCoolVectorData )和指向类的虚线路径(即 pygeoapi.provider.mycoolvectordata.MyCoolVectorDataProvider )

  • 在数据集提供程序配置中按如下方式指定:

providers:
    - type: feature
      # name may also refer to a known core pygeopai plugin
      name: MyCoolVectorData
      data: /path/to/file
      id_field: stn_id

自定义pygeapi进程管理器

也可以定制pygeapi进程管理器。与提供程序插件类似,您可以使用pygeapi配置的 server.manager.name 以指示指向Python包和相关管理器类的虚线路径( i.e. 类似于上面的选项1)或已知的核心pygeapi插件的名称( i.e. ,类似于上面的备选方案2)。

示例:自定义pygeoapi矢量数据提供程序

让我们考虑向量数据提供程序插件的步骤(源代码位于: 供应商

Python代码

下面的模板提供了一个最小的示例(让我们调用该文件 mycoolvectordata.py

from pygeoapi.provider.base import BaseProvider

class MyCoolVectorDataProvider(BaseProvider):
    """My cool vector data provider"""

    def __init__(self, provider_def):
        """Inherit from parent class"""

        super().__init__(provider_def)

    def get_fields(self):

        # open dat file and return fields and their datatypes
        return {
            'field1': 'string',
            'field2': 'string'
        }

    def query(self, offset=0, limit=10, resulttype='results',
              bbox=[], datetime_=None, properties=[], sortby=[],
              select_properties=[], skip_geometry=False, **kwargs):

        # optionally specify the output filename pygeoapi can use as part
        # of the response (HTTP Content-Disposition header)
        self.filename = "my-cool-filename.dat"

        # open data file (self.data) and process, return
        return {
            'type': 'FeatureCollection',
            'features': [{
                'type': 'Feature',
                'id': '371',
                'geometry': {
                    'type': 'Point',
                    'coordinates': [ -75, 45 ]
                },
                'properties': {
                    'stn_id': '35',
                    'datetime': '2001-10-30T14:24:55Z',
                    'value': '89.9'
                }
            }]
        }

    def get_schema():
        # return a `dict` of a JSON schema (inline or reference)
        return ('application/geo+json', {'$ref': 'https://geojson.org/schema/Feature.json'})

为了简洁起见,上面的代码将始终返回数据集的单个特性。实际上,插件开发人员将连接到具有运行查询和返回相关结果集的功能的数据源,并实现 get 相应的方法。只要插件实现了它的基本提供者的API契约,所有其他功能都留给提供者实现。

每个基类记录实现所需的函数、参数和返回类型。

备注

您可以使用将语言支持添加到您的插件 these guides

备注

您可以让Pourgeapi核心为 crs 查询使用 @crs_transform 装饰师开着 query()get() 方法:研究方法。看见 CRS支持

示例:自定义pygeoapi栅格数据提供程序

让我们考虑栅格数据提供程序插件的步骤(源代码位于: 供应商

Python代码

下面的模板提供了一个最小的示例(让我们调用该文件 mycoolrasterdata.py

from pygeoapi.provider.base import BaseProvider

class MyCoolRasterDataProvider(BaseProvider):
    """My cool raster data provider"""

    def __init__(self, provider_def):
        """Inherit from parent class"""

        super().__init__(provider_def)
        self.num_bands = 4
        self.axes = ['Lat', 'Long']

    def get_coverage_domainset(self):
        # return a CIS JSON DomainSet

    def get_coverage_rangetype(self):
        # return a CIS JSON RangeType

    def query(self, bands=[], subsets={}, format_='json', **kwargs):
        # process bands and subsets parameters
        # query/extract coverage data

        # optionally specify the output filename pygeoapi can use as part
        of the response (HTTP Content-Disposition header)
        self.filename = "my-cool-filename.dat"

        if format_ == 'json':
            # return a CoverageJSON representation
            return {'type': 'Coverage', ...}  # trimmed for brevity
        else:
            # return default (likely binary) representation
            return bytes(112)

为了简洁起见,上面的代码对于元数据总是JSON,对于数据总是二进制或CoverageJSON。实际上,插件开发人员将连接到一个具有运行查询和返回相关结果集功能的数据源,只要插件实现其基本提供者的API契约,所有其他功能都留给提供者实现。

每个基类记录实现所需的函数、参数和返回类型。

示例:自定义pygeoapi格式化程序

Python代码

下面的模板提供了一个最小的示例(让我们调用该文件 mycooljsonformat.py

import json
from pygeoapi.formatter.base import BaseFormatter

class MyCoolJSONFormatter(BaseFormatter):
    """My cool JSON formatter"""

    def __init__(self, formatter_def):
        """Inherit from parent class"""

        super().__init__({'name': 'cooljson', 'geom': None})
        self.mimetype = 'application/json; subtype:mycooljson'

    def write(self, options={}, data=None):
        """custom writer"""

        out_data {'rows': []}

        for feature in data['features']:
            out_data.append(feature['properties'])

        return out_data

正在处理插件

处理插件遵循OGC API-进程开发。鉴于规范正在开发中,在 pygeoapi/process/hello_world.py 暂时提供了一个合适的例子。