Issue 43 (#207)
* squach contents new branch * source code explanations * flake8 * change in requirements * modified: docs/source/code.rst modified: docs/source/plugins.rst * modified: docs/source/code.rst modified: pygeoapi/formatter/csv_.py modified: pygeoapi/provider/geojson.py * changes requred in review code * modified: .travis.yml missing " * change file to use requirements-dev.txt modified: readthedocs.yml * flask_cors removal from requirements.txt * text changes
@@ -107,3 +107,6 @@ local.openapi.yml
|
||||
# misc
|
||||
*.swp
|
||||
.pytest_cache
|
||||
|
||||
#Eclipse project
|
||||
.project
|
||||
|
||||
@@ -5,7 +5,7 @@ sudo: false
|
||||
|
||||
env:
|
||||
global:
|
||||
- PYGEOAPI_CONFIG=pygeoapi-config.yml
|
||||
- PYGEOAPI_CONFIG="$(pwd)/pygeoapi-config.yml"
|
||||
- CPLUS_INCLUDE_PATH=/usr/include/gdal
|
||||
- C_INCLUDE_PATH=/usr/include/gdal
|
||||
|
||||
@@ -47,9 +47,11 @@ before_script:
|
||||
|
||||
script:
|
||||
- pytest --cov=pygeoapi
|
||||
- make -C ./docs html
|
||||
- find . -type f -name "*.py" | xargs flake8
|
||||
- python3 setup.py --long-description | rst2html5.py
|
||||
|
||||
|
||||
after_success:
|
||||
- python3 setup.py sdist bdist_wheel --universal
|
||||
- debuild -b -uc -us
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXPROJ = pygeoapi
|
||||
SOURCEDIR = source
|
||||
BUILDDIR = build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 71 KiB |
@@ -0,0 +1,219 @@
|
||||
.. _code:
|
||||
|
||||
|
||||
|
||||
Code documentation
|
||||
==================
|
||||
|
||||
Top level code documentation. Follow link in section for module/class member information
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
|
||||
.. automodule:: pygeoapi.api
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
|
||||
flask_app
|
||||
---------
|
||||
|
||||
.. automodule:: pygeoapi.flask_app
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
|
||||
.. automodule:: pygeoapi.log
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
|
||||
OpenAPI
|
||||
-------
|
||||
|
||||
.. automodule:: pygeoapi.openapi
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
Plugins
|
||||
-------
|
||||
|
||||
.. note:: Please consult section :ref:`plugins`
|
||||
|
||||
|
||||
.. automodule:: pygeoapi.plugin
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
Utils
|
||||
-----
|
||||
|
||||
.. automodule:: pygeoapi.util
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
Formatter package
|
||||
-----------------
|
||||
|
||||
|
||||
.. automodule:: pygeoapi.formatter
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
Base class
|
||||
^^^^^^^^^^
|
||||
.. automodule:: pygeoapi.formatter.base
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
csv
|
||||
^^^
|
||||
|
||||
.. automodule:: pygeoapi.formatter.csv_
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
Process package
|
||||
---------------
|
||||
|
||||
.. automodule:: pygeoapi.process
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
Base class
|
||||
^^^^^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.process.base
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
hello_world
|
||||
^^^^^^^^^^^
|
||||
|
||||
Hello world example process
|
||||
|
||||
.. automodule:: pygeoapi.process.hello_world
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
.. _data Provider:
|
||||
|
||||
Provider
|
||||
--------
|
||||
|
||||
|
||||
|
||||
.. automodule:: pygeoapi.provider
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
|
||||
Base class
|
||||
^^^^^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.base
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
:special-members:
|
||||
|
||||
|
||||
CSV provider
|
||||
^^^^^^^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.csv_
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
Elastic provider
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.elasticsearch_
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
|
||||
GeoJSON
|
||||
^^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.geojson
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
|
||||
GeoPackage
|
||||
^^^^^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.geopackage
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
|
||||
OGR
|
||||
^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.ogr
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
|
||||
postgresql
|
||||
^^^^^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.postgresql
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
|
||||
|
||||
sqlite
|
||||
^^^^^^
|
||||
|
||||
.. automodule:: pygeoapi.provider.sqlite
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:private-members:
|
||||
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# pygeoapi documentation build configuration file, created by
|
||||
# sphinx-quickstart on Wed Jul 3 06:22:56 2019.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.todo',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.ifconfig',
|
||||
'sphinx.ext.viewcode',
|
||||
'sphinx.ext.doctest',
|
||||
'sphinx.ext.githubpages']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
#
|
||||
# source_suffix = ['.rst', '.md']
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'pygeoapi'
|
||||
copyright = '2019, pygeoapi team'
|
||||
author = 'pygeoapi team'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.6'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = ''
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = None
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This patterns also effect to html_static_path and html_extra_path
|
||||
exclude_patterns = []
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||
todo_include_todos = True
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Custom sidebar templates, must be a dictionary that maps document names
|
||||
# to template names.
|
||||
#
|
||||
# This is required for the alabaster theme
|
||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||
html_sidebars = {
|
||||
'**': [
|
||||
'relations.html', # needs 'show_related': True theme option to display
|
||||
'searchbox.html',
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'pygeoapidoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
#
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#
|
||||
# 'preamble': '',
|
||||
|
||||
# Latex figure (float) alignment
|
||||
#
|
||||
# 'figure_align': 'htbp',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'pygeoapi.tex', 'pygeoapi Documentation',
|
||||
'pygeoapi team', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
(master_doc, 'pygeoapi', 'pygeoapi Documentation',
|
||||
[author], 1)
|
||||
]
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
(master_doc, 'pygeoapi', 'pygeoapi Documentation',
|
||||
author, 'pygeoapi', 'pygeoapi provides an API to geospatial data',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
@@ -0,0 +1,22 @@
|
||||
.. _configuration:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
pygeoapi uses a yaml file as configuration source and the file location is read from the ``PYGEOAPI_CONFIG`` env variable
|
||||
|
||||
.. note::
|
||||
pygeoapi is under high development, and new configuration paramenters are constantely being added. For the lastest parameters
|
||||
please consult the `pygeoapi-config.yml <https://github.com/geopython/pygeoapi/blob/master/pygeoapi-config.yml>`_ file provided on github
|
||||
|
||||
|
||||
|
||||
Using ``pygeoapi-config.yml`` as reference we will have the following sections:
|
||||
|
||||
* `server` for server related configurations
|
||||
* `logging` for logging configuration
|
||||
* `metadata` server and content metadata (information used to populate multiple content)
|
||||
* `datasets` data content offered by server (collections in WFS3.0)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
.. _docker:
|
||||
|
||||
Docker
|
||||
======
|
||||
|
||||
Docker Images ``geopython/pygeoapi:latest`` and versions are `available on DockerHub <https://hub.docker.com/r/geopython/pygeoapi>`_ .
|
||||
|
||||
Each Docker Image contains a default configuration ``default.config.yml``
|
||||
with the project's test data and WFS3 datasets.
|
||||
|
||||
You can override this default config via Docker Volume mapping or by extending the Docker Image
|
||||
and copying in your config.
|
||||
|
||||
See an `example for the geoapi demo server <https://github.com/geopython/demo.pygeoapi.io/tree/master/services/pygeoapi>`_
|
||||
for the latter method.
|
||||
|
||||
`<https://github.com/geopython/demo.pygeoapi.io/tree/master/services>`_
|
||||
Depending on your config you may need specific backends to be available.
|
||||
|
||||
Running - Basics
|
||||
----------------
|
||||
|
||||
By default this Image will start a pygeoapi Docker Container
|
||||
using ``gunicorn`` on internal port 80.
|
||||
|
||||
To run with default built-in config and data:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
docker run -p 5000:80 -it geopython/pygeoapi run
|
||||
# or simply
|
||||
docker run -p 5000:80 -it geopython/pygeoapi
|
||||
|
||||
|
||||
then browse to **http://localhost:5000**
|
||||
|
||||
You can also run all unit tests to verify:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
docker run -it geopython/pygeoapi test
|
||||
|
||||
|
||||
Running - Overriding the default config
|
||||
---------------------------------------
|
||||
|
||||
Normally you would override the ``default.config.yml`` with your own ``pygeoapi`` config.
|
||||
This can be effected best via Docker Volume Mapping.
|
||||
|
||||
For example if your config is in ``my.config.yml``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
docker run -p 5000:80 -v $(pwd)/my.config.yml:/pygeoapi/local.config.yml -it geopython/pygeoapi
|
||||
|
||||
|
||||
But better/cleaner is to use ``docker-compose``. Something like:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
version: "3"
|
||||
services:
|
||||
pygeoapi:
|
||||
image: geopython/pygeoapi:latest
|
||||
volumes:
|
||||
- ./my.config.yml:/pygeoapi/local.config.yml
|
||||
|
||||
Or you can create a ``Dockerfile`` extending the base Image and **COPY** in your config:
|
||||
|
||||
|
||||
.. code-block:: dockerfile
|
||||
|
||||
FROM geopython/pygeoapi:latest
|
||||
COPY ./my.config.yml /pygeoapi/local.config.yml
|
||||
|
||||
|
||||
See how the demo server is setup (`here <https://github.com/geopython/demo.pygeoapi.io/tree/master/services/pygeoapi_master>`_)
|
||||
|
||||
Running - Running on a sub-path
|
||||
-------------------------------
|
||||
|
||||
By default the ``pygeoapi`` Docker Image will run from the ``root`` path ``/``.
|
||||
If you need to run from a sub-path and have all internal URLs correct
|
||||
you need to set ``SCRIPT_NAME`` environment variable.
|
||||
|
||||
For example to run with ``my.config.yml`` on ``http://localhost:5000/mypygeoapi``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
docker run -p 5000:80 -e SCRIPT_NAME='/mypygeoapi' -v $(pwd)/my.config.yml:/pygeoapi/local.config.yml -it geopython/pygeoapi
|
||||
|
||||
|
||||
browse to **http://localhost:5000/mypygeoapi**
|
||||
|
||||
Or within a ``docker-compose.yml`` full example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
version: "3"
|
||||
services:
|
||||
pygeoapi:
|
||||
image: geopython/pygeoapi:latest
|
||||
volumes:
|
||||
- ./my.config.yml:/pygeoapi/local.config.yml
|
||||
ports:
|
||||
- "5000:80"
|
||||
environment:
|
||||
- SCRIPT_NAME=/pygeoapi
|
||||
|
||||
|
||||
See `pygeoapi demo service <https://github.com/geopython/demo.pygeoapi.io/tree/master/services/pygeoapi_master>`_ for an full example.
|
||||
@@ -0,0 +1,43 @@
|
||||
.. pygeoapi documentation master file, created by
|
||||
sphinx-quickstart on Wed Jul 3 06:22:56 2019.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to pygeoapi's documentation!
|
||||
====================================
|
||||
|
||||
|build-status| |docs| |coverage|
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
:caption: Contents:
|
||||
|
||||
pygeoapi
|
||||
install
|
||||
openapi
|
||||
docker
|
||||
wsgi
|
||||
configuration
|
||||
plugins
|
||||
code
|
||||
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
|
||||
.. |build-status| image:: https://api.travis-ci.org/geopython/pygeoapi.svg?branch=master
|
||||
:alt: build status
|
||||
:target: https://travis-ci.org/geopython/pygeoapi
|
||||
|
||||
.. |docs| image:: https://readthedocs.org/projects/pygeoapi/badge/?version=latest&style=plastic
|
||||
:alt: Documentation Status
|
||||
:target: https://readthedocs.org/projects/pygeoapi
|
||||
|
||||
.. |coverage| image:: https://codecov.io/gh/geopython/pygeoapi/branch/master/graph/badge.svg
|
||||
:alt: Test coverage
|
||||
:target: https://codecov.io/gh/pygeoapi/pygeoapi.org
|
||||
@@ -0,0 +1,54 @@
|
||||
.. _install:
|
||||
|
||||
Install
|
||||
=======
|
||||
|
||||
pygeoapi is nativally run as a Flask app (the code struct is an API and Flask is used as a wrapper).
|
||||
|
||||
pygeoapi uses two configuration files: **pygeoapi-config.yml** and **openapi.yml**. First configuration contains all the information and setup to run pygeoapi while the second is exclusivally for the openapi.
|
||||
|
||||
pygeoapi requires setting ``PYGEOAPI_CONFIG`` and ``PYGEOAPI_OPENAPI`` env variable. ``PYGEOAPI_CONFIG`` points to the yaml file containing the configuration, in the example
|
||||
bellow we copy the ``local.config.yml`` default configuration to ``pygeoapi-config.yml`` and use this configuration file.
|
||||
|
||||
``PYGEOAPI_OPENAPI`` variable is the path to openapi file configuration, this file **needs to be autogenerated** using the ``pygeoapi generate-openapi-document`` command and
|
||||
the pygeoapi config files e.g: ``pygeoapi generate-openapi-document -c local.config.yml > openapi.yml``. And then setting the env variable to the path:
|
||||
``export PYGEOAPI_OPENAPI=/path/to/openapi.yml``
|
||||
|
||||
For production environments it is recommended to use :ref:`docker` or a specialized WSGI HTTP server :ref:`wsgi`
|
||||
|
||||
Copy/paste install
|
||||
------------------
|
||||
|
||||
It is advisable to run pygeoapi inside an isolated enviroment either *virtualenv* or *docker*, mainly to avoid python package conflicts.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
#create virtualenv
|
||||
virtualenv -p python pygeoapi
|
||||
cd pygeoapi
|
||||
. bin/activate
|
||||
git clone https://github.com/geopython/pygeoapi.git
|
||||
cd pygeoapi
|
||||
|
||||
#install requirements
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
# install source in current directory
|
||||
pip install -e .
|
||||
cp pygeoapi-config.yml local.config.yml
|
||||
#edit configuration file
|
||||
nano local.config.yml
|
||||
|
||||
export PYGEOAPI_CONFIG=/path/to/local.config.yml
|
||||
# generate OpenAPI Document
|
||||
pygeoapi generate-openapi-document -c local.config.yml > openapi.yml
|
||||
export PYGEOAPI_OPENAPI=/path/to/openapi.yml
|
||||
|
||||
#Run pygeoapi
|
||||
pygeoapi serve
|
||||
|
||||
|
||||
If the default configuration was used then we should have pygeoapi running on
|
||||
|
||||
.. image:: /_static/openapi_intro_page.png
|
||||
@@ -0,0 +1,64 @@
|
||||
.. _openapi:
|
||||
|
||||
OpenAPI
|
||||
=======
|
||||
|
||||
|
||||
`OpenAPI spec <https://swagger.io/docs/specification/about/>`_ is an open specification for REST end points, currentely OGC services are being redefined using such specification.
|
||||
The REST structure and payload are defined using yaml file structures, the file structure is described here: `<https://swagger.io/docs/specification/basic-structure/>`_
|
||||
|
||||
pygeoapi REST end points descriptions on OpenAPI standard are automatically generated based on the configuration file:
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pygeoapi generate-openapi-document -c local.config.yml > openapi.yml
|
||||
|
||||
|
||||
The api will them be accessible at `/api` endpoint.
|
||||
|
||||
For api demo please check: `<https://demo.pygeoapi.io/master/api>`_
|
||||
|
||||
The api page has REST description but also integrated clients that can be used to send requests to the REST end points and see the response provided
|
||||
|
||||
|
||||
Using OpenAPI
|
||||
-------------
|
||||
|
||||
Acessing the openAPI webpage we have the following structure:
|
||||
|
||||
.. image:: /_static/openapi_intro_page.png
|
||||
|
||||
Please notice that **each dataset** will be represented as a REST end point under `collections`
|
||||
|
||||
|
||||
In this example we will test and `GET` data concerning windmills in the Netherlands, first we will check the avaiable datasets,
|
||||
by accessing the service's collections:
|
||||
|
||||
|
||||
.. image:: /_static/openapi_get_collections.png
|
||||
|
||||
The service collection metadata will contain a description of the collections provided by the server
|
||||
|
||||
.. image:: /_static/openapi_get_collections_result.png
|
||||
|
||||
The dataset `dutch_windmills` will be available on the `collections` end point, in the following example we'll obtain the specific metadata of the dataset
|
||||
|
||||
.. image:: /_static/openapi_get_collection.png
|
||||
|
||||
.. image:: /_static/openapi_get_collection_result.png
|
||||
|
||||
|
||||
features/items composing the data are agregated on the `/items` end point, in this REST end point it is possible to obtain all dataset, or restrict
|
||||
it features/items to a **numerical limit**, **bounding box**, **time stamp**, **pagging** (start index)
|
||||
|
||||
.. image:: /_static/openapi_get_item.png
|
||||
|
||||
For each feature in dataset we have a **specific identifier** (notice that the identifier is not part of the JSON properties),
|
||||
|
||||
.. image:: /_static/openapi_get_item_id.png
|
||||
|
||||
This identifier can be used to obtain a specific item from the dataset using the `items\{id}` end point as follows:
|
||||
|
||||
.. image:: /_static/openapi_get_item_id2.png
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
.. _plugins:
|
||||
|
||||
Plugins
|
||||
=======
|
||||
|
||||
In this section we will explain how pygeoapi uses a plugin approach for data providers, formatters and processes.
|
||||
|
||||
Plugin data provider plugin
|
||||
---------------------------
|
||||
|
||||
Plugins are in general modules containing derived classed classes that ensure minimal requirements for the plugin to work.
|
||||
Lets consider the steps for a data provider plugin (source code is located here: :ref:`data Provider`)
|
||||
|
||||
#. create a new module file on the `provider folder` (e.g myprovider.py)
|
||||
#. copy code from `base.py`
|
||||
#. import base provider class
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from pygeoapi.provider.base import BaseProvider
|
||||
|
||||
#. create a child class from the `BaseProvider` class with a specific name
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class BaseProvider(object):
|
||||
"""generic Provider ABC"""
|
||||
|
||||
def __init__(self, provider_def):
|
||||
"""
|
||||
Initialize object
|
||||
|
||||
to become:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyDataProvider(object):
|
||||
"""My data provider"""
|
||||
|
||||
def __init__(self, provider_def):
|
||||
"""Inherit from parent class"""
|
||||
BaseProvider.__init__(self, provider_def)
|
||||
|
||||
|
||||
|
||||
#. implement class methods.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def query(self):
|
||||
|
||||
def get(self, identifier):
|
||||
|
||||
def create(self, new_feature):
|
||||
|
||||
def update(self, identifier, new_feature):
|
||||
|
||||
def delete(self, identifier):
|
||||
|
||||
|
||||
The above class methods are related to the specific URLs defined on the OGC openapi specification:
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
.. _pygeoapi:
|
||||
|
||||
pygeoapi project
|
||||
================
|
||||
|
||||
pygeoapi is a Python server implementation of the emerging suite of OGC API standards. pygeoapi is development
|
||||
using the `12-factor app <https://12factor.net/>`_ approach, allowing for easy deployment in cloud systems.
|
||||
|
||||
pygeoapi is a OsGEO recognized project (`here <https://www.osgeo.org/projects/pygeoapi/>`_).
|
||||
|
||||
Demo server
|
||||
-----------
|
||||
|
||||
There is a demo server on `<https://demo.pygeoapi.io>`_ running the latest (Docker) version from the master branch of this repo. pygeoapi runs there at `https://demo.pygeoapi.io/master <https://demo.pygeoapi.io/master>`_ .
|
||||
|
||||
The demo server setup and config is maintained within a seperate GH repo: `<https://github.com/geopython/demo.pygeoapi.io>`_.
|
||||
@@ -0,0 +1,48 @@
|
||||
.. _wsgi:
|
||||
|
||||
WSGI
|
||||
====
|
||||
|
||||
Web Server Gateway Interface (WSGI) is standard for forwarding request to web applications written on Python language. pygeoapi it self
|
||||
doesn't implement WSGI since it is an API,
|
||||
therefore it is required a webframework to access HTTP requests and pass the information to pygeopai
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
HTTP request --> Flask (flask_app.py) --> pygeopai API
|
||||
|
||||
|
||||
the pygeoapi package integrates `Flask <https://flask.palletsprojects.com/en/1.1.x/>`_ as webframework for defining the API routes/end points and WSGI support.
|
||||
|
||||
The flask WSGI server can be easely run as a pygeoapi command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
pygeoapi serve
|
||||
|
||||
Running a native Flask server is not adivsable, the prefered option is as follows:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
HTTP request --> WSGI server (gunicorn) --> Flask (flask_app.py) --> pygeopai API
|
||||
|
||||
By having a specific WSGI server, the HTTPS are efficiently processed into threads/processes. The current docker pygeoapi
|
||||
implement such strategy (see section: :ref:`docker`), it is prefered to implement pygeopai using docker solutions than running host native WSGI servers.
|
||||
|
||||
|
||||
Running gunicorn
|
||||
----------------
|
||||
|
||||
Gunicorn is one of several WSGI supporting server on python (list of server supporting WSGI: `here <https://wsgi.readthedocs.io/en/latest/servers.html>`_). This server
|
||||
is simple to run from the command, e.g:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
gunicorn pygeoapi.flask_app:APP
|
||||
|
||||
For extra configuration parameters like port binding, workers please consult the gunicorn `settings <http://docs.gunicorn.org/en/stable/settings.html>`_
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# =================================================================
|
||||
""" Root level code of pygeoapi, parsing content provided by webframework.
|
||||
Returns content from plugins and sets reponses
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import json
|
||||
@@ -45,11 +48,13 @@ LOGGER = logging.getLogger(__name__)
|
||||
TEMPLATES = '{}{}templates'.format(os.path.dirname(
|
||||
os.path.realpath(__file__)), os.sep)
|
||||
|
||||
#: Return headers for requests (e.g:X-Powered-By)
|
||||
HEADERS = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Powered-By': 'pygeoapi {}'.format(__version__)
|
||||
}
|
||||
|
||||
#: Formats allowed for ?f= requests
|
||||
FORMATS = ['json', 'html']
|
||||
|
||||
|
||||
@@ -833,9 +838,9 @@ def check_format(args, headers):
|
||||
|
||||
def to_json(dict_):
|
||||
"""
|
||||
serialize dict to json
|
||||
Serialize dict to json
|
||||
|
||||
:param dict_: dict_
|
||||
:param dict_: `dict` of JSON representation
|
||||
|
||||
:returns: JSON string representation
|
||||
"""
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#
|
||||
# =================================================================
|
||||
|
||||
""" Flask module providing the route paths to the api"""
|
||||
|
||||
import os
|
||||
|
||||
import click
|
||||
@@ -61,6 +63,11 @@ api_ = API(CONFIG)
|
||||
|
||||
@APP.route('/')
|
||||
def root():
|
||||
"""
|
||||
HTTP root content of pygeopai. Intro page access point
|
||||
|
||||
:returns: HTTP response
|
||||
"""
|
||||
headers, status_code, content = api_.root(request.headers, request.args)
|
||||
|
||||
response = make_response(content, status_code)
|
||||
@@ -72,6 +79,11 @@ def root():
|
||||
|
||||
@APP.route('/api')
|
||||
def api():
|
||||
"""
|
||||
OpenAPI access point
|
||||
|
||||
:returns: HTTP response
|
||||
"""
|
||||
with open(os.environ.get('PYGEOAPI_OPENAPI')) as ff:
|
||||
openapi = yaml_load(ff)
|
||||
|
||||
@@ -87,6 +99,12 @@ def api():
|
||||
|
||||
@APP.route('/conformance')
|
||||
def api_conformance():
|
||||
"""
|
||||
OGC open api conformance access point
|
||||
|
||||
:returns: HTTP response
|
||||
"""
|
||||
|
||||
headers, status_code, content = api_.api_conformance(request.headers,
|
||||
request.args)
|
||||
|
||||
@@ -100,6 +118,13 @@ def api_conformance():
|
||||
@APP.route('/collections')
|
||||
@APP.route('/collections/<name>')
|
||||
def describe_collections(name=None):
|
||||
"""
|
||||
OGC open api collections access point
|
||||
|
||||
:param name: identifier of collection name
|
||||
:returns: HTTP response
|
||||
"""
|
||||
|
||||
headers, status_code, content = api_.describe_collections(
|
||||
request.headers, request.args, name)
|
||||
|
||||
@@ -113,6 +138,12 @@ def describe_collections(name=None):
|
||||
@APP.route('/collections/<feature_collection>/items')
|
||||
@APP.route('/collections/<feature_collection>/items/<feature>')
|
||||
def dataset(feature_collection, feature=None):
|
||||
"""
|
||||
OGC open api collections/{dataset}/items/{feature} access point
|
||||
|
||||
:returns: HTTP response
|
||||
"""
|
||||
|
||||
if feature is None:
|
||||
headers, status_code, content = api_.get_features(
|
||||
request.headers, request.args, feature_collection)
|
||||
@@ -131,6 +162,12 @@ def dataset(feature_collection, feature=None):
|
||||
@APP.route('/processes')
|
||||
@APP.route('/processes/<name>')
|
||||
def describe_processes(name=None):
|
||||
"""
|
||||
OGC open api processes access point (experimental)
|
||||
|
||||
:param name: identifier of process to describe
|
||||
:returns: HTTP response
|
||||
"""
|
||||
headers, status_code, content = api_.describe_processes(
|
||||
request.headers, request.args, name)
|
||||
|
||||
@@ -144,6 +181,13 @@ def describe_processes(name=None):
|
||||
|
||||
@APP.route('/processes/<name>/jobs', methods=['GET', 'POST'])
|
||||
def execute_process(name=None):
|
||||
"""
|
||||
OGC open api jobs from processes access point (experimental)
|
||||
|
||||
:param name: identifier of process to execute
|
||||
:returns: HTTP response
|
||||
"""
|
||||
|
||||
if request.method == 'GET':
|
||||
headers, status_code, content = ({}, 200, "[]")
|
||||
elif request.method == 'POST':
|
||||
@@ -162,7 +206,14 @@ def execute_process(name=None):
|
||||
@click.pass_context
|
||||
@click.option('--debug', '-d', default=False, is_flag=True, help='debug')
|
||||
def serve(ctx, debug=False):
|
||||
"""Serve pygeoapi via Flask"""
|
||||
"""
|
||||
Serve pygeoapi via Flask. Runs pygeoapi
|
||||
as a flask server. Not recommend for production.
|
||||
|
||||
:param debug: `bool` of whether to run in debug mode
|
||||
:returns void
|
||||
|
||||
"""
|
||||
|
||||
# setup_logger(CONFIG['logging'])
|
||||
APP.run(debug=True, host=api_.config['server']['bind']['host'],
|
||||
|
||||
@@ -26,3 +26,5 @@
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# =================================================================
|
||||
|
||||
"""Output formatter package"""
|
||||
|
||||
@@ -46,7 +46,7 @@ class CSVFormatter(BaseFormatter):
|
||||
|
||||
:param formatter_def: formatter definition
|
||||
|
||||
:returns: pygeoapi.formatter.csv_.CSVFormatter
|
||||
:returns: `pygeoapi.formatter.csv_.CSVFormatter`
|
||||
"""
|
||||
|
||||
geom = False
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#
|
||||
# =================================================================
|
||||
|
||||
"""Logging system"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
@@ -26,12 +26,15 @@
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# =================================================================
|
||||
"""Plugin loader"""
|
||||
|
||||
import importlib
|
||||
import logging
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
#: Loads provider plugins to be used by pygeoapi,\
|
||||
#: formatters and processes available
|
||||
PLUGINS = {
|
||||
'provider': {
|
||||
'CSV': 'pygeoapi.provider.csv_.CSVProvider',
|
||||
|
||||
@@ -26,3 +26,5 @@
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# =================================================================
|
||||
|
||||
"""OGC process package, each process is an independent module"""
|
||||
|
||||
@@ -33,7 +33,7 @@ LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BaseProcessor(object):
|
||||
"""generic Processor ABC"""
|
||||
"""generic Processor ABC. Processes are inherited from this class"""
|
||||
|
||||
def __init__(self, processor_def, process_metadata):
|
||||
"""
|
||||
|
||||
@@ -33,6 +33,7 @@ from pygeoapi.process.base import BaseProcessor
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
#: Process metadata and description
|
||||
PROCESS_METADATA = {
|
||||
'version': '0.1.0',
|
||||
'id': 'hello-world',
|
||||
@@ -80,7 +81,7 @@ PROCESS_METADATA = {
|
||||
|
||||
|
||||
class HelloWorldProcessor(BaseProcessor):
|
||||
"""Hello World Processor"""
|
||||
"""Hello World Processor example"""
|
||||
|
||||
def __init__(self, provider_def):
|
||||
"""
|
||||
|
||||
@@ -26,3 +26,5 @@
|
||||
# OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# =================================================================
|
||||
|
||||
"""Provider module containing the plugins wrapping data sources"""
|
||||
|
||||
@@ -52,14 +52,14 @@ class GeoJSONProvider(BaseProvider):
|
||||
and will override any 'id' provided in the original data.
|
||||
The feature 'properties' will be preserved.
|
||||
|
||||
TODO
|
||||
- query method should take bbox
|
||||
- instead of methods returning FeatureCollections,
|
||||
we should be yielding Features and aggregating in the view
|
||||
- there are strict id semantics; all features in the input GeoJSON file
|
||||
must be present and be unique strings. Otherwise it will break.
|
||||
- How to raise errors in the provider implementation such that
|
||||
appropriate HTTP responses will be raised
|
||||
TODO:
|
||||
* query method should take bbox
|
||||
* instead of methods returning FeatureCollections,
|
||||
we should be yielding Features and aggregating in the view
|
||||
* there are strict id semantics; all features in the input GeoJSON file
|
||||
must be present and be unique strings. Otherwise it will break.
|
||||
* How to raise errors in the provider implementation such that
|
||||
* appropriate HTTP responses will be raised
|
||||
"""
|
||||
|
||||
def __init__(self, provider_def):
|
||||
@@ -73,6 +73,7 @@ class GeoJSONProvider(BaseProvider):
|
||||
Yes loading from disk, deserializing and validation
|
||||
happens on every request. This is not efficient.
|
||||
"""
|
||||
|
||||
if os.path.exists(self.data):
|
||||
with open(self.data) as src:
|
||||
data = json.loads(src.read())
|
||||
@@ -104,6 +105,7 @@ class GeoJSONProvider(BaseProvider):
|
||||
|
||||
:returns: FeatureCollection dict of 0..n GeoJSON features
|
||||
"""
|
||||
|
||||
# TODO filter by bbox without resorting to third-party libs
|
||||
data = self._load()
|
||||
|
||||
@@ -124,6 +126,7 @@ class GeoJSONProvider(BaseProvider):
|
||||
:param identifier: feature id
|
||||
:returns: dict of single GeoJSON feature
|
||||
"""
|
||||
|
||||
all_data = self._load()
|
||||
for feature in all_data['features']:
|
||||
if str(feature['properties'][self.id_field]) == identifier:
|
||||
@@ -154,6 +157,7 @@ class GeoJSONProvider(BaseProvider):
|
||||
:param identifier: feature id
|
||||
:param new_feature: new GeoJSON feature dictionary
|
||||
"""
|
||||
|
||||
all_data = self._load()
|
||||
for i, feature in enumerate(all_data['features']):
|
||||
if feature['properties']['id'] == identifier:
|
||||
@@ -170,6 +174,7 @@ class GeoJSONProvider(BaseProvider):
|
||||
|
||||
:param identifier: feature id
|
||||
"""
|
||||
|
||||
all_data = self._load()
|
||||
for i, feature in enumerate(all_data['features']):
|
||||
if feature['properties']['id'] == identifier:
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#
|
||||
# =================================================================
|
||||
|
||||
"""Generic util functions used in the code"""
|
||||
|
||||
import logging
|
||||
|
||||
import yaml
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/source/conf.py
|
||||
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
formats: all
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
version: 3.7
|
||||
install:
|
||||
- requirements: requirements.txt
|
||||
- requirements: requirements-dev.txt
|
||||
@@ -20,3 +20,6 @@ pypandoc
|
||||
pyOpenSSL
|
||||
ndg-httpsclient
|
||||
pyasn1
|
||||
|
||||
#Read the docs
|
||||
sphinx_rtd_theme
|
||||
|
||||