├── logs └── .placelholder ├── VERSION.txt ├── outputs └── .placeholder ├── processes ├── __init__.py ├── bboxinout.py ├── jsonprocess.py ├── sayhello.py ├── ultimate_question.py ├── area.py ├── grassbuffer.py ├── buffer.py ├── feature_count.py ├── centroids.py └── sleep.py ├── workdir └── .placeholder ├── requirements-dev.txt ├── docker ├── ubuntu │ ├── nginx │ │ ├── nginx_service.sh │ │ ├── pywps4_service.sh │ │ ├── pywps.conf │ │ └── Dockerfile │ └── flask │ │ ├── pywps4_service.sh │ │ └── Dockerfile ├── alpine │ ├── gunicorn │ │ ├── run_all.sh │ │ └── Dockerfile │ ├── nginx │ │ ├── docker-compose.yml │ │ ├── Dockerfile │ │ └── pywps.conf │ └── flask │ │ └── Dockerfile └── isolation │ └── Dockerfile ├── MANIFEST.in ├── docs ├── _static │ └── pywps.png ├── README.md ├── directories.rst ├── configuration.rst ├── processes.rst ├── index.rst ├── flask.rst ├── make.bat ├── Makefile └── conf.py ├── tox.ini ├── requirements.txt ├── .gitignore ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .travis.yml ├── tests ├── test_log.py ├── __init__.py ├── test_describe.py ├── test_capabilities.py ├── test_requests.py ├── common.py └── test_execute.py ├── static ├── pics │ ├── PyWPS-Execute-red.svg │ ├── PyWPS-DescribeProcess-green.svg │ └── PyWPS-GetCapabilities-brightgreen.svg ├── data │ └── point.gml └── requests │ ├── execute_buffer_post_referenceinput.xml │ ├── execute_buffer_post.xml │ └── execute_buffer_post_referenceoutput.xml ├── LICENCE.txt ├── wsgi ├── deployment.rst └── pywps.wsgi ├── pywps.cfg ├── templates ├── home.html └── base.html ├── setup.py ├── demo.py └── README.rst /logs/.placelholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 4.2.1 2 | -------------------------------------------------------------------------------- /outputs/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /processes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /workdir/.placeholder: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | flake8 2 | Sphinx 3 | -------------------------------------------------------------------------------- /docker/ubuntu/nginx/nginx_service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | exec nginx -g "daemon off;" -------------------------------------------------------------------------------- /docker/ubuntu/flask/pywps4_service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/python3 /pywps-flask/demo.py -a -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt 2 | recursive-include static 3 | recursive logs 4 | recursive workdir/ 5 | -------------------------------------------------------------------------------- /docs/_static/pywps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/pywps-flask/HEAD/docs/_static/pywps.png -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py36 3 | 4 | [testenv] 5 | deps=lxml 6 | commands= 7 | python tests/__init__.py 8 | -------------------------------------------------------------------------------- /docker/alpine/gunicorn/run_all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | gunicorn -b 0.0.0.0:$GU_PORT --workers $GU_WORKERS --log-syslog --pythonpath /pywps-flask wsgi.pywps_app:application -------------------------------------------------------------------------------- /docker/ubuntu/nginx/pywps4_service.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | gunicorn -b 0.0.0.0:8081 --workers $GU_WORKERS --log-syslog --pythonpath /pywps-flask wsgi.pywps_app:application -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Flask 2 | Jinja2 3 | jsonschema 4 | lxml 5 | OWSLib 6 | pyproj 7 | requests 8 | Shapely 9 | Werkzeug 10 | SQLAlchemy 11 | psutil 12 | pywps 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | workdir/* 3 | outputs/* 4 | logs/* 5 | 6 | # vim, mac os 7 | *.sw* 8 | .DS_Store 9 | .*.un~ 10 | .idea 11 | 12 | # python setup.py build/install artifacts 13 | build 14 | dist 15 | pywps_demo.egg-info/ 16 | 17 | # docs build artifacts 18 | _build/ 19 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # How to build docs 2 | 3 | You need to add several directories to :env:`PYTHONPATH`: PyWPS, GRASS GIS, 4 | PyWPS-Demo:: 5 | 6 | ```bash 7 | export PYTHONPATH=../:../../pywps/:/usr/local/grass-7.3.svn/etc/python/ 8 | ``` 9 | 10 | Than you can run `make html` command and let the docs build 11 | 12 | 13 | -------------------------------------------------------------------------------- /docker/alpine/nginx/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | services: 4 | pywps: 5 | image: pywps/gunicorn-alpine:latest 6 | nginx: 7 | image: pywps/nginx-alpine:latest 8 | build: . 9 | depends_on: 10 | - pywps 11 | ports: 12 | - 80:80 13 | links: 14 | - pywps 15 | -------------------------------------------------------------------------------- /docker/alpine/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.15.7-alpine 2 | MAINTAINER Jorge Samuel Mendes de Jesus 3 | 4 | RUN rm /etc/nginx/conf.d/default.conf 5 | COPY pywps.conf /etc/nginx/conf.d/pywps.conf 6 | 7 | 8 | #Build: docker build -t pywps/nginx-alpine:latest . 9 | #Usage: docker-compose up 10 | #Usage: docker run -p 80:80 -it pywps/nginx-alpine:latest 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | # Environment 4 | 5 | - operating system: 6 | - Python version: 7 | - PyWPS version: 8 | - source/distribution 9 | - [ ] git clone 10 | - [ ] Debian 11 | - [ ] PyPI 12 | - [ ] zip/tar.gz 13 | - [ ] other (please specify): 14 | - web server 15 | - [ ] Apache/mod_wsgi 16 | - [ ] CGI 17 | - [ ] other (please specify): 18 | 19 | # Steps to Reproduce 20 | 21 | # Additional Information 22 | -------------------------------------------------------------------------------- /docs/directories.rst: -------------------------------------------------------------------------------- 1 | .. _files: 2 | 3 | ============================ 4 | File structure of PyWPS-Demo 5 | ============================ 6 | 7 | This chapter describes files and directories structure of PyWPS-Demo and their 8 | relationship to :ref:`configuration` 9 | 10 | :file:`demo.py` 11 | 12 | :file:`pywps.cfg` 13 | 14 | :file:`processes` 15 | 16 | :file:`logs` 17 | 18 | :file:`outputs` 19 | 20 | :file:`workdir` 21 | 22 | :file:`static` 23 | 24 | .. todo:: Add some text :-) 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | # Related Issue / Discussion 4 | 5 | # Additional Information 6 | 7 | # Contribution Agreement 8 | 9 | (as per https://github.com/geopython/pywps/blob/master/CONTRIBUTING.rst#contributions-and-licensing) 10 | 11 | - [ ] I'd like to contribute [feature X|bugfix Y|docs|something else] to PyWPS. I confirm that my contributions to PyWPS will be compatible with the PyWPS license guidelines at the time of contribution. 12 | - [ ] I have already previously agreed to the PyWPS Contributions and Licensing Guidelines 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | 3 | dist: xenial 4 | 5 | python: 6 | - "2.7" 7 | - "3.4" 8 | 9 | # Handle git submodules yourself 10 | git: 11 | submodules: false 12 | 13 | install: 14 | - sudo apt-get update -qq 15 | - sudo apt-get install -qq gdal-bin libgdal1i libgdal1-dev libgdal-dev libgeos-dev python-dev python3-dev 16 | - pip install GDAL==1.11.1 --global-option=build_ext --global-option="-I/usr/include/gdal" 17 | - pip install -r requirements.txt 18 | 19 | before_script: 20 | "python demo.py -d" 21 | 22 | script: "python -m unittest tests" 23 | -------------------------------------------------------------------------------- /docs/configuration.rst: -------------------------------------------------------------------------------- 1 | .. _configuration: 2 | 3 | ============= 4 | Configuration 5 | ============= 6 | 7 | PyWPS-Demo comes with configuration file, which shall work for both - Flask and 8 | Apache2 deployment. It's stored in :file:`pywps.cfg` some default values. You 9 | are adviced to play with the configuration values and see what they do. More 10 | detailed documentation about PyWPS configuraition can be found at http://pywps.readthedocs.io/en/latest/configuration.html 11 | 12 | Also have a look at :ref:`files` chapter, which describes file and directory 13 | structure of PyWPS-Demo. 14 | -------------------------------------------------------------------------------- /tests/test_log.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sqlite3 3 | 4 | class LoggingTest(unittest.TestCase): 5 | 6 | def test_db_log(self): 7 | 8 | conn = sqlite3.connect('logs/pywps-logs.sqlite3') 9 | 10 | cur = conn.cursor() 11 | cur.execute('select * from pywps_stored_requests') 12 | all_lines = cur.fetchall() 13 | 14 | self.assertEqual(len(all_lines)%5, 0) 15 | 16 | def load_tests(loader=None, tests=None, pattern=None): 17 | if not loader: 18 | loader = unittest.TestLoader() 19 | suite_list = [ 20 | loader.loadTestsFromTestCase(LoggingTest), 21 | ] 22 | return unittest.TestSuite(suite_list) 23 | -------------------------------------------------------------------------------- /docs/processes.rst: -------------------------------------------------------------------------------- 1 | .. _processes: 2 | 3 | ============== 4 | Demo processes 5 | ============== 6 | 7 | PyWPS-Demo comes along with sample processes, so you could get inspired how to 8 | write the process: 9 | 10 | .. autoclass:: processes.area.Area 11 | 12 | .. autoclass:: processes.bboxinout.Box 13 | 14 | .. autoclass:: processes.buffer.Buffer 15 | 16 | .. autoclass:: processes.centroids.Centroids 17 | 18 | .. autoclass:: processes.feature_count.FeatureCount 19 | 20 | .. autoclass:: processes.grassbuffer.GrassBuffer 21 | 22 | .. autoclass:: processes.sayhello.SayHello 23 | 24 | .. autoclass:: processes.sleep.Sleep 25 | 26 | .. autoclass:: processes.ultimate_question.UltimateQuestion 27 | -------------------------------------------------------------------------------- /docker/alpine/nginx/pywps.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | server_name _; 5 | 6 | #better to redirect / to wps application 7 | location / { 8 | return 301 /wps; 9 | } 10 | 11 | location /wps { 12 | # with try_files active there will be problems 13 | 14 | proxy_set_header Host $host; 15 | proxy_redirect off; 16 | proxy_set_header X-NginX-Proxy true; 17 | proxy_set_header X-Real-IP $remote_addr; 18 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 19 | proxy_pass http://pywps:8081/; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docker/ubuntu/nginx/pywps.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80 default_server; 3 | listen [::]:80 default_server; 4 | server_name _; 5 | 6 | #better to redirect / to wps application 7 | location / { 8 | return 301 /wps; 9 | } 10 | 11 | location /wps { 12 | # with try_files active there will be problems 13 | 14 | proxy_set_header Host $host; 15 | proxy_redirect off; 16 | proxy_set_header X-NginX-Proxy true; 17 | proxy_set_header X-Real-IP $remote_addr; 18 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 19 | proxy_pass http://127.0.0.1:8081/; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docker/alpine/gunicorn/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM pywps/flask-alpine:latest 2 | MAINTAINER Jorge Samuel Mendes de Jesus 3 | 4 | #For Gunicorn 5 | ARG GU_WORKERS=5 6 | ENV GU_WORKERS=${GU_WORKERS} 7 | ARG GU_PORT=8081 8 | ENV GU_PORT=${GU_PORT} 9 | 10 | 11 | COPY run_all.sh /run_all.sh 12 | 13 | #For pywps 14 | RUN pip3 install gunicorn 15 | RUN ln -s /pywps-flask/wsgi/pywps.wsgi /pywps-flask/wsgi/pywps_app.py 16 | 17 | 18 | ENTRYPOINT ["/run_all.sh"] 19 | 20 | #Build: docker build -t pywps/gunicorn-alpine:latest . 21 | #Usage: docker run -p 8081:8081 -it pywps/gunicorn-alpine:latest 22 | #Usage w/ 10 workers: docker run -e GU_WORKERS=10 -e GU_PORT=8082 -p 8082:8082 -it pywps/gunicorn-alpine:latest 23 | 24 | -------------------------------------------------------------------------------- /static/pics/PyWPS-Execute-red.svg: -------------------------------------------------------------------------------- 1 | PyWPSPyWPSExecuteExecute -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import unittest 3 | 4 | from tests import test_capabilities 5 | from tests import test_describe 6 | from tests import test_execute 7 | from tests import test_requests 8 | from tests import test_log 9 | #from tests import test_exceptions 10 | 11 | def load_tests(loader=None, tests=None, pattern=None): 12 | return unittest.TestSuite([ 13 | test_capabilities.load_tests(), 14 | test_describe.load_tests(), 15 | test_execute.load_tests(), 16 | test_requests.load_tests(), 17 | test_log.load_tests() 18 | ]) 19 | 20 | if __name__ == "__main__": 21 | result = unittest.TextTestRunner(verbosity=2).run(load_tests()) 22 | if not result.wasSuccessful(): 23 | sys.exit(1) 24 | -------------------------------------------------------------------------------- /static/pics/PyWPS-DescribeProcess-green.svg: -------------------------------------------------------------------------------- 1 | PyWPSPyWPSDescribeProcessDescribeProcess -------------------------------------------------------------------------------- /tests/test_describe.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import lxml.etree as etree 3 | import subprocess 4 | 5 | from tests.common import validate, URL 6 | 7 | class DescribeTest(unittest.TestCase): 8 | 9 | def setUp(self): 10 | 11 | self.url = URL + '?service=wps&request=describeprocess&version=1.0.0&identifier=all' 12 | self.schema_url = 'http://schemas.opengis.net/wps/1.0.0/wpsDescribeProcess_response.xsd' 13 | 14 | def test_valid(self): 15 | assert validate(self.url, self.schema_url) 16 | 17 | def load_tests(loader=None, tests=None, pattern=None): 18 | if not loader: 19 | loader = unittest.TestLoader() 20 | suite_list = [ 21 | loader.loadTestsFromTestCase(DescribeTest), 22 | ] 23 | return unittest.TestSuite(suite_list) 24 | -------------------------------------------------------------------------------- /static/pics/PyWPS-GetCapabilities-brightgreen.svg: -------------------------------------------------------------------------------- 1 | PyWPSPyWPSGetCapabilitiesGetCapabilities -------------------------------------------------------------------------------- /tests/test_capabilities.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import lxml.etree as etree 3 | import urllib 4 | import subprocess 5 | 6 | from tests.common import validate, URL 7 | 8 | class CapabilitiesTest(unittest.TestCase): 9 | 10 | def setUp(self): 11 | 12 | self.url = URL + "?service=wps&request=getcapabilities" 13 | self.schema_url = 'http://schemas.opengis.net/wps/1.0.0/wpsGetCapabilities_response.xsd' 14 | 15 | def test_valid(self): 16 | assert validate(self.url, self.schema_url) 17 | 18 | def load_tests(loader=None, tests=None, pattern=None): 19 | if not loader: 20 | loader = unittest.TestLoader() 21 | suite_list = [ 22 | loader.loadTestsFromTestCase(CapabilitiesTest), 23 | ] 24 | return unittest.TestSuite(suite_list) 25 | -------------------------------------------------------------------------------- /static/data/point.gml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 0.048535402004027880.01369376726825339 10 | 0.048535402004027880.01369376726825339 11 | 12 | 13 | 14 | 15 | 0.048535402004028,0.013693767268253 16 | 2 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /static/requests/execute_buffer_post_referenceinput.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | buffer 4 | 5 | 6 | poly_in 7 | 8 | 9 | 10 | buffer 11 | 12 | 1 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /processes/bboxinout.py: -------------------------------------------------------------------------------- 1 | 2 | from pywps import Process, BoundingBoxInput, BoundingBoxOutput 3 | 4 | __author__ = 'Jachym' 5 | 6 | 7 | class Box(Process): 8 | def __init__(self): 9 | inputs = [BoundingBoxInput('bboxin', 10 | 'box in', ['epsg:4326', 'epsg:3035'])] 11 | outputs = [BoundingBoxOutput('bboxout', 12 | 'box out', ['epsg:4326'])] 13 | 14 | super(Box, self).__init__( 15 | self._handler, 16 | identifier='boundingbox', 17 | version='0.1', 18 | title="Bounding box in- and out", 19 | abstract="""Given a bounding box, it 20 | returns the same bounding box""", 21 | inputs=inputs, 22 | outputs=outputs, 23 | store_supported=True, 24 | status_supported=True 25 | ) 26 | 27 | def _handler(self, request, response): 28 | response.outputs['bboxout'].data = request.inputs['bboxin'][0].data 29 | 30 | return response 31 | -------------------------------------------------------------------------------- /processes/jsonprocess.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pywps import Process, LiteralInput, ComplexOutput, Format 3 | 4 | class TestJson(Process): 5 | def __init__(self): 6 | inputs = [LiteralInput('name', 'Input name', data_type='string')] 7 | outputs = [ComplexOutput('out', 'Referenced Output', 8 | supported_formats=[Format('application/geojson')])] 9 | 10 | super(TestJson, self).__init__( 11 | self._handler, 12 | identifier='testjson', 13 | title='Process Test', 14 | version='1.0.0', 15 | inputs=inputs, 16 | outputs=outputs, 17 | store_supported=True, 18 | status_supported=True 19 | ) 20 | 21 | def _handler(self, request, response): 22 | data = json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]') 23 | out_bytes = json.dumps(data, indent=2) 24 | response.outputs['out'].output_format = 'application/json' 25 | response.outputs['out'].data = out_bytes 26 | 27 | return response 28 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. PyWPS-Demo documentation master file, created by 2 | sphinx-quickstart on Thu Aug 11 21:27:33 2016. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | ######################## 7 | PyWPS-Flask documentation 8 | ######################## 9 | 10 | PyWPS-Flask is a little project, which shall be distributed along with `PyWPS 11 | `_ project. This is just an example service based on PyWPS, with 12 | several example processes. We are adding also a sample `demo.py` which can be used 13 | with the `Flask `_ microframework. 14 | 15 | For more comprehensive documentation visit http://pywps.readthedocs.io/en/latest/ 16 | 17 | ========= 18 | Contents: 19 | ========= 20 | 21 | .. toctree:: 22 | :maxdepth: 2 23 | 24 | flask 25 | configuration 26 | processes 27 | directories 28 | 29 | 30 | 31 | 32 | Indices and tables 33 | ================== 34 | 35 | * :ref:`genindex` 36 | * :ref:`modindex` 37 | * :ref:`search` 38 | 39 | -------------------------------------------------------------------------------- /processes/sayhello.py: -------------------------------------------------------------------------------- 1 | 2 | from pywps import Process, LiteralInput, LiteralOutput, UOM 3 | 4 | 5 | 6 | class SayHello(Process): 7 | def __init__(self): 8 | inputs = [LiteralInput('name', 'Input name', data_type='string')] 9 | outputs = [LiteralOutput('response', 10 | 'Output response', data_type='string')] 11 | 12 | super(SayHello, self).__init__( 13 | self._handler, 14 | identifier='say_hello', 15 | title='Process Say Hello', 16 | abstract='Returns a literal string output\ 17 | with Hello plus the inputed name', 18 | version='1.3.3.7', 19 | inputs=inputs, 20 | outputs=outputs, 21 | store_supported=True, 22 | status_supported=True 23 | ) 24 | 25 | def _handler(self, request, response): 26 | response.outputs['response'].data = 'Hello ' + \ 27 | request.inputs['name'][0].data 28 | response.outputs['response'].uom = UOM('unity') 29 | return response 30 | -------------------------------------------------------------------------------- /tests/test_requests.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import os 3 | 4 | from tests.common import validate_file, URL 5 | 6 | class RequestsTest(unittest.TestCase): 7 | 8 | def test_valid_request(self): 9 | 10 | def valid_request(url, dirname, fname): 11 | fullpath = os.path.join(dirname, fname) 12 | if os.path.isfile(fullpath) and \ 13 | not fname.startswith('.'): 14 | valid = validate_file(fullpath, url) 15 | self.assertTrue(valid, "%s requests valid" % fullpath) 16 | 17 | url = 'http://schemas.opengis.net/wps/1.0.0/wpsAll.xsd' 18 | for (dirpath, dirnames, filenames) in os.walk(os.path.join('static', 'requests')): 19 | for filename in filenames: 20 | valid_request(url, dirpath, filename) 21 | 22 | 23 | def load_tests(loader=None, tests=None, pattern=None): 24 | if not loader: 25 | loader = unittest.TestLoader() 26 | suite_list = [ 27 | loader.loadTestsFromTestCase(RequestsTest), 28 | ] 29 | return unittest.TestSuite(suite_list) 30 | -------------------------------------------------------------------------------- /docker/ubuntu/nginx/Dockerfile: -------------------------------------------------------------------------------- 1 | #based on https://hub.docker.com/r/geographica/gdal2/~/dockerfile/ 2 | FROM pywps/flask-ubuntu:latest 3 | MAINTAINER Jorge S. Mendes de Jesus 4 | 5 | #For Gunicorn 6 | ARG GU_WORKERS=5 7 | ENV GU_WORKERS=${GU_WORKERS} 8 | 9 | RUN apt-get update -y && apt-get install -y \ 10 | nginx 11 | 12 | RUN rm /etc/nginx/sites-enabled/default 13 | COPY pywps.conf /etc/nginx/sites-enabled/pywps.conf 14 | 15 | WORKDIR /pywps-flask 16 | RUN pip3 install gunicorn 17 | RUN ln -s /pywps-flask/wsgi/pywps.wsgi /pywps-flask/wsgi/pywps_app.py 18 | 19 | 20 | COPY pywps4_service.sh /etc/service/pywps4/run 21 | RUN chmod +x /etc/service/pywps4/run 22 | 23 | RUN mkdir /etc/service/nginx 24 | COPY nginx_service.sh /etc/service/nginx/run 25 | RUN chmod +x /etc/service/nginx/run 26 | 27 | EXPOSE 80 28 | 29 | 30 | #docker build -t pywps/nginx-ubuntu . 31 | #docker run -p 80:80 pywps/nginx-ubuntu 32 | #http://localhost:5000/wps?request=GetCapabilities&service=WPS 33 | #http://localhost:5000/wps?request=DescribeProcess&service=WPS&identifier=all&version=1.0.0 34 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 PyWPS Development Team, represented by Jachym Cepicky 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /processes/ultimate_question.py: -------------------------------------------------------------------------------- 1 | 2 | from pywps.app import Process 3 | from pywps.inout.outputs import LiteralOutput 4 | from pywps.app.Common import Metadata 5 | 6 | 7 | class UltimateQuestion(Process): 8 | def __init__(self): 9 | inputs = [] 10 | outputs = [LiteralOutput('answer', 11 | 'Answer to Ultimate Question', 12 | data_type='string')] 13 | 14 | super(UltimateQuestion, self).__init__( 15 | self._handler, 16 | identifier='ultimate_question', 17 | version='1.3.3.7', 18 | title='Answer to the ultimate question', 19 | abstract='The process gives the answer to the ultimate question\ 20 | of "What is the meaning of life?', 21 | profile='', 22 | metadata=[Metadata('Ultimate Question'), Metadata('What is the meaning of life')], 23 | inputs=inputs, 24 | outputs=outputs, 25 | store_supported=False, 26 | status_supported=False 27 | ) 28 | 29 | def _handler(self, request, response): 30 | response.outputs['answer'].data = '42' 31 | return response 32 | -------------------------------------------------------------------------------- /wsgi/deployment.rst: -------------------------------------------------------------------------------- 1 | .. _deployment: 2 | 3 | ================================= 4 | Deployment to a production server 5 | ================================= 6 | 7 | .. note:: This text was taken from `official PyWPS documentation `_ 8 | 9 | Deployment on Apache2 httpd server 10 | ---------------------------------- 11 | 12 | First, the WSGI module must be installed and enabled:: 13 | 14 | $ sudo apt-get install libapache2-mod-wsgi 15 | $ sudo a2enmod wsgi 16 | 17 | You then can edit your site configuration file 18 | (`/etc/apache2/sites-enabled/yoursite.conf`) and add the following:: 19 | 20 | # PyWPS 21 | WSGIDaemonProcess pywps user=www-data group=www-data processes=2 threads=5 22 | WSGIScriptAlias /pywps /var/www/pywps/pywps.wsgi 23 | 24 | 25 | WSGIScriptReloading On 26 | WSGIProcessGroup group 27 | WSGIApplicationGroup %{GLOBAL} 28 | Order deny,allow 29 | Allow from all 30 | 31 | 32 | .. note:: `WSGIScriptAlias` points to the `pywps.wsgi` script created 33 | before - it will be available under the url http://localhost/pywps 34 | 35 | And of course restart the server:: 36 | 37 | $ sudo service apache2 restart 38 | -------------------------------------------------------------------------------- /tests/common.py: -------------------------------------------------------------------------------- 1 | import lxml.etree as etree 2 | import sys 3 | import urllib 4 | PY2 = sys.version_info[0] == 2 5 | 6 | URL = 'http://localhost:5000/wps' 7 | 8 | NAMESPACES = { 9 | 'xlink': "http://www.w3.org/1999/xlink", 10 | 'wps': "http://www.opengis.net/wps/1.0.0", 11 | 'ows': "http://www.opengis.net/ows/1.1", 12 | 'gml': "http://www.opengis.net/gml", 13 | 'xsi': "http://www.w3.org/2001/XMLSchema-instance", 14 | 'ogr': "http://ogr.maptools.org/" 15 | } 16 | 17 | if not PY2: 18 | import urllib.request 19 | 20 | def get_response(url, post_data=None): 21 | 22 | response = None 23 | 24 | if PY2: 25 | response = urllib.urlopen(url, data=post_data) 26 | else: 27 | #if post_data: 28 | # post_data = post_data.decode() 29 | response = urllib.request.urlopen(url, data=post_data) 30 | 31 | return response 32 | 33 | def get_schema(url): 34 | 35 | schema = get_response(url) 36 | xmlschema_doc = etree.parse(schema) 37 | return etree.XMLSchema(xmlschema_doc) 38 | 39 | def validate_file(path, schema): 40 | 41 | body_doc = etree.parse(path) 42 | schema = get_schema(schema) 43 | return schema.validate(body_doc) 44 | 45 | def validate(url, schema, post_data=None): 46 | response = get_response(url, post_data) 47 | info = response.info() 48 | body = response.read() 49 | body_doc = etree.fromstring(body) 50 | 51 | schema = get_schema(schema) 52 | 53 | if schema.validate(body_doc): 54 | return True 55 | else: 56 | print(body) 57 | return False 58 | 59 | -------------------------------------------------------------------------------- /pywps.cfg: -------------------------------------------------------------------------------- 1 | [metadata:main] 2 | identification_title=PyWPS Demo server 3 | identification_abstract=PyWPS testing and development server. Do NOT use this server in production environment. You shall setup PyWPS as WSGI application for production. Please refer documentation for further detials. 4 | identification_keywords=WPS,GRASS,PyWPS, Demo, Dev 5 | identification_keywords_type=theme 6 | identification_fees=None 7 | identification_accessconstraints=None 8 | provider_name=PyWPS Developement team 9 | provider_url=http://pywps.org/' 10 | contact_name=Your Name 11 | contact_position=Developer 12 | contact_address=My Street 13 | contact_city=My City 14 | contact_stateorprovince=None 15 | contact_postalcode=000 00 16 | contact_country=World, Internet 17 | contact_phone=+00 00 11 22 33 18 | contact_fax=+00 99 88 77 66 19 | contact_email=info@yourdomain.org 20 | contact_url=http://pywps.org 21 | contact_hours=8:00-20:00UTC 22 | contact_instructions=Knock on the door 23 | contact_role=pointOfContact 24 | 25 | [server] 26 | maxsingleinputsize=1mb 27 | maxrequestsize=3mb 28 | url=http://localhost:5000/wps 29 | outputurl=http://localhost:5000/outputs/ 30 | outputpath=outputs 31 | workdir=workdir 32 | wd_inp_subdir=inputs 33 | wd_out_subdir=outputs 34 | maxprocesses=10 35 | parallelprocesses=2 36 | 37 | [processing] 38 | mode=docker 39 | port_min=5050 40 | port_max=5070 41 | docker_img=container 42 | dckr_inp_dir=/pywps-flask/data 43 | dckr_out_dir=/pywps-flask/outputs 44 | 45 | [logging] 46 | level=INFO 47 | file=logs/pywps.log 48 | database=sqlite:///logs/pywps-logs.sqlite3 49 | format=%(asctime)s] [%(levelname)s] file=%(pathname)s line=%(lineno)s module=%(module)s function=%(funcName)s %(message)s 50 | 51 | 52 | [grass] 53 | gisbase=/usr/local/grass-7.3.svn/ 54 | -------------------------------------------------------------------------------- /processes/area.py: -------------------------------------------------------------------------------- 1 | 2 | import json 3 | import os 4 | import subprocess 5 | from pywps import Process, ComplexInput, LiteralOutput, Format 6 | from pywps.wpsserver import temp_dir 7 | 8 | __author__ = 'matteo' 9 | 10 | 11 | class Area(Process): 12 | """Process calculating area of given polygon 13 | """ 14 | def __init__(self): 15 | inputs = [ComplexInput('layer', 'Layer', 16 | [Format('application/gml+xml')])] 17 | outputs = [LiteralOutput('area', 'Area', data_type='string')] 18 | 19 | super(Area, self).__init__( 20 | self._handler, 21 | identifier='area', 22 | title='Process Area', 23 | abstract="""Process returns the area of each 24 | feature from a submitted GML file""", 25 | inputs=inputs, 26 | outputs=outputs, 27 | store_supported=True, 28 | status_supported=True 29 | ) 30 | 31 | def _handler(self, request, response): 32 | # ogr2ogr requires gdal-bin 33 | from shapely.geometry import shape 34 | with temp_dir() as tmp: 35 | input_gml = request.inputs['layer'][0].file 36 | input_geojson = os.path.join(tmp, 'input.geojson') 37 | subprocess.check_call(['ogr2ogr', '-f', 'geojson', 38 | str(input_geojson), input_gml]) 39 | with open(input_geojson, 'rb') as f: 40 | data = json.loads(f.read()) 41 | for feature in data['features']: 42 | geom = shape(feature['geometry']) 43 | feature['area'] = geom.area 44 | response.outputs['area'].data = [feature['area'] 45 | for feature in data['features']] 46 | return response 47 | -------------------------------------------------------------------------------- /wsgi/pywps.wsgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This is example WSGI application of PyWPS, which can be used by e.g. Apache2 5 | HTTPD server 6 | 7 | Copyright (c) 2016 - PyWPS PSC 8 | 9 | Permission is hereby granted, free of charge, to any person 10 | obtaining a copy of this software and associated documentation 11 | files (the "Software"), to deal in the Software without 12 | restriction, including without limitation the rights to use, 13 | copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the 15 | Software is furnished to do so, subject to the following 16 | conditions: 17 | 18 | The above copyright notice and this permission notice shall be 19 | included in all copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 23 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 25 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 26 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | """ 30 | 31 | __author__ = "Jachym Cepicky" 32 | 33 | from pywps.app.Service import Service 34 | 35 | # processes need to be installed in PYTHON_PATH 36 | from processes.sleep import Sleep 37 | from processes.ultimate_question import UltimateQuestion 38 | from processes.centroids import Centroids 39 | from processes.sayhello import SayHello 40 | from processes.feature_count import FeatureCount 41 | from processes.buffer import Buffer 42 | from processes.area import Area 43 | 44 | 45 | processes = [ 46 | FeatureCount(), 47 | SayHello(), 48 | Centroids(), 49 | UltimateQuestion(), 50 | Sleep(), 51 | Buffer(), 52 | Area() 53 | ] 54 | 55 | application = Service(processes, ['pywps.cfg']) 56 | -------------------------------------------------------------------------------- /docker/isolation/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.8 2 | MAINTAINER Jorge S. Mendes de Jesus 3 | 4 | ARG GDAL_VERSION=2.3.2 5 | ARG XERCES_VERSION=3.2.2 6 | ARG PROCESSOR_N=4 7 | ARG FLASK_GIT=https://github.com/jorgejesus/pywps-flask.git 8 | ARG FLASK_BRANCH=pywps_4.2 9 | 10 | RUN apk update && apk add --no-cache \ 11 | git \ 12 | gcc \ 13 | bash \ 14 | openssh \ 15 | musl-dev \ 16 | python3 \ 17 | python3-dev \ 18 | libxml2-dev \ 19 | libxslt-dev \ 20 | linux-headers \ 21 | expat \ 22 | expat-dev \ 23 | g++ \ 24 | libstdc++ \ 25 | make \ 26 | swig 27 | 28 | 29 | RUN apk add --no-cache \ 30 | --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing \ 31 | geos \ 32 | geos-dev 33 | 34 | #Compiling Xerces 35 | RUN wget http://www.apache.org/dist/xerces/c/3/sources/xerces-c-$XERCES_VERSION.tar.gz -O /tmp/xerces-c-$XERCES_VERSION.tar.gz && \ 36 | tar xvf /tmp/xerces-c-$XERCES_VERSION.tar.gz -C /tmp && \ 37 | cd /tmp/xerces-c-$XERCES_VERSION && \ 38 | LDFLAGS="-s" ./configure --prefix=/usr/local/src/xerces && \ 39 | make -j $PROCESSOR_N install 40 | 41 | 42 | # Install GDAL 43 | RUN wget http://download.osgeo.org/gdal/$GDAL_VERSION/gdal-$GDAL_VERSION.tar.gz -O /tmp/gdal.tar.gz && \ 44 | tar xzf /tmp/gdal.tar.gz -C /tmp && \ 45 | cd /tmp/gdal-$GDAL_VERSION && \ 46 | CFLAGS="-g -Wall" LDFLAGS="-s" ./configure --with-expat=yes --with-xerces=/opt/xerces --with-geos=yes \ 47 | && make -j $PROCESSOR_N && make install 48 | 49 | RUN cd /tmp/gdal-$GDAL_VERSION/swig/python \ 50 | && python3 setup.py build \ 51 | && python3 setup.py install 52 | 53 | RUN git clone $FLASK_GIT -b $FLASK_BRANCH --single-branch 54 | WORKDIR /pywps-flask 55 | RUN pip3 install -r requirements.txt 56 | 57 | 58 | EXPOSE 5000 59 | ENTRYPOINT ["/usr/bin/python3", "demo.py","-a"] 60 | 61 | #docker build -t pywps . 62 | #docker run -p 5000:5000 pywps 63 | #http://localhost:5000/wps?request=GetCapabilities&service=WPS 64 | #http://localhost:5000/wps?request=DescribeProcess&service=WPS&identifier=all&version=1.0.0 65 | -------------------------------------------------------------------------------- /processes/grassbuffer.py: -------------------------------------------------------------------------------- 1 | 2 | from pywps import Process, LiteralInput, ComplexInput, ComplexOutput, Format 3 | 4 | 5 | from pywps.validator.mode import MODE 6 | 7 | __author__ = 'Jachym' 8 | 9 | 10 | class GrassBuffer(Process): 11 | 12 | def __init__(self): 13 | inputs = [ComplexInput('poly_in', 'Input1', 14 | supported_formats=[Format('application/gml+xml')], 15 | mode=MODE.SIMPLE), 16 | LiteralInput('buffer', 'Buffer', data_type='float', 17 | allowed_values=(0, 1, 10, (10, 10, 100), (100, 100, 1000)))] 18 | outputs = [ComplexOutput('buff_out', 'Buffered', 19 | supported_formats=[ 20 | Format('application/gml+xml') 21 | ])] 22 | 23 | super(GrassBuffer, self).__init__( 24 | self._handler, 25 | identifier='grassbuffer', 26 | version='0.1', 27 | title="GRASS v.buffer", 28 | abstract='The process uses the GRASS GIS \ 29 | v.buffer module to generate buffers around inputs ', 30 | profile='', 31 | inputs=inputs, 32 | outputs=outputs, 33 | store_supported=True, 34 | status_supported=True, 35 | # grass_location="/tmp/outputs/pyws_process_GMkyxP/pywps_location" 36 | grass_location="epsg:3857" 37 | ) 38 | 39 | def _handler(self, request, response): 40 | 41 | from grass.pygrass.modules import Module 42 | Module('v.import', 43 | input=request.inputs['poly_in'][0].file, 44 | epsg=3857, output='poly', extent='input') 45 | Module('v.buffer', 46 | input='poly', 47 | distance=request.inputs['buffer'][0].data, 48 | output='buffer') 49 | Module('v.out.ogr', input='buffer', output='buffer.gml', format='GML') 50 | 51 | response.outputs['buff_out'].file = 'buffer.gml' 52 | 53 | return response 54 | -------------------------------------------------------------------------------- /docker/alpine/flask/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.8 2 | MAINTAINER Jorge S. Mendes de Jesus 3 | 4 | ARG GDAL_VERSION=2.4.0 5 | ARG XERCES_VERSION=3.2.2 6 | ARG PROCESSOR_N=4 7 | ARG FLASK_GIT=https://github.com/jorgejesus/pywps-flask.git 8 | ARG FLASK_BRANCH=pywps_4.2 9 | 10 | RUN apk update && apk add --no-cache \ 11 | git \ 12 | gcc \ 13 | bash \ 14 | openssh \ 15 | musl-dev \ 16 | python3 \ 17 | python3-dev \ 18 | libxml2-dev \ 19 | libxslt-dev \ 20 | linux-headers \ 21 | expat \ 22 | expat-dev \ 23 | g++ \ 24 | libstdc++ \ 25 | make \ 26 | swig 27 | 28 | RUN apk add --no-cache \ 29 | --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing \ 30 | geos \ 31 | geos-dev 32 | 33 | 34 | #Compiling Xerces 35 | RUN wget http://www.apache.org/dist/xerces/c/3/sources/xerces-c-$XERCES_VERSION.tar.gz -O /tmp/xerces-c-$XERCES_VERSION.tar.gz && \ 36 | tar xvf /tmp/xerces-c-$XERCES_VERSION.tar.gz -C /tmp && \ 37 | cd /tmp/xerces-c-$XERCES_VERSION && \ 38 | LDFLAGS="-s" ./configure --prefix=/usr/local/src/xerces && \ 39 | make -j $PROCESSOR_N install 40 | 41 | # Install GDAL 42 | RUN wget http://download.osgeo.org/gdal/$GDAL_VERSION/gdal-$GDAL_VERSION.tar.gz -O /tmp/gdal.tar.gz && \ 43 | tar xzf /tmp/gdal.tar.gz -C /tmp && \ 44 | cd /tmp/gdal-$GDAL_VERSION && \ 45 | LDFLAGS="-s" ./configure --with-expat=yes --with-xerces=/opt/xerces --with-geos=yes \ 46 | && make -j $PROCESSOR_N && make install 47 | 48 | RUN cd /tmp/gdal-$GDAL_VERSION/swig/python \ 49 | && python3 setup.py install 50 | 51 | RUN rm -rf /var/cache/apk/* 52 | 53 | RUN git clone $FLASK_GIT -b $FLASK_BRANCH --single-branch 54 | WORKDIR /pywps-flask 55 | RUN pip3 install -r requirements.txt 56 | 57 | ENTRYPOINT ["/usr/bin/python3", "demo.py","-a"] 58 | 59 | #docker build -t pywps/flask-alpine . 60 | #docker run -p 5000:5000 pywps/flask-alpine:latest 61 | #http://localhost:5000/wps?request=GetCapabilities&service=WPS 62 | #http://localhost:5000/wps?request=DescribeProcess&service=WPS&identifier=all&version=1.0.0 63 | -------------------------------------------------------------------------------- /docker/ubuntu/flask/Dockerfile: -------------------------------------------------------------------------------- 1 | #based on https://hub.docker.com/r/geographica/gdal2/~/dockerfile/ 2 | #Using a fat docker since other images like nginx-gunicorn will just incorporate extra services 3 | FROM phusion/baseimage:0.11 4 | MAINTAINER Jorge S. Mendes de Jesus 5 | 6 | ARG ROOTDIR=/usr/local/ 7 | ARG GDAL_VERSION=2.4.0 8 | ARG PROCESSOR_N=8 9 | ARG FLASK_GIT=https://github.com/jorgejesus/pywps-flask.git 10 | ARG FLASK_BRANCH=pywps_4.2 11 | 12 | WORKDIR $ROOTDIR/ 13 | 14 | RUN apt-get update -y && install_clean \ 15 | software-properties-common \ 16 | build-essential \ 17 | python3-dev \ 18 | python3-numpy \ 19 | python3-lxml \ 20 | python3-lxml \ 21 | python3-pip \ 22 | python3-wheel \ 23 | python3-setuptools \ 24 | libspatialite-dev \ 25 | sqlite3 \ 26 | libpq-dev \ 27 | libxml2-dev \ 28 | libcurl4-gnutls-dev \ 29 | libproj-dev \ 30 | libxml2-dev \ 31 | libxslt-dev \ 32 | libgeos-dev \ 33 | libxerces-c-dev \ 34 | git \ 35 | cmake 36 | 37 | 38 | ADD http://download.osgeo.org/gdal/$GDAL_VERSION/gdal-$GDAL_VERSION.tar.gz $ROOTDIR/src/ 39 | 40 | RUN cd src/ && tar -xvf gdal-$GDAL_VERSION.tar.gz && cd gdal-$GDAL_VERSION \ 41 | && LDFLAGS="-s" ./configure --with-python --with-geos --with-spatialite --with-pg --with-curl --with-xerces --with-expat \ 42 | && make -j $PROCESSOR_N && make install && ldconfig \ 43 | && cd $ROOTDIR && cd src/gdal-$GDAL_VERSION/swig/python \ 44 | && python3 setup.py build \ 45 | && python3 setup.py install \ 46 | && cd $ROOTDIR && rm -Rf src/gdal* 47 | 48 | 49 | WORKDIR / 50 | RUN git clone $FLASK_GIT -b $FLASK_BRANCH --single-branch 51 | 52 | WORKDIR /pywps-flask 53 | RUN pip3 install -r requirements.txt 54 | 55 | RUN mkdir /etc/service/pywps4 56 | COPY pywps4_service.sh /etc/service/pywps4/run 57 | RUN chmod +x /etc/service/pywps4/run 58 | 59 | 60 | EXPOSE 5000 61 | 62 | #docker build -t pywps/flask-ubuntu:latest . 63 | #docker run -p 5000:5000 pywps/flask-ubuntu 64 | #http://localhost:5000/wps?request=GetCapabilities&service=WPS 65 | #http://localhost:5000/wps?request=DescribeProcess&service=WPS&identifier=all&version=1.0.0 66 | -------------------------------------------------------------------------------- /templates/home.html: -------------------------------------------------------------------------------- 1 | 2 | {% extends "base.html" %} 3 | {% block title %}PyWPS-4 example service{% endblock %} 4 | {% block major_content%} 5 |
6 |

PyWPS-4 example service

7 | 8 |

This is an example PyWPS service, point your WPS client to: 9 |

10 | {{ server_url }} 11 |

12 |

13 | 16 | 20 | 21 |

22 | It exposes the following processes: 23 |
    24 | {% for name, abstract in process_descriptor.items() %} 25 |
  • {{ name }} - {{ abstract }}
  •    26 | 27 | {% endfor %} 28 |
29 | 30 | 31 |

32 |

33 | PyWPS documentation is at pywps.rtfd.org; source code and issue tracking at 35 | 36 | github.com/geopython/pywps. Code for this app is at 39 | github.com/geopython/pywps-flask/blob/master/demo.py. 40 |

41 | 42 |

43 | PyWPS documentation is at 44 | pywps.rtfd.org; 45 | source code and issue tracking at 46 | 47 | github.com/geopython/pywps. Code for this app is at 48 | 49 | github.com/geopython/pywps-flask. 50 |

51 | 52 | {% endblock major_content%} 53 | -------------------------------------------------------------------------------- /static/requests/execute_buffer_post.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | buffer 4 | 5 | 6 | poly_in 7 | 8 | 9 | 14 | 15 | 16 | 0.048535402004027880.01369376726825339 17 | 0.048535402004027880.01369376726825339 18 | 19 | 20 | 21 | 22 | 0.048535402004028,0.013693767268253 23 | 2 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | buffer 32 | 33 | 1 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /static/requests/execute_buffer_post_referenceoutput.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | buffer 4 | 5 | 6 | poly_in 7 | 8 | 9 | 14 | 15 | 16 | 0.048535402004027880.01369376726825339 17 | 0.048535402004027880.01369376726825339 18 | 19 | 20 | 21 | 22 | 0.048535402004028,0.013693767268253 23 | 2 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | buffer 32 | 33 | 1 34 | 35 | 36 | 37 | 38 | 39 | 40 | buff_out 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {% block title %}{% endblock %} 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 |
25 |
26 | 27 | 40 | 41 |
42 |
{% block major_content %} {% endblock %}
43 |
44 |

Copyright (C) 2014-2016 PyWPS Development Team, represented by Jachym Cepicky. Creative Commons License

45 |
46 |
47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /processes/buffer.py: -------------------------------------------------------------------------------- 1 | 2 | from pywps import Process, LiteralInput, \ 3 | ComplexInput, ComplexOutput, Format, FORMATS 4 | 5 | 6 | from pywps.validator.mode import MODE 7 | 8 | __author__ = 'Brauni' 9 | 10 | 11 | class Buffer(Process): 12 | def __init__(self): 13 | inputs = [ComplexInput('poly_in', 'Input vector file', 14 | supported_formats=[Format('application/gml+xml')], 15 | mode=MODE.STRICT), 16 | LiteralInput('buffer', 'Buffer size', data_type='float', 17 | allowed_values=(0, 1, 10, (10, 10, 100), (100, 100, 1000)))] 18 | outputs = [ComplexOutput('buff_out', 'Buffered file', 19 | supported_formats=[ 20 | Format('application/gml+xml') 21 | ] 22 | )] 23 | 24 | super(Buffer, self).__init__( 25 | self._handler, 26 | identifier='buffer', 27 | version='0.1', 28 | title="GDAL Buffer process", 29 | abstract="""The process returns buffers around the input features, 30 | using the GDAL library""", 31 | profile='', 32 | inputs=inputs, 33 | outputs=outputs, 34 | store_supported=True, 35 | status_supported=True 36 | ) 37 | 38 | def _handler(self, request, response): 39 | from osgeo import ogr 40 | 41 | inSource = ogr.Open(request.inputs['poly_in'][0].file) 42 | 43 | inLayer = inSource.GetLayer() 44 | out = inLayer.GetName() + '_buffer' 45 | 46 | # create output file 47 | driver = ogr.GetDriverByName('GML') 48 | outSource = driver.CreateDataSource( 49 | out, 50 | ["XSISCHEMAURI=\ 51 | http://schemas.opengis.net/gml/2.1.2/feature.xsd"]) 52 | outLayer = outSource.CreateLayer(out, None, ogr.wkbUnknown) 53 | 54 | # for each feature 55 | featureCount = inLayer.GetFeatureCount() 56 | index = 0 57 | 58 | while index < featureCount: 59 | # get the geometry 60 | inFeature = inLayer.GetNextFeature() 61 | inGeometry = inFeature.GetGeometryRef() 62 | 63 | # make the buffer 64 | buff = inGeometry.Buffer(float(request.inputs['buffer'][0].data)) 65 | 66 | # create output feature to the file 67 | outFeature = ogr.Feature(feature_def=outLayer.GetLayerDefn()) 68 | outFeature.SetGeometryDirectly(buff) 69 | outLayer.CreateFeature(outFeature) 70 | outFeature.Destroy() # makes it crash when using debug 71 | index += 1 72 | 73 | response.update_status('Buffering', 100*(index/featureCount)) 74 | 75 | outSource.Destroy() 76 | 77 | response.outputs['buff_out'].output_format = FORMATS.GML 78 | response.outputs['buff_out'].file = out 79 | 80 | return response 81 | -------------------------------------------------------------------------------- /processes/feature_count.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 PyWPS Project Steering Committee 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | from pywps import Process, ComplexInput, Format, LiteralOutput 22 | from pywps.app.Common import Metadata 23 | 24 | 25 | class FeatureCount(Process): 26 | def __init__(self): 27 | inputs = [ComplexInput('layer', 'Layer', 28 | [Format('application/gml+xml')])] 29 | outputs = [LiteralOutput('count', 'Count', data_type='integer')] 30 | 31 | super(FeatureCount, self).__init__( 32 | self._handler, 33 | identifier='feature_count', 34 | version='None', 35 | title='Feature count', 36 | abstract='This process counts the number\ 37 | of features in an uploaded GML ', 38 | profile='', 39 | metadata=[Metadata('Feature'), Metadata('Count')], 40 | inputs=inputs, 41 | outputs=outputs, 42 | store_supported=True, 43 | status_supported=True 44 | ) 45 | 46 | def _handler(self, request, response): 47 | import lxml.etree 48 | from pywps.app.basic import xpath_ns 49 | doc = lxml.etree.parse(request.inputs['layer'][0].file) 50 | feature_elements = xpath_ns(doc, '//gml:featureMember') 51 | response.outputs['count'].data = len(feature_elements) 52 | return response 53 | 54 | 55 | def main(): 56 | """Example of how to debug this process, executing it outside a PyWPS 57 | instance. 58 | """ 59 | count = FeatureCount() 60 | (request, response) = count.build_request_response() 61 | literal_in = count.inputs[0] 62 | literal_in.file = '../data/railroads.gml' 63 | request.inputs["layer"].append(literal_in) 64 | count._handler(request, response) 65 | 66 | assert response.outputs['count'].data == 832 67 | print("All good!") 68 | 69 | if __name__ == "__main__": 70 | main() 71 | -------------------------------------------------------------------------------- /processes/centroids.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 PyWPS Project Steering Committee 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | import json 22 | import subprocess 23 | from pywps import Process, ComplexInput, ComplexOutput, Format, FORMATS 24 | 25 | 26 | class Centroids(Process): 27 | def __init__(self): 28 | inputs = [ComplexInput('layer', 'Layer', 29 | supported_formats=[ 30 | Format('application/gml+xml') 31 | ])] 32 | outputs = [ComplexOutput('out', 'Referenced Output', 33 | supported_formats=[ 34 | Format('application/json') 35 | ])] 36 | 37 | super(Centroids, self).__init__( 38 | self._handler, 39 | identifier='centroids', 40 | title='Process Centroids', 41 | abstract='Returns a GeoJSON \ 42 | with centroids of features from an uploaded GML.', 43 | inputs=inputs, 44 | outputs=outputs, 45 | store_supported=True, 46 | status_supported=True 47 | ) 48 | 49 | def _handler(self, request, response): 50 | # ogr2ogr requires gdal-bin 51 | from shapely.geometry import shape, mapping 52 | 53 | input_gml = request.inputs['layer'][0].file 54 | input_geojson = 'input.geojson' 55 | subprocess.check_call(['ogr2ogr', '-f', 'geojson', 56 | input_geojson, input_gml]) 57 | with open(input_geojson, 'rb') as f: 58 | data = json.loads(f.read()) 59 | for feature in data['features']: 60 | geom = shape(feature['geometry']) 61 | feature['geometry'] = mapping(geom.centroid) 62 | out_bytes = json.dumps(data, indent=2) 63 | response.outputs['out'].output_format = Format(FORMATS['JSON']) 64 | response.outputs['out'].data = out_bytes 65 | return response 66 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2 | # 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in all 12 | # copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | 23 | try: 24 | from setuptools import setup 25 | except ImportError: 26 | from distutils.core import setup 27 | 28 | with open('VERSION.txt') as ff: 29 | VERSION = ff.read().strip() 30 | 31 | with open('requirements.txt') as f: 32 | INSTALL_REQUIRES = f.read().splitlines()[:-1] 33 | INSTALL_REQUIRES.append('pywps=='+VERSION) 34 | 35 | DESCRIPTION = ( 36 | '''PyWPS is an implementation of the Web Processing Service standard from the 37 | Open Geospatial Consortium. PyWPS is written in Python. 38 | 39 | PyWPS-Flask is an example service using the PyWPS server, distributed along 40 | with a basic set of sample processes and sample configuration file. It's 41 | usually used for testing and development purposes. 42 | ''') 43 | 44 | KEYWORDS = 'PyWPS WPS OGC processing' 45 | 46 | config = { 47 | 'description': DESCRIPTION, 48 | 'keywords': KEYWORDS, 49 | 'author': 'PyWPS PSC', 50 | 'license': 'MIT', 51 | 'platforms': 'all', 52 | 'url': 'http://pywps.org', 53 | 'download_url': 'https://github.com/lazaa32/pywps-flask', 54 | 'author_email': 'luis.a.de.sousa@gmail.com', 55 | 'maintainer': 'Luis de Sousa', 56 | 'maintainer_email': 'luis.de.sousa@protonmail.ch', 57 | 'classifiers': [ 58 | 'Development Status :: 5 - Production/Stable', 59 | 'Environment :: Web Environment', 60 | 'Intended Audience :: Developers', 61 | 'Intended Audience :: Science/Research', 62 | 'License :: OSI Approved :: MIT License', 63 | 'Operating System :: OS Independent', 64 | 'Programming Language :: Python', 65 | 'Topic :: Scientific/Engineering :: GIS' 66 | ], 67 | 'version': VERSION, 68 | 'install_requires': INSTALL_REQUIRES, 69 | 'dependency_links': [ 70 | 'git+https://github.com/geopython/pywps.git@pywps-'+VERSION+'#egg=pywps-'+VERSION 71 | ], 72 | 'packages': ['processes', 'tests'], 73 | 'scripts': ['demo.py'], 74 | 'name': 'pywps-flask' 75 | } 76 | 77 | setup(**config) 78 | -------------------------------------------------------------------------------- /processes/sleep.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2016 PyWPS Project Steering Committee 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy 4 | # of this software and associated documentation files (the "Software"), to deal 5 | # in the Software without restriction, including without limitation the rights 6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | # copies of the Software, and to permit persons to whom the Software is 8 | # furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in all 11 | # copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | # SOFTWARE. 20 | 21 | from pywps import Process, LiteralInput, LiteralOutput 22 | from pywps.app.Common import Metadata 23 | 24 | 25 | class Sleep(Process): 26 | 27 | SUCCESS_MESSAGE = 'done sleeping' 28 | 29 | def __init__(self): 30 | inputs = [LiteralInput('delay', 31 | 'Delay between every update', 32 | data_type='float')] 33 | outputs = [LiteralOutput('sleep_output', 34 | 'Sleep Output', 35 | data_type='string')] 36 | 37 | super(Sleep, self).__init__( 38 | self._handler, 39 | identifier='sleep', 40 | version='None', 41 | title='Sleep Process', 42 | abstract="The process will sleep for a given delay \ 43 | or 10 seconds if not a valid value", 44 | profile='', 45 | metadata=[Metadata('Sleep'), Metadata('Wait'), Metadata('Delay')], 46 | inputs=inputs, 47 | outputs=outputs, 48 | store_supported=True, 49 | status_supported=True 50 | ) 51 | 52 | def _handler(self, request, response): 53 | import time 54 | 55 | sleep_delay = request.inputs['delay'][0].data 56 | if sleep_delay: 57 | sleep_delay = float(sleep_delay) 58 | else: 59 | sleep_delay = 10 60 | 61 | time.sleep(sleep_delay) 62 | response.update_status('PyWPS Process started. Waiting...', 20) 63 | time.sleep(sleep_delay) 64 | response.update_status('PyWPS Process started. Waiting...', 40) 65 | time.sleep(sleep_delay) 66 | response.update_status('PyWPS Process started. Waiting...', 60) 67 | time.sleep(sleep_delay) 68 | response.update_status('PyWPS Process started. Waiting...', 80) 69 | time.sleep(sleep_delay) 70 | response.outputs['sleep_output'].data = self.SUCCESS_MESSAGE 71 | 72 | return response 73 | 74 | 75 | def main(): 76 | """Example of how to debug this process, running outside a PyWPS instance. 77 | """ 78 | sleep = Sleep() 79 | (request, response) = sleep.build_request_response() 80 | literal_in = sleep.inputs[0] 81 | literal_in.data = 10 82 | request.inputs["delay"].append(literal_in) 83 | sleep._handler(request, response) 84 | 85 | assert response.outputs["sleep_output"].data == sleep.SUCCESS_MESSAGE 86 | print("All good!") 87 | 88 | if __name__ == "__main__": 89 | main() 90 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright (c) 2016 PyWPS Project Steering Committee 4 | # 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | 24 | import os 25 | import flask 26 | 27 | import pywps 28 | from pywps import Service 29 | 30 | from processes.sleep import Sleep 31 | from processes.ultimate_question import UltimateQuestion 32 | from processes.centroids import Centroids 33 | from processes.sayhello import SayHello 34 | from processes.feature_count import FeatureCount 35 | from processes.buffer import Buffer 36 | from processes.area import Area 37 | from processes.bboxinout import Box 38 | from processes.jsonprocess import TestJson 39 | 40 | 41 | app = flask.Flask(__name__) 42 | 43 | processes = [ 44 | FeatureCount(), 45 | SayHello(), 46 | Centroids(), 47 | UltimateQuestion(), 48 | Sleep(), 49 | Buffer(), 50 | Area(), 51 | Box(), 52 | TestJson() 53 | ] 54 | 55 | # For the process list on the home page 56 | 57 | process_descriptor = {} 58 | for process in processes: 59 | abstract = process.abstract 60 | identifier = process.identifier 61 | process_descriptor[identifier] = abstract 62 | 63 | # This is, how you start PyWPS instance 64 | service = Service(processes, ['pywps.cfg']) 65 | 66 | 67 | @app.route("/") 68 | def hello(): 69 | server_url = pywps.configuration.get_config_value("server", "url") 70 | request_url = flask.request.url 71 | return flask.render_template('home.html', request_url=request_url, 72 | server_url=server_url, 73 | process_descriptor=process_descriptor) 74 | 75 | 76 | @app.route('/wps', methods=['GET', 'POST']) 77 | def wps(): 78 | 79 | return service 80 | 81 | 82 | @app.route('/outputs/'+'') 83 | def outputfile(filename): 84 | targetfile = os.path.join('outputs', filename) 85 | if os.path.isfile(targetfile): 86 | file_ext = os.path.splitext(targetfile)[1] 87 | with open(targetfile, mode='rb') as f: 88 | file_bytes = f.read() 89 | mime_type = None 90 | if 'xml' in file_ext: 91 | mime_type = 'text/xml' 92 | return flask.Response(file_bytes, content_type=mime_type) 93 | else: 94 | flask.abort(404) 95 | 96 | 97 | @app.route('/static/'+'') 98 | def staticfile(filename): 99 | targetfile = os.path.join('static', filename) 100 | if os.path.isfile(targetfile): 101 | with open(targetfile, mode='rb') as f: 102 | file_bytes = f.read() 103 | mime_type = None 104 | return flask.Response(file_bytes, content_type=mime_type) 105 | else: 106 | flask.abort(404) 107 | 108 | if __name__ == "__main__": 109 | import argparse 110 | 111 | parser = argparse.ArgumentParser( 112 | description="""Script for starting an example PyWPS 113 | instance with sample processes""", 114 | epilog="""Do not use this service in a production environment. 115 | It's intended to be running in test environment only! 116 | For more documentation, visit http://pywps.org/doc 117 | """ 118 | ) 119 | parser.add_argument('-d', '--daemon', 120 | action='store_true', help="run in daemon mode") 121 | parser.add_argument('-a','--all-addresses', 122 | action='store_true', help="run flask using IPv4 0.0.0.0 (all network interfaces)," + 123 | "otherwise bind to 127.0.0.1 (localhost). This maybe necessary in systems that only run Flask") 124 | args = parser.parse_args() 125 | 126 | if args.all_addresses: 127 | bind_host='0.0.0.0' 128 | else: 129 | bind_host='127.0.0.1' 130 | 131 | if args.daemon: 132 | pid = None 133 | try: 134 | pid = os.fork() 135 | except OSError as e: 136 | raise Exception("%s [%d]" % (e.strerror, e.errno)) 137 | 138 | if (pid == 0): 139 | os.setsid() 140 | app.run(threaded=True,host=bind_host) 141 | else: 142 | os._exit(0) 143 | else: 144 | app.run(threaded=True,host=bind_host) 145 | -------------------------------------------------------------------------------- /docs/flask.rst: -------------------------------------------------------------------------------- 1 | .. _flask: 2 | 3 | ===== 4 | Flask 5 | ===== 6 | 7 | `Flask `_ is a microframework for web applications in Python. 8 | Some characteristics of Flask: 9 | 10 | * built-in development server and debugger 11 | * RESTful request dispatching 12 | * 100% WSGI 1.0 compliant 13 | 14 | You can develop your PyWPS application and modules using a local Flask 15 | server and then move it to a production environment (e.g. with Apache2 HTTP 16 | server). 17 | 18 | Start PyWPS service 19 | ------------------ 20 | 21 | Start the PyWPS example service using Flask's built-in server:: 22 | 23 | $ python3 demo.py 24 | 25 | You should see some output from the WPS-server that is now running at 26 | http://localhost:5000/wps. Alternatively you may use Python2 and issue `python demo.py`. 27 | 28 | Testing the server 29 | ------------------ 30 | 31 | Basics 32 | ~~~~~~ 33 | 34 | You should be able to interact with the WPS-server like any other 35 | HTTP-server, i.e. either requesting URLs using your web browser or using 36 | commandline tools like `wget` or `curl`. For example using `wget` to 37 | fetch the `Capabilities` of the WPS Server:: 38 | 39 | $ wget --content-on-error -O - "http://localhost:5000/wps?service=wps&request=getcapabilities" 40 | 41 | Or visit the URL directly in the browser:: 42 | 43 | http://localhost:5000/wps?service=wps&request=getcapabilities 44 | 45 | In both cases you should see the response:: 46 | 47 | 48 | 51 | 52 | If anything goes wrong, you should see the result in Flask terminal, for example:: 53 | 54 | http://localhost:5000/wps 55 | 56 | With response:: 57 | 58 | 59 | 60 | 61 | 62 | service 63 | 64 | 65 | And output from Flask in the terminal:: 66 | 67 | ERROR:PYWPS:Exception: code: 400, locator: service, description: service 68 | NoneType 69 | 70 | Processes 71 | ~~~~~~~~~ 72 | 73 | The `GetCapabilities` response in the previous section lists the WPS Processes 74 | available on the Flask example service. 75 | 76 | Issue a `DescribeProcess` WPS request for the `say_hello` WPS Process using the URL:: 77 | 78 | http://127.0.0.1:5000/wps?service=WPS&request=DescribeProcess&version=1.0.0&identifier=say_hello 79 | 80 | Note that the `version` parameter is required with most WPS-requests. 81 | The output includes the `Inputs` for this WPS Process:: 82 | 83 | 84 | 88 | 89 | say_hello 90 | Process Say Hello 91 | 92 | 93 | name 94 | Input name 95 | 96 | string 97 | 98 | 99 | 100 | 101 | 102 | 103 | response 104 | Output response 105 | 106 | string 107 | 108 | 109 | 110 | 111 | 112 | 113 | This response indicates that the `say_hello` WPS Process requires one 114 | parameter `name`. Execute the `say_hello` WPS Process with the URL:: 115 | 116 | http://127.0.0.1:5000/wps?service=WPS&request=Execute&version=1.0.0& 117 | identifier=say_hello&datainputs=name=Luis 118 | 119 | You should see a response like:: 120 | 121 | 122 | 128 | 129 | say_hello 130 | Process Say Hello 131 | 132 | 133 | PyWPS Process finished 134 | 135 | 136 | 137 | response 138 | Output response 139 | 140 | Hello Luis 142 | 143 | 144 | 145 | 146 | 147 | NB it is recommended to use HTTP POST requests for invoking WPS Execute operations as 148 | normally `DataInputs` will be more complex. 149 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Installation 3 | ============ 4 | 5 | The app depends on PyWPS and several other libraries that are listed in 6 | ``requirements.txt``. It is advisable to run it using a python virtualenv to prevent package instalation problems:: 7 | 8 | $ virtualenv -p python3 pywps_flask_env 9 | $ cd pywps_flask_dir 10 | $ . bin/activate 11 | $ git clone https://github.com/geopython/pywps-flask 12 | $ cd pywps-flask 13 | $ pip3 install -r requirements.txt 14 | 15 | 16 | If python virtualenv is not an option:: 17 | 18 | $ git clone https://github.com/geopython/pywps-flask 19 | $ cd pywps-flask 20 | $ pip3 install -r requirements.txt 21 | 22 | 23 | 24 | For Debian based systems you will need to install GDAL with:: 25 | 26 | $ sudo apt-get install python3-gdal 27 | 28 | 29 | When using only using `requirement.txt`, the `pywps-flask` will run for the directory that was pulled from github, for a system wise installation is it advisable to use `setup.py`:: 30 | 31 | $ git clone https://github.com/geopython/pywps-flask 32 | $ cd pywps-flask 33 | $ python3 setup.py install 34 | 35 | 36 | ======= 37 | Running 38 | ======= 39 | 40 | Simply run the python file:: 41 | 42 | $ python3 demo.py -a 43 | 44 | The flag `-a` will bind to the ip range `0.0.0.0` and is normally the safest option access to `pypwps-flask` 45 | 46 | The `-d` option will run pywps-flask as daemon and to stop it is necessary to determine the PID and kill it, one trick is to use fuser to determine PID, and then use it to kill the process:: 47 | 48 | $ fuser tcp/5000 49 | $ kill -15 50 | 51 | 52 | 53 | ============== 54 | Docker images 55 | ============== 56 | 57 | The docker folder contains 2 subfolders, each subfolder contains a differente pywps implementation. 58 | 59 | Folder ``flask`` has the default pywps-flask implementation using only Flask while folder ``nginx`` implements pywps using Nginx and Green unicorn as WSGI server. While folder ``ubuntu`` has the same images but using phusion image (ubuntu 18.04) 60 | 61 | 62 | 63 | Flask-Alpine (basic) 64 | -------------------- 65 | 66 | Basic pywps image is based on Alpine 3.8 and will run the native Flask service, this is not apropriate for production. The docker file can be found in: ``docker/alpine/flask/Dockerfile`` 67 | 68 | 69 | 70 | To build the image (inside the folder with the Dockerfile):: 71 | 72 | $ docker build -t pywps/flask-alpine . 73 | 74 | And to run it:: 75 | 76 | $ docker run -p 5000:5000 pywps/flask-alpine:latest 77 | 78 | 79 | Pywps will be available in the following URL:: 80 | 81 | $ http://localhost:5000 82 | 83 | 84 | 85 | Gunicorn-Alpine (production) 86 | ---------------------------- 87 | 88 | This image implements the previous ``flask-alpine image`` (you need to build it first, or it will be automatically pulled from dockerhub) but wrapping flask in a WSGI server (gunicorn) where each worker runs a an app. This image allows for the following environment variables: 89 | 90 | - GU_WORKERS - Numer or workers. Gunicorn uses a set of workers to run pywps (normally ``workers = (2 * cpu) + 1``). (default: 5) 91 | - GU_PORT - Port running Gunicorn (default:8081) 92 | 93 | 94 | 95 | Gunicorn-Alpine is locate in folder ``docker/alpine/gunicorn/Dockerfile`` 96 | 97 | This image can already be implemented in production but it is advisable to use Nginx for HTTP load balance and Gunicorn as WSGI server (see below) 98 | 99 | To build the image (inside the folder with the Dockerfile):: 100 | 101 | $ docker build -t pywps/gunicorn-alpine:latest . 102 | 103 | 104 | And to run it:: 105 | 106 | $ docker run -p 8081:8081 -it pywps/gunicorn-alpine:latest 107 | 108 | or:: 109 | 110 | $ docker run -e GU_WORKERS=10 -e GU_PORT=8082 -p 8082:8082 -it pywps/gunicorn-alpine:latest 111 | 112 | Pywps will be available at the following URL:: 113 | 114 | $ http://localhost:8082 115 | 116 | 117 | Nginx-Alpine 118 | ------------ 119 | 120 | This is the complete stack intented for production, to have a stack we require to use ``docker-compose`` 121 | to build two images: ``pywps/gunicorn-alpine:latest`` and ``pywps/nginx-alpine:latest`` 122 | 123 | Those images will be pulled from dockerhub, but they can compiled locally by building Flask-Alpine, Gunicron-Alpine and Nginx-Alpine, in this case only showing for nginx:: 124 | 125 | 126 | $ cd docker/alpine/nginx/Dockerfile 127 | $ docker build -t pywps/nginx-alpine:latest . 128 | 129 | Then the stack can be started using docker compose:: 130 | 131 | $ docker-compose up 132 | 133 | 134 | In this case pywps (only the WPS) will be avalable on:: 135 | 136 | 137 | http://localhost 138 | 139 | 140 | Flask-Ubuntu (basic) 141 | -------------------- 142 | 143 | The same as ``Flask-Ubuntu`` but using phusion image (ubuntu 18.04):: 144 | 145 | 146 | $ cd docker/ubuntu/flask 147 | $ docker build -t pywps/flask-ubuntu:latest . 148 | 149 | And to run it:: 150 | 151 | $ docker run -p 5000:5000 pywps/flask-ubuntu 152 | 153 | 154 | Nginx-Ubuntu (production) 155 | ------------------------- 156 | 157 | This image is based on ``Flask-Ubuntu`` and will require it (either build locally or pull from dockerhub). This image has Nginx and Gunicorn totally integrated as services in a docker image:: 158 | 159 | 160 | $ cd docker/ubuntu/nginx 161 | $ docker build -t pywps/nginx-ubuntu . 162 | 163 | And to run it:: 164 | 165 | $ docker run -p 80:80 pywps/nginx-ubuntu 166 | 167 | It is possible to set the number of Gunicorn workers: 168 | 169 | * GU_WORKERS - Numer or workers. (default: 5) 170 | 171 | e.g:: 172 | 173 | $ docker run -e GU_WORKERS=10 -p 80:80 pywps/nginx-ubuntu 174 | 175 | 176 | 177 | Volumes 178 | ------- 179 | 180 | 181 | Named volumes allow for container content to be available in the host system. The most important folders in pywps containers are: 182 | 183 | * /pywps-flask/logs 184 | * /pywps-flask/outputs 185 | * /pywps-flask/processes 186 | 187 | And file: 188 | * /pywps-flask/pywps.cfg 189 | 190 | Named volumes need to be created prior to ``docker run``:: 191 | 192 | $ docker volume create pywps_logs 193 | $ docker volume create pywps_outputs 194 | $ docker volume create pywps_processes 195 | 196 | To check the path on the host to volume and other information:: 197 | 198 | 199 | $ docker volume ls pywps_processes 200 | 201 | 202 | To run a docker will all the volumes available in the host:: 203 | 204 | $ docker run -p 5000:5000 -v pywps_logs:/pywps-flask/pywps_logs \ 205 | -v pywps_outputs:/pywps-flask/pywps_outputs \ 206 | -v pywps_processes:/pywps-flask/pywps_processes \ 207 | -v pywps_cfg:/pywps-flask/pywps.cfg pywps/flask-alpine:latest 208 | 209 | 210 | THE END 211 | ======= 212 | 213 | 214 | -------------------------------------------------------------------------------- /tests/test_execute.py: -------------------------------------------------------------------------------- 1 | """Test various processes 2 | """ 3 | import unittest 4 | import lxml.etree as etree 5 | import time 6 | 7 | from tests.common import validate, URL, get_response 8 | 9 | from tests.common import NAMESPACES 10 | 11 | class SayHello(unittest.TestCase): 12 | """Test sayhello process 13 | """ 14 | 15 | def setUp(self): 16 | 17 | self.schema_url = 'http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd' 18 | 19 | 20 | def test_valid(self): 21 | "GET Execute request" 22 | 23 | url = URL + '?service=wps&request=execute&identifier=say_hello&version=1.0.0&datainputs=name=ahoj' 24 | assert validate(url, self.schema_url) 25 | 26 | def test_valid_lineage(self): 27 | "GET Execute request, lineage=true" 28 | 29 | url = URL + '?service=wps&request=execute&identifier=say_hello&version=1.0.0&datainputs=name=ahoj&lineage=true' 30 | assert validate(url, self.schema_url) 31 | 32 | class Buffer(unittest.TestCase): 33 | """Test buffer process 34 | """ 35 | 36 | def setUp(self): 37 | 38 | self.schema_url = 'http://schemas.opengis.net/wps/1.0.0/wpsExecute_response.xsd' 39 | self.url = URL 40 | resp = get_response('http://localhost:5000/static/requests/execute_buffer_post.xml') 41 | self.request_data = resp.read() 42 | 43 | def test_valid(self): 44 | "POST Execute request" 45 | 46 | validate(self.url, self.schema_url, self.request_data) 47 | 48 | 49 | 50 | #def test_valid_lineage(self): 51 | # "GET Execute Buffer" 52 | 53 | # assert validate(url, self.schema_url) 54 | 55 | class SyncAndAsync(unittest.TestCase): 56 | """Test buffer sync and async 57 | """ 58 | 59 | def _get_request(self, url): 60 | 61 | request = get_response(url) 62 | request_data = request.read() 63 | return request_data 64 | 65 | def _get_response(self, request): 66 | 67 | response = get_response(URL, request) 68 | response_data = response.read() 69 | response_doc = etree.fromstring(response_data) 70 | 71 | return response_doc 72 | 73 | 74 | def test_sync(self): 75 | request = self._get_request('http://localhost:5000/static/requests/execute_buffer_post.xml') 76 | response = self._get_response(request) 77 | 78 | self.assertEqual( 79 | response.xpath('//wps:ExecuteResponse/wps:Process/ows:Identifier', 80 | namespaces=NAMESPACES)[0].text, 'buffer') 81 | 82 | self.assertEqual( 83 | response.xpath( 84 | '//wps:ExecuteResponse/wps:Status/wps:ProcessSucceeded', 85 | namespaces=NAMESPACES)[0].text, 86 | 'PyWPS Process GDAL Buffer process finished') 87 | 88 | self.assertEqual(len(response.xpath( 89 | '//wps:ExecuteResponse/wps:ProcessOutputs/wps:Output', 90 | namespaces=NAMESPACES)), 1) 91 | 92 | self.assertEqual(response.xpath( 93 | '//wps:ExecuteResponse/wps:ProcessOutputs/' 94 | 'wps:Output/wps:Data/wps:ComplexData', 95 | namespaces=NAMESPACES)[0].get('mimeType'), 96 | 'application/gml+xml') 97 | 98 | self.assertTrue(response.xpath( 99 | '//wps:ExecuteResponse/wps:ProcessOutputs/wps:Output/wps:Data/' 100 | 'wps:ComplexData/ogr:FeatureCollection', 101 | namespaces=NAMESPACES)) 102 | 103 | def test_async(self): 104 | request = self._get_request('http://localhost:5000/static/requests/execute_buffer_async.xml') 105 | response = self._get_response(request) 106 | 107 | self.assertEqual( 108 | response.xpath('//wps:ExecuteResponse/wps:Process/ows:Identifier', 109 | namespaces=NAMESPACES)[0].text, 'buffer') 110 | 111 | self.assertTrue(response.xpath( 112 | '//wps:ExecuteResponse/wps:Status/wps:ProcessAccepted', 113 | namespaces=NAMESPACES)) 114 | 115 | status_location = response.xpath( 116 | '//wps:ExecuteResponse', 117 | namespaces=NAMESPACES)[0].get('statusLocation') 118 | self.assertTrue(status_location) 119 | 120 | time.sleep(1) # make sure, buffer is done 121 | status = self._get_request(status_location) 122 | status_doc = etree.fromstring(status) 123 | 124 | self.assertTrue(status_doc.xpath( 125 | '//wps:ExecuteResponse/wps:Status/wps:ProcessSucceeded', 126 | namespaces=NAMESPACES)) 127 | 128 | self.assertTrue(status_doc.xpath( 129 | '//wps:ExecuteResponse/wps:ProcessOutputs/wps:Output/wps:Data/' 130 | 'wps:ComplexData/ogr:FeatureCollection', 131 | namespaces=NAMESPACES)) 132 | 133 | def test_async_reference(self): 134 | request = self._get_request('http://localhost:5000/static/requests/execute_buffer_async_reference.xml') 135 | response = self._get_response(request) 136 | 137 | self.assertTrue(response.xpath( 138 | '//wps:ExecuteResponse/wps:Status/wps:ProcessAccepted', 139 | namespaces=NAMESPACES)) 140 | 141 | status_location = response.xpath( 142 | '//wps:ExecuteResponse', 143 | namespaces=NAMESPACES)[0].get('statusLocation') 144 | self.assertTrue(status_location) 145 | 146 | time.sleep(1) # make sure, buffer is done 147 | status = self._get_request(status_location) 148 | status_doc = etree.fromstring(status) 149 | 150 | self.assertTrue(status_doc.xpath( 151 | '//wps:ExecuteResponse/wps:Status/wps:ProcessSucceeded', 152 | namespaces=NAMESPACES)) 153 | 154 | self.assertTrue(status_doc.xpath( 155 | '//wps:ExecuteResponse/wps:ProcessOutputs/wps:Output/' 156 | 'wps:Reference', 157 | namespaces=NAMESPACES)) 158 | 159 | self.assertEqual(status_doc.xpath( 160 | '//wps:ExecuteResponse/wps:ProcessOutputs/wps:Output/' 161 | 'wps:Reference', 162 | namespaces=NAMESPACES)[0].get('mimeType'), 'application/gml+xml') 163 | 164 | data_href = status_doc.xpath( 165 | '//wps:ExecuteResponse/wps:ProcessOutputs/wps:Output/' 166 | 'wps:Reference', 167 | namespaces=NAMESPACES)[0].get('{http://www.w3.org/1999/xlink}href') 168 | 169 | data = self._get_request(data_href) 170 | data_doc = etree.fromstring(data) 171 | 172 | self.assertTrue(data_doc.xpath('//ogr:FeatureCollection', 173 | namespaces=NAMESPACES)) 174 | 175 | def load_tests(loader=None, tests=None, pattern=None): 176 | if not loader: 177 | loader = unittest.TestLoader() 178 | suite_list = [ 179 | loader.loadTestsFromTestCase(SayHello), 180 | loader.loadTestsFromTestCase(Buffer) 181 | ] 182 | return unittest.TestSuite(suite_list) 183 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | set I18NSPHINXOPTS=%SPHINXOPTS% . 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. xml to make Docutils-native XML files 37 | echo. pseudoxml to make pseudoxml-XML files for display purposes 38 | echo. linkcheck to check all external links for integrity 39 | echo. doctest to run all doctests embedded in the documentation if enabled 40 | echo. coverage to run coverage check of the documentation if enabled 41 | goto end 42 | ) 43 | 44 | if "%1" == "clean" ( 45 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 46 | del /q /s %BUILDDIR%\* 47 | goto end 48 | ) 49 | 50 | 51 | REM Check if sphinx-build is available and fallback to Python version if any 52 | %SPHINXBUILD% 1>NUL 2>NUL 53 | if errorlevel 9009 goto sphinx_python 54 | goto sphinx_ok 55 | 56 | :sphinx_python 57 | 58 | set SPHINXBUILD=python -m sphinx.__init__ 59 | %SPHINXBUILD% 2> nul 60 | if errorlevel 9009 ( 61 | echo. 62 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 63 | echo.installed, then set the SPHINXBUILD environment variable to point 64 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 65 | echo.may add the Sphinx directory to PATH. 66 | echo. 67 | echo.If you don't have Sphinx installed, grab it from 68 | echo.http://sphinx-doc.org/ 69 | exit /b 1 70 | ) 71 | 72 | :sphinx_ok 73 | 74 | 75 | if "%1" == "html" ( 76 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 80 | goto end 81 | ) 82 | 83 | if "%1" == "dirhtml" ( 84 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 88 | goto end 89 | ) 90 | 91 | if "%1" == "singlehtml" ( 92 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 93 | if errorlevel 1 exit /b 1 94 | echo. 95 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 96 | goto end 97 | ) 98 | 99 | if "%1" == "pickle" ( 100 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 101 | if errorlevel 1 exit /b 1 102 | echo. 103 | echo.Build finished; now you can process the pickle files. 104 | goto end 105 | ) 106 | 107 | if "%1" == "json" ( 108 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 109 | if errorlevel 1 exit /b 1 110 | echo. 111 | echo.Build finished; now you can process the JSON files. 112 | goto end 113 | ) 114 | 115 | if "%1" == "htmlhelp" ( 116 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 117 | if errorlevel 1 exit /b 1 118 | echo. 119 | echo.Build finished; now you can run HTML Help Workshop with the ^ 120 | .hhp project file in %BUILDDIR%/htmlhelp. 121 | goto end 122 | ) 123 | 124 | if "%1" == "qthelp" ( 125 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 129 | .qhcp project file in %BUILDDIR%/qthelp, like this: 130 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\PyWPS-Demo.qhcp 131 | echo.To view the help file: 132 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\PyWPS-Demo.ghc 133 | goto end 134 | ) 135 | 136 | if "%1" == "devhelp" ( 137 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. 141 | goto end 142 | ) 143 | 144 | if "%1" == "epub" ( 145 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 149 | goto end 150 | ) 151 | 152 | if "%1" == "latex" ( 153 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 157 | goto end 158 | ) 159 | 160 | if "%1" == "latexpdf" ( 161 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 162 | cd %BUILDDIR%/latex 163 | make all-pdf 164 | cd %~dp0 165 | echo. 166 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 167 | goto end 168 | ) 169 | 170 | if "%1" == "latexpdfja" ( 171 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 172 | cd %BUILDDIR%/latex 173 | make all-pdf-ja 174 | cd %~dp0 175 | echo. 176 | echo.Build finished; the PDF files are in %BUILDDIR%/latex. 177 | goto end 178 | ) 179 | 180 | if "%1" == "text" ( 181 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 182 | if errorlevel 1 exit /b 1 183 | echo. 184 | echo.Build finished. The text files are in %BUILDDIR%/text. 185 | goto end 186 | ) 187 | 188 | if "%1" == "man" ( 189 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 190 | if errorlevel 1 exit /b 1 191 | echo. 192 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 193 | goto end 194 | ) 195 | 196 | if "%1" == "texinfo" ( 197 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 198 | if errorlevel 1 exit /b 1 199 | echo. 200 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 201 | goto end 202 | ) 203 | 204 | if "%1" == "gettext" ( 205 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 206 | if errorlevel 1 exit /b 1 207 | echo. 208 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 209 | goto end 210 | ) 211 | 212 | if "%1" == "changes" ( 213 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 214 | if errorlevel 1 exit /b 1 215 | echo. 216 | echo.The overview file is in %BUILDDIR%/changes. 217 | goto end 218 | ) 219 | 220 | if "%1" == "linkcheck" ( 221 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 222 | if errorlevel 1 exit /b 1 223 | echo. 224 | echo.Link check complete; look for any errors in the above output ^ 225 | or in %BUILDDIR%/linkcheck/output.txt. 226 | goto end 227 | ) 228 | 229 | if "%1" == "doctest" ( 230 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 231 | if errorlevel 1 exit /b 1 232 | echo. 233 | echo.Testing of doctests in the sources finished, look at the ^ 234 | results in %BUILDDIR%/doctest/output.txt. 235 | goto end 236 | ) 237 | 238 | if "%1" == "coverage" ( 239 | %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage 240 | if errorlevel 1 exit /b 1 241 | echo. 242 | echo.Testing of coverage in the sources finished, look at the ^ 243 | results in %BUILDDIR%/coverage/python.txt. 244 | goto end 245 | ) 246 | 247 | if "%1" == "xml" ( 248 | %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml 249 | if errorlevel 1 exit /b 1 250 | echo. 251 | echo.Build finished. The XML files are in %BUILDDIR%/xml. 252 | goto end 253 | ) 254 | 255 | if "%1" == "pseudoxml" ( 256 | %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml 257 | if errorlevel 1 exit /b 1 258 | echo. 259 | echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. 260 | goto end 261 | ) 262 | 263 | :end 264 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help 23 | help: 24 | @echo "Please use \`make ' where is one of" 25 | @echo " html to make standalone HTML files" 26 | @echo " dirhtml to make HTML files named index.html in directories" 27 | @echo " singlehtml to make a single large HTML file" 28 | @echo " pickle to make pickle files" 29 | @echo " json to make JSON files" 30 | @echo " htmlhelp to make HTML files and a HTML help project" 31 | @echo " qthelp to make HTML files and a qthelp project" 32 | @echo " applehelp to make an Apple Help Book" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | @echo " coverage to run coverage check of the documentation (if enabled)" 49 | 50 | .PHONY: clean 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | .PHONY: html 55 | html: 56 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 57 | @echo 58 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 59 | 60 | .PHONY: dirhtml 61 | dirhtml: 62 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 63 | @echo 64 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 65 | 66 | .PHONY: singlehtml 67 | singlehtml: 68 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 69 | @echo 70 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 71 | 72 | .PHONY: pickle 73 | pickle: 74 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 75 | @echo 76 | @echo "Build finished; now you can process the pickle files." 77 | 78 | .PHONY: json 79 | json: 80 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 81 | @echo 82 | @echo "Build finished; now you can process the JSON files." 83 | 84 | .PHONY: htmlhelp 85 | htmlhelp: 86 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 87 | @echo 88 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 89 | ".hhp project file in $(BUILDDIR)/htmlhelp." 90 | 91 | .PHONY: qthelp 92 | qthelp: 93 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 94 | @echo 95 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 96 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 97 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PyWPS-Demo.qhcp" 98 | @echo "To view the help file:" 99 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PyWPS-Demo.qhc" 100 | 101 | .PHONY: applehelp 102 | applehelp: 103 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 104 | @echo 105 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 106 | @echo "N.B. You won't be able to view it unless you put it in" \ 107 | "~/Library/Documentation/Help or install it in your application" \ 108 | "bundle." 109 | 110 | .PHONY: devhelp 111 | devhelp: 112 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 113 | @echo 114 | @echo "Build finished." 115 | @echo "To view the help file:" 116 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PyWPS-Demo" 117 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PyWPS-Demo" 118 | @echo "# devhelp" 119 | 120 | .PHONY: epub 121 | epub: 122 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 123 | @echo 124 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 125 | 126 | .PHONY: latex 127 | latex: 128 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 129 | @echo 130 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 131 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 132 | "(use \`make latexpdf' here to do that automatically)." 133 | 134 | .PHONY: latexpdf 135 | latexpdf: 136 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 137 | @echo "Running LaTeX files through pdflatex..." 138 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 139 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 140 | 141 | .PHONY: latexpdfja 142 | latexpdfja: 143 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 144 | @echo "Running LaTeX files through platex and dvipdfmx..." 145 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 146 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 147 | 148 | .PHONY: text 149 | text: 150 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 151 | @echo 152 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 153 | 154 | .PHONY: man 155 | man: 156 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 157 | @echo 158 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 159 | 160 | .PHONY: texinfo 161 | texinfo: 162 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 163 | @echo 164 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 165 | @echo "Run \`make' in that directory to run these through makeinfo" \ 166 | "(use \`make info' here to do that automatically)." 167 | 168 | .PHONY: info 169 | info: 170 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 171 | @echo "Running Texinfo files through makeinfo..." 172 | make -C $(BUILDDIR)/texinfo info 173 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 174 | 175 | .PHONY: gettext 176 | gettext: 177 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 178 | @echo 179 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 180 | 181 | .PHONY: changes 182 | changes: 183 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 184 | @echo 185 | @echo "The overview file is in $(BUILDDIR)/changes." 186 | 187 | .PHONY: linkcheck 188 | linkcheck: 189 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 190 | @echo 191 | @echo "Link check complete; look for any errors in the above output " \ 192 | "or in $(BUILDDIR)/linkcheck/output.txt." 193 | 194 | .PHONY: doctest 195 | doctest: 196 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 197 | @echo "Testing of doctests in the sources finished, look at the " \ 198 | "results in $(BUILDDIR)/doctest/output.txt." 199 | 200 | .PHONY: coverage 201 | coverage: 202 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 203 | @echo "Testing of coverage in the sources finished, look at the " \ 204 | "results in $(BUILDDIR)/coverage/python.txt." 205 | 206 | .PHONY: xml 207 | xml: 208 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 209 | @echo 210 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 211 | 212 | .PHONY: pseudoxml 213 | pseudoxml: 214 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 215 | @echo 216 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 217 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # PyWPS-Flask documentation build configuration file, created by 4 | # sphinx-quickstart on Thu Aug 11 21:27:33 2016. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | import sys 16 | import os 17 | 18 | # If extensions (or modules to document with autodoc) are in another directory, 19 | # add these directories to sys.path here. If the directory is relative to the 20 | # documentation root, use os.path.abspath to make it absolute, like shown here. 21 | #sys.path.insert(0, os.path.abspath('.')) 22 | 23 | # -- General configuration ------------------------------------------------ 24 | 25 | # If your documentation needs a minimal Sphinx version, state it here. 26 | #needs_sphinx = '1.0' 27 | 28 | # Add any Sphinx extension module names here, as strings. They can be 29 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 30 | # ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.doctest', 34 | 'sphinx.ext.intersphinx', 35 | 'sphinx.ext.todo', 36 | 'sphinx.ext.coverage', 37 | 'sphinx.ext.ifconfig', 38 | 'sphinx.ext.linkcode' 39 | ] 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # source_suffix = ['.rst', '.md'] 47 | source_suffix = '.rst' 48 | 49 | # The encoding of source files. 50 | #source_encoding = 'utf-8-sig' 51 | 52 | # The master toctree document. 53 | master_doc = 'index' 54 | 55 | # General information about the project. 56 | project = u'PyWPS-Flask' 57 | copyright = u'2016, PyWPS Development Team' 58 | author = u'PyWPS Development Team' 59 | 60 | # The version info for the project you're documenting, acts as replacement for 61 | # |version| and |release|, also used in various other places throughout the 62 | # built documents. 63 | # 64 | # The short X.Y version. 65 | version = u'4.2' 66 | # The full version, including alpha/beta/rc tags. 67 | release = u'4.2.1' 68 | 69 | # The language for content autogenerated by Sphinx. Refer to documentation 70 | # for a list of supported languages. 71 | # 72 | # This is also used if you do content translation via gettext catalogs. 73 | # Usually you set "language" from the command line for these cases. 74 | language = None 75 | 76 | # There are two options for replacing |today|: either, you set today to some 77 | # non-false value, then it is used: 78 | #today = '' 79 | # Else, today_fmt is used as the format for a strftime call. 80 | #today_fmt = '%B %d, %Y' 81 | 82 | # List of patterns, relative to source directory, that match files and 83 | # directories to ignore when looking for source files. 84 | exclude_patterns = ['_build'] 85 | 86 | # The reST default role (used for this markup: `text`) to use for all 87 | # documents. 88 | #default_role = None 89 | 90 | # If true, '()' will be appended to :func: etc. cross-reference text. 91 | #add_function_parentheses = True 92 | 93 | # If true, the current module name will be prepended to all description 94 | # unit titles (such as .. function::). 95 | #add_module_names = True 96 | 97 | # If true, sectionauthor and moduleauthor directives will be shown in the 98 | # output. They are ignored by default. 99 | #show_authors = False 100 | 101 | # The name of the Pygments (syntax highlighting) style to use. 102 | pygments_style = 'sphinx' 103 | 104 | # A list of ignored prefixes for module index sorting. 105 | #modindex_common_prefix = [] 106 | 107 | # If true, keep warnings as "system message" paragraphs in the built documents. 108 | #keep_warnings = False 109 | 110 | # If true, `todo` and `todoList` produce output, else they produce nothing. 111 | todo_include_todos = True 112 | 113 | 114 | # -- Options for HTML output ---------------------------------------------- 115 | 116 | # The theme to use for HTML and HTML Help pages. See the documentation for 117 | # a list of builtin themes. 118 | html_theme = 'alabaster' 119 | #html_logo = '_static/pywps.png' 120 | 121 | # Theme options are theme-specific and customize the look and feel of a theme 122 | # further. For a list of options available for each theme, see the 123 | # documentation. 124 | # alabaster settings 125 | html_theme_options = { 126 | 'show_related': True, 127 | 'travis_button': True, 128 | 'github_banner': True, 129 | 'github_user': 'geopython', 130 | 'github_repo': 'pywps-flask', 131 | 'github_button': True, 132 | 'logo': 'pywps.png', 133 | 'logo_name': False 134 | } 135 | 136 | # Add any paths that contain custom themes here, relative to this directory. 137 | #html_theme_path = [] 138 | 139 | # The name for this set of Sphinx documents. If None, it defaults to 140 | # " v documentation". 141 | #html_title = None 142 | 143 | # A shorter title for the navigation bar. Default is the same as html_title. 144 | #html_short_title = None 145 | 146 | # The name of an image file (relative to this directory) to place at the top 147 | # of the sidebar. 148 | #html_logo = None 149 | 150 | # The name of an image file (relative to this directory) to use as a favicon of 151 | # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 152 | # pixels large. 153 | #html_favicon = None 154 | 155 | # Add any paths that contain custom static files (such as style sheets) here, 156 | # relative to this directory. They are copied after the builtin static files, 157 | # so a file named "default.css" will overwrite the builtin "default.css". 158 | html_static_path = ['_static'] 159 | 160 | # Add any extra paths that contain custom files (such as robots.txt or 161 | # .htaccess) here, relative to this directory. These files are copied 162 | # directly to the root of the documentation. 163 | #html_extra_path = [] 164 | 165 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 166 | # using the given strftime format. 167 | #html_last_updated_fmt = '%b %d, %Y' 168 | 169 | # If true, SmartyPants will be used to convert quotes and dashes to 170 | # typographically correct entities. 171 | #html_use_smartypants = True 172 | 173 | # Custom sidebar templates, maps document names to template names. 174 | # alabaster settings 175 | html_sidebars = { 176 | '**': [ 177 | 'about.html', 178 | 'navigation.html', 179 | 'searchbox.html', 180 | ] 181 | } 182 | 183 | # Additional templates that should be rendered to pages, maps page names to 184 | # template names. 185 | #html_additional_pages = {} 186 | 187 | # If false, no module index is generated. 188 | #html_domain_indices = True 189 | 190 | # If false, no index is generated. 191 | #html_use_index = True 192 | 193 | # If true, the index is split into individual pages for each letter. 194 | #html_split_index = False 195 | 196 | # If true, links to the reST sources are added to the pages. 197 | # html_show_sourcelink = True 198 | 199 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 200 | #html_show_sphinx = True 201 | 202 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 203 | #html_show_copyright = True 204 | 205 | # If true, an OpenSearch description file will be output, and all pages will 206 | # contain a tag referring to it. The value of this option must be the 207 | # base URL from which the finished HTML is served. 208 | #html_use_opensearch = '' 209 | 210 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 211 | #html_file_suffix = None 212 | 213 | # Language to be used for generating the HTML full-text search index. 214 | # Sphinx supports the following languages: 215 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 216 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 217 | #html_search_language = 'en' 218 | 219 | # A dictionary with options for the search language support, empty by default. 220 | # Now only 'ja' uses this config value 221 | #html_search_options = {'type': 'default'} 222 | 223 | # The name of a javascript file (relative to the configuration directory) that 224 | # implements a search results scorer. If empty, the default will be used. 225 | #html_search_scorer = 'scorer.js' 226 | 227 | # Output file base name for HTML help builder. 228 | htmlhelp_basename = 'PyWPS-Flaskdoc' 229 | 230 | # -- Options for LaTeX output --------------------------------------------- 231 | 232 | latex_elements = { 233 | # The paper size ('letterpaper' or 'a4paper'). 234 | #'papersize': 'letterpaper', 235 | 236 | # The font size ('10pt', '11pt' or '12pt'). 237 | #'pointsize': '10pt', 238 | 239 | # Additional stuff for the LaTeX preamble. 240 | #'preamble': '', 241 | 242 | # Latex figure (float) alignment 243 | #'figure_align': 'htbp', 244 | } 245 | 246 | # Grouping the document tree into LaTeX files. List of tuples 247 | # (source start file, target name, title, 248 | # author, documentclass [howto, manual, or own class]). 249 | latex_documents = [ 250 | (master_doc, 'PyWPS-Flask.tex', u'PyWPS-Flask Documentation', 251 | u'PyWPS Development Team', 'manual'), 252 | ] 253 | 254 | # The name of an image file (relative to this directory) to place at the top of 255 | # the title page. 256 | #latex_logo = None 257 | 258 | # For "manual" documents, if this is true, then toplevel headings are parts, 259 | # not chapters. 260 | #latex_use_parts = False 261 | 262 | # If true, show page references after internal links. 263 | #latex_show_pagerefs = False 264 | 265 | # If true, show URL addresses after external links. 266 | #latex_show_urls = False 267 | 268 | # Documents to append as an appendix to all manuals. 269 | #latex_appendices = [] 270 | 271 | # If false, no module index is generated. 272 | #latex_domain_indices = True 273 | 274 | 275 | # -- Options for manual page output --------------------------------------- 276 | 277 | # One entry per manual page. List of tuples 278 | # (source start file, name, description, authors, manual section). 279 | man_pages = [ 280 | (master_doc, 'pywps-flask', u'PyWPS-Flask Documentation', 281 | [author], 1) 282 | ] 283 | 284 | # If true, show URL addresses after external links. 285 | #man_show_urls = False 286 | 287 | 288 | # -- Options for Texinfo output ------------------------------------------- 289 | 290 | # Grouping the document tree into Texinfo files. List of tuples 291 | # (source start file, target name, title, author, 292 | # dir menu entry, description, category) 293 | texinfo_documents = [ 294 | (master_doc, 'PyWPS-Flask', u'PyWPS-Flask Documentation', 295 | author, 'PyWPS-Flask', 'One line description of project.', 296 | 'Miscellaneous'), 297 | ] 298 | 299 | # Documents to append as an appendix to all manuals. 300 | #texinfo_appendices = [] 301 | 302 | # If false, no module index is generated. 303 | #texinfo_domain_indices = True 304 | 305 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 306 | #texinfo_show_urls = 'footnote' 307 | 308 | # If true, do not generate a @detailmenu in the "Top" node's menu. 309 | #texinfo_no_detailmenu = False 310 | 311 | 312 | # Example configuration for intersphinx: refer to the Python standard library. 313 | intersphinx_mapping = {'https://docs.python.org/': None} 314 | 315 | def linkcode_resolve(domain, info): 316 | if domain != 'py': 317 | return None 318 | if not info['module']: 319 | return None 320 | filename = info['module'].replace('.', '/') 321 | return "http://github.com/geopython/pywps-flask/blob/master/%s.py" % filename 322 | --------------------------------------------------------------------------------