Add support for the django framework (#630)

* Add initial support with a django skeleton project

* Start the django server from pygeoapi configuration and push to settings

* Add views to django routes

* fixed flake8 problems

* added jobs endpoints

* added EDR endpoints

* renamed jobs endpoints and functions

* fixed flake8

* Bump django version to support 3 and 4

* Fix api request instance


Fix api request instance

* fixed stac catalog path request

* fixed problem with static

* get parameters from django requests

* Add sample project for django application

* Fix flake8 errors

* added documentation for Django downstream project

Co-authored-by: Luca Delucchi <lucadeluge@gmail.com>
Co-authored-by: Krishna Lodha <krishnaglodha@gmail.com>
This commit is contained in:
Francesco Bartoli
2022-08-27 18:01:32 +02:00
committed by GitHub
parent 6faad638c2
commit 98bfdd5cf2
21 changed files with 2290 additions and 12 deletions
+2
View File
@@ -114,3 +114,5 @@ ENV/
*.code-workspace
.DS_Store
pygeoapi/django_pygeoapi/sample_project/db.sqlite3
+103
View File
@@ -0,0 +1,103 @@
.. _downstream:
Downstream Projects
=======
Downstreaming `pygeoapi` project with various python frameworks.
------------------
In this page, we'll demonstrate how to downstream `pygeoapi` project with various python frameworks.
Django
^^^^^^
Django is a high-level Python web framework that encourages rapid development and clean, pragmatic design. Click `here <https://www.djangoproject.com/>`_ to read more about Django.
In this section we create a sample django project and use `pygeoapi` package as a pluggable django application and serve all the capabilities of `pygeoapi` using Django. For the truly impatient developers, there is a Django `sample_project` in the source code.
To create everything from scratch please follow these steps :
- Create a Project folder and create a fresh virtual environment using your preferred tool. e.g.
.. code-block:: bash
python3 -m venv env
Once created, activate it.
- Install the following dependencies
.. code-block:: bash
pip install Django pygeoapi
- Create a django project in a directory and cd into it.
.. code-block:: python
django-admin startproject sampleproject
cd /sampleproject
- Download `pygeoapi-config.yml` using
.. code-block:: bash
curl -O https://raw.githubusercontent.com/geopython/pygeoapi/django_pygeoapi/sample_project/pygeoapi-config.yml
and put it in the same folder at root level.
- Set environment variable
.. code-block:: bash
export PYGEOAPI_CONFIG=pygeoapi-config.yml
export PYGEOAPI_OPENAPI=example-openapi.yml
- Run `python manage.py collectstatic` to get all static files.
- Generate OpenAPI document using following `pygeoapi` command
.. code-block:: bash
pygeoapi openapi generate $PYGEOAPI_CONFIG --output-file $PYGEOAPI_OPENAPI
- Update Django `sampleproject/settings.py` file as per following
.. code-block:: python
import os
from pygeoapi.django_app import config
INSTALLED_APPS = [
# other apps
....
#pygeoapi app
'pygeoapi'
]
# Put following setting after STATIC_URL
STATIC_ROOT = os.path.join( BASE_DIR / 'assets')
# Specific pygeoapi setting
PYGEOAPI_CONFIG = config()
...
- Update Django `sampleproject/urls.py` file to run pygeoapi at e.g. `pga` path
.. code-block:: python
from django.contrib import admin
from django.urls import path, include
from pygeoapi.django_pygeoapi import urls
urlpatterns = [
path('admin/', admin.site.urls),
path('pga/', include(urls)) # added here
]
- Update pygeoapi `pygeoapi-config.yml` file with following settings
1. Update the `url` property under `server` in `pygeoapi-config.yml` accordingly to your django project url. e.g. In this case the path set is `pga` .
2. Update all data paths e.g. `tests/data/ne_110m_lakes.geojson` to match with the absolute path of the pygeoapi project directory.
- Run Django project using `python manage.py runserver`. Once server starts, head over to `localhost:8000/pga` to see `pygeoapi` running.
+1
View File
@@ -23,6 +23,7 @@ pygeoapi |release| documentation
configuration
administration
running
downstream
running-with-docker
tour
openapi
+880
View File
@@ -0,0 +1,880 @@
components:
parameters:
f:
description: The optional f parameter indicates the output format which the
server shall provide as part of the response document. The default format
is GeoJSON.
explode: false
in: query
name: f
required: false
schema:
default: json
enum:
- json
- html
- jsonld
type: string
style: form
lang:
description: The optional lang parameter instructs the server return a response
in a certain language, if supported. If the language is not among the available
values, the Accept-Language header language will be used if it is supported.
If the header is missing, the default server language is used. Note that providers
may only support a single language (or often no language at all), that can
be different from the server language. Language strings can be written in
a complex (e.g. "fr-CA,fr;q=0.9,en-US;q=0.8,en;q=0.7"), simple (e.g. "de")
or locale-like (e.g. "de-CH" or "fr_BE") fashion.
in: query
name: lang
required: false
schema:
default: en-US
enum:
- en-US
- fr-CA
type: string
properties:
description: The properties that should be included for each feature. The parameter
value is a comma-separated list of property names.
explode: false
in: query
name: properties
required: false
schema:
items:
type: string
type: array
style: form
skipGeometry:
description: This option can be used to skip response geometries for each feature.
explode: false
in: query
name: skipGeometry
required: false
schema:
default: false
type: boolean
style: form
startindex:
description: The optional startindex parameter indicates the index within the
result set from which the server shall begin presenting results in the response
document. The first element has an index of 0 (default).
explode: false
in: query
name: startindex
required: false
schema:
default: 0
minimum: 0
type: integer
style: form
responses:
'200':
description: successful operation
Queryables:
content:
application/json:
schema:
$ref: '#/components/schemas/queryables'
description: successful queryables operation
default:
content:
application/json:
schema:
$ref: http://schemas.opengis.net/ogcapi/processes/part1/1.0/openapi/schemas/exception.yaml
description: Unexpected error
schemas:
queryable:
properties:
description:
description: a human-readable narrative describing the queryable
type: string
language:
default:
- en
description: the language used for the title and description
type: string
queryable:
description: the token that may be used in a CQL predicate
type: string
title:
description: a human readable title for the queryable
type: string
type:
description: the data type of the queryable
type: string
type-ref:
description: a reference to the formal definition of the type
format: url
type: string
required:
- queryable
- type
type: object
queryables:
properties:
queryables:
items:
$ref: '#/components/schemas/queryable'
type: array
required:
- queryables
type: object
info:
contact:
email: you@example.org
name: Organization Name
url: https://pygeoapi.io
description: pygeoapi provides an API to geospatial data
license:
name: CC-BY 4.0 license
url: https://creativecommons.org/licenses/by/4.0/
termsOfService: https://creativecommons.org/licenses/by/4.0/
title: pygeoapi default instance
version: 0.13.dev0
x-keywords:
- geospatial
- data
- api
openapi: 3.0.2
paths:
/:
get:
description: Landing page
operationId: getLandingPage
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/LandingPage
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Landing page
tags:
- server
/collections:
get:
description: Collections
operationId: getCollections
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Collections
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Collections
tags:
- server
/collections/canada-metadata:
get:
description: Sample metadata records from open.canada.ca
operationId: describeCanada-metadataCollection
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Collection
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Open Canada sample data metadata
tags:
- canada-metadata
/collections/canada-metadata/items:
get:
description: Sample metadata records from open.canada.ca
operationId: getCanada-metadataFeatures
parameters:
- &id001
description: The optional f parameter indicates the output format which the
server shall provide as part of the response document. The default format
is GeoJSON.
explode: false
in: query
name: f
required: false
schema:
default: json
enum:
- json
- html
- jsonld
- csv
type: string
style: form
- &id002
description: The optional lang parameter instructs the server return a response
in a certain language, if supported. If the language is not among the available
values, the Accept-Language header language will be used if it is supported.
If the header is missing, the default server language is used. Note that
providers may only support a single language (or often no language at all),
that can be different from the server language. Language strings can be
written in a complex (e.g. "fr-CA,fr;q=0.9,en-US;q=0.8,en;q=0.7"), simple
(e.g. "de") or locale-like (e.g. "de-CH" or "fr_BE") fashion.
in: query
name: lang
required: false
schema:
default: en-US
enum:
- en-US
- fr-CA
type: string
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/bbox
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/limit
- description: The properties that should be included for each feature. The
parameter value is a comma-separated list of property names.
explode: false
in: query
name: properties
required: false
schema:
items:
enum:
- recordCreated
- recordUpdated
- type
- title
- description
- contactPoint
- associations
- externalId
- themes
- q
type: string
type: array
style: form
- $ref: '#/components/parameters/skipGeometry'
- $ref: https://raw.githubusercontent.com/opengeospatial/ogcapi-records/master/core/openapi/parameters/sortby.yaml
- $ref: '#/components/parameters/startindex'
- $ref: https://raw.githubusercontent.com/opengeospatial/ogcapi-records/master/core/openapi/parameters/q.yaml
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/datetime
- explode: false
in: query
name: recordCreated
required: false
schema:
type: string
style: form
- explode: false
in: query
name: recordUpdated
required: false
schema:
type: string
style: form
- explode: false
in: query
name: type
required: false
schema:
type: string
style: form
- explode: false
in: query
name: title
required: false
schema:
type: string
style: form
- explode: false
in: query
name: description
required: false
schema:
type: string
style: form
- explode: false
in: query
name: contactPoint
required: false
schema:
type: string
style: form
- explode: false
in: query
name: associations
required: false
schema:
type: string
style: form
- explode: false
in: query
name: externalId
required: false
schema:
type: string
style: form
- explode: false
in: query
name: themes
required: false
schema:
type: string
style: form
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Features
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Open Canada sample data items
tags:
- canada-metadata
/collections/canada-metadata/items/{featureId}:
get:
description: Sample metadata records from open.canada.ca
operationId: getCanada-metadataFeature
parameters:
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/featureId
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Feature
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Open Canada sample data item by id
tags:
- canada-metadata
/collections/canada-metadata/queryables:
get:
description: Sample metadata records from open.canada.ca
operationId: getCanada-metadataQueryables
parameters:
- *id001
- *id002
responses:
'200':
$ref: '#/components/responses/Queryables'
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Open Canada sample data queryables
tags:
- canada-metadata
/collections/gdps-temperature:
get:
description: Global Deterministic Prediction System sample
operationId: describeGdps-temperatureCollection
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Collection
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Global Deterministic Prediction System sample metadata
tags:
- gdps-temperature
/collections/gdps-temperature/coverage:
get:
description: Global Deterministic Prediction System sample
operationId: getGdps-temperatureCoverage
parameters:
- *id001
- *id002
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Features
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Global Deterministic Prediction System sample coverage
tags:
- gdps-temperature
/collections/gdps-temperature/coverage/domainset:
get:
description: Global Deterministic Prediction System sample
operationId: getGdps-temperatureCoverageDomainSet
parameters:
- *id001
- *id002
responses:
'200':
$ref: https://raw.githubusercontent.com/tomkralidis/ogcapi-coverages-1/fix-cis/yaml-unresolved/schemas/cis_1.1/domainSet.yaml
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Global Deterministic Prediction System sample coverage domain set
tags:
- gdps-temperature
/collections/gdps-temperature/coverage/rangetype:
get:
description: Global Deterministic Prediction System sample
operationId: getGdps-temperatureCoverageRangeType
parameters:
- *id001
- *id002
responses:
'200':
$ref: https://raw.githubusercontent.com/tomkralidis/ogcapi-coverages-1/fix-cis/yaml-unresolved/schemas/cis_1.1/rangeType.yaml
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Global Deterministic Prediction System sample coverage range type
tags:
- gdps-temperature
/collections/lakes:
get:
description: lakes of the world, public domain
operationId: describeLakesCollection
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Collection
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Large Lakes metadata
tags:
- lakes
/collections/lakes/items:
get:
description: lakes of the world, public domain
operationId: getLakesFeatures
parameters:
- *id001
- *id002
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/bbox
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/limit
- description: The properties that should be included for each feature. The
parameter value is a comma-separated list of property names.
explode: false
in: query
name: properties
required: false
schema:
items:
enum:
- id
- scalerank
- name
- name_alt
- admin
- featureclass
type: string
type: array
style: form
- $ref: '#/components/parameters/skipGeometry'
- $ref: https://raw.githubusercontent.com/opengeospatial/ogcapi-records/master/core/openapi/parameters/sortby.yaml
- $ref: '#/components/parameters/startindex'
- explode: false
in: query
name: id
required: false
schema:
type: string
style: form
- explode: false
in: query
name: scalerank
required: false
schema:
type: string
style: form
- explode: false
in: query
name: name
required: false
schema:
type: string
style: form
- explode: false
in: query
name: name_alt
required: false
schema:
type: string
style: form
- explode: false
in: query
name: admin
required: false
schema:
type: string
style: form
- explode: false
in: query
name: featureclass
required: false
schema:
type: string
style: form
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Features
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Large Lakes items
tags:
- lakes
/collections/lakes/items/{featureId}:
get:
description: lakes of the world, public domain
operationId: getLakesFeature
parameters:
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/featureId
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Feature
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Large Lakes item by id
tags:
- lakes
/collections/lakes/queryables:
get:
description: lakes of the world, public domain
operationId: getLakesQueryables
parameters:
- *id001
- *id002
responses:
'200':
$ref: '#/components/responses/Queryables'
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Large Lakes queryables
tags:
- lakes
/collections/obs:
get:
description: My cool observations
operationId: describeObsCollection
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Collection
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Observations metadata
tags:
- obs
/collections/obs/items:
get:
description: My cool observations
operationId: getObsFeatures
parameters:
- *id001
- *id002
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/bbox
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/limit
- description: The properties that should be included for each feature. The
parameter value is a comma-separated list of property names.
explode: false
in: query
name: properties
required: false
schema:
items:
enum:
- id
- stn_id
- datetime
- value
- lat
- long
type: string
type: array
style: form
- $ref: '#/components/parameters/skipGeometry'
- $ref: https://raw.githubusercontent.com/opengeospatial/ogcapi-records/master/core/openapi/parameters/sortby.yaml
- $ref: '#/components/parameters/startindex'
- explode: false
in: query
name: id
required: false
schema:
type: string
style: form
- explode: false
in: query
name: stn_id
required: false
schema:
type: string
style: form
- explode: false
in: query
name: datetime
required: false
schema:
type: string
style: form
- explode: false
in: query
name: value
required: false
schema:
type: string
style: form
- explode: false
in: query
name: lat
required: false
schema:
type: string
style: form
- explode: false
in: query
name: long
required: false
schema:
type: string
style: form
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Features
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Observations items
tags:
- obs
/collections/obs/items/{featureId}:
get:
description: My cool observations
operationId: getObsFeature
parameters:
- $ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/parameters/featureId
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/Feature
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Observations item by id
tags:
- obs
/collections/obs/queryables:
get:
description: My cool observations
operationId: getObsQueryables
parameters:
- *id001
- *id002
responses:
'200':
$ref: '#/components/responses/Queryables'
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'404':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/NotFound
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: Get Observations queryables
tags:
- obs
/conformance:
get:
description: API conformance definition
operationId: getConformanceDeclaration
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ConformanceDeclaration
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
'500':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/ServerError
summary: API conformance definition
tags:
- server
/openapi:
get:
description: This document
operationId: getOpenapi
parameters:
- $ref: '#/components/parameters/f'
- $ref: '#/components/parameters/lang'
- description: UI to render the OpenAPI document
explode: false
in: query
name: ui
required: false
schema:
default: swagger
enum:
- swagger
- redoc
type: string
style: form
responses:
'200':
$ref: '#/components/responses/200'
'400':
$ref: http://schemas.opengis.net/ogcapi/features/part1/1.0/openapi/ogcapi-features-1.yaml#/components/responses/InvalidParameter
default:
$ref: '#/components/responses/default'
summary: This document
tags:
- server
/processes:
get:
description: Processes
operationId: getProcesses
parameters:
- $ref: '#/components/parameters/f'
responses:
'200':
$ref: http://schemas.opengis.net/ogcapi/processes/part1/1.0/openapi/responses/ProcessList.yaml
default:
$ref: '#/components/responses/default'
summary: Processes
tags:
- server
/processes/hello-world:
get:
description: An example process that takes a name as input, and echoes it back
as output. Intended to demonstrate a simple process with a single literal
input.
operationId: describeHello-worldProcess
parameters:
- $ref: '#/components/parameters/f'
responses:
'200':
$ref: '#/components/responses/200'
default:
$ref: '#/components/responses/default'
summary: Get process metadata
tags:
- hello-world
/processes/hello-world/execution:
post:
description: An example process that takes a name as input, and echoes it back
as output. Intended to demonstrate a simple process with a single literal
input.
operationId: executeHello-worldJob
requestBody:
content:
application/json:
example:
inputs:
message: An optional message.
name: World
schema:
$ref: http://schemas.opengis.net/ogcapi/processes/part1/1.0/openapi/schemas/execute.yaml
description: Mandatory execute request JSON
required: true
responses:
'200':
$ref: '#/components/responses/200'
'201':
$ref: http://schemas.opengis.net/ogcapi/processes/part1/1.0/openapi/responses/ExecuteAsync.yaml
'404':
$ref: http://schemas.opengis.net/ogcapi/processes/part1/1.0/openapi/responses/NotFound.yaml
'500':
$ref: http://schemas.opengis.net/ogcapi/processes/part1/1.0/openapi/responses/ServerError.yaml
default:
$ref: '#/components/responses/default'
summary: Process Hello World execution
tags:
- hello-world
/stac:
get:
description: SpatioTemporal Asset Catalog
operationId: getStacCatalog
parameters: []
responses:
'200':
$ref: '#/components/responses/200'
default:
$ref: '#/components/responses/default'
summary: SpatioTemporal Asset Catalog
tags:
- stac
servers:
- description: pygeoapi provides an API to geospatial data
url: http://localhost:5000
tags:
- description: pygeoapi provides an API to geospatial data
externalDocs:
description: information
url: https://example.org
name: server
- description: SpatioTemporal Asset Catalog
name: stac
- description: My cool observations
name: obs
- description: lakes of the world, public domain
name: lakes
- description: Global Deterministic Prediction System sample
name: gdps-temperature
- description: Sample metadata records from open.canada.ca
name: canada-metadata
- description: An example process that takes a name as input, and echoes it back as
output. Intended to demonstrate a simple process with a single literal input.
name: hello-world
+5 -1
View File
@@ -43,6 +43,7 @@ def cli():
@cli.command()
@click.option('--flask', 'server', flag_value="flask", default=True)
@click.option('--starlette', 'server', flag_value="starlette")
@click.option('--django', 'server', flag_value="django")
@click.pass_context
def serve(ctx, server):
"""Run the server with different daemon type (--flask is the default)"""
@@ -55,8 +56,11 @@ def serve(ctx, server):
from pygeoapi.starlette_app import serve as serve_starlette
ctx.forward(serve_starlette)
ctx.invoke(serve_starlette)
elif server == "django":
from pygeoapi.django_app import main as serve_django
ctx.invoke(serve_django)
else:
raise click.ClickException('--flask/--starlette is required')
raise click.ClickException('--flask/--starlette/--django is required')
cli.add_command(config)
+24 -11
View File
@@ -276,6 +276,8 @@ class APIRequest:
self._path_info = request.scope['path'].strip('/')
elif hasattr(request.headers, 'environ'):
self._path_info = request.headers.environ['PATH_INFO'].strip('/')
elif hasattr(request, 'path_info'):
self._path_info = request.path_info
# Extract locale from params or headers
self._raw_locale, self._locale = self._get_locale(request.headers,
@@ -309,17 +311,22 @@ class APIRequest:
# Set data from Flask request
api_req._data = request.data
elif hasattr(request, 'body'):
try:
import nest_asyncio
nest_asyncio.apply()
# Set data from Starlette request after async
# coroutine completion
# TODO: this now blocks, but once Flask v2 with async support
# has been implemented, with_data() can become async too
loop = asyncio.get_event_loop()
api_req._data = loop.run_until_complete(request.body())
except ModuleNotFoundError:
LOGGER.error("Module nest-asyncio not found")
if "django" in str(request.__class__):
# Set data from Django request
api_req._data = request.body
else:
try:
import nest_asyncio
nest_asyncio.apply()
# Set data from Starlette request after async
# coroutine completion
# TODO:
# this now blocks, but once Flask v2 with async support
# has been implemented, with_data() can become async too
loop = asyncio.get_event_loop()
api_req._data = loop.run_until_complete(request.body())
except ModuleNotFoundError:
LOGGER.error("Module nest-asyncio not found")
return api_req
@staticmethod
@@ -337,6 +344,12 @@ class APIRequest:
elif hasattr(request, 'query_params'):
# Return ImmutableMultiDict from Starlette request
return request.query_params
elif hasattr(request, 'GET'):
# Return QueryDict from Django GET request
return request.GET
elif hasattr(request, 'POST'):
# Return QueryDict from Django GET request
return request.POST
LOGGER.debug('No query parameters found')
return {}
View File
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
from pathlib import Path
def config():
from pygeoapi.util import yaml_load
if not os.environ.get('PYGEOAPI_CONFIG'):
raise RuntimeError('PYGEOAPI_CONFIG environment variable not set')
with open(os.environ.get('PYGEOAPI_CONFIG'), encoding='utf8') as fh:
CONFIG = yaml_load(fh)
return CONFIG
def main():
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_pygeoapi.settings')
django_app_path = Path(os.path.dirname(__file__))
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
CONFIG = config()
sys.argv = [str(django_app_path / "django_app.py"),
"runserver",
f"{CONFIG['server']['bind'].get('host')}:"
f"{CONFIG['server']['bind'].get('port')}"]
sys.path.append(str(django_app_path))
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample_project.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()
@@ -0,0 +1,275 @@
# =================================================================
#
# Authors: Tom Kralidis <tomkralidis@gmail.com>
#
# Copyright (c) 2020 Tom Kralidis
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# =================================================================
server:
bind:
host: 0.0.0.0
port: 8000
url: http://localhost:8000/sample-project
mimetype: application/json; charset=UTF-8
encoding: utf-8
gzip: false
languages:
# First language is the default language
- en-US
- fr-CA
# cors: true
pretty_print: true
limit: 10
# templates:
# path: /path/to/Jinja2/templates
# static: /path/to/static/folder # css/js/img
map:
url: https://tile.openstreetmap.org/{z}/{x}/{y}.png
attribution: '&copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
# manager:
# name: TinyDB
# connection: /tmp/pygeoapi-process-manager.db
# output_dir: /tmp/
# ogc_schemas_location: /opt/schemas.opengis.net
logging:
level: ERROR
#logfile: /tmp/pygeoapi.log
metadata:
identification:
title:
en: pygeoapi default instance
fr: instance par défaut de pygeoapi
description:
en: pygeoapi provides an API to geospatial data
fr: pygeoapi fournit une API aux données géospatiales
keywords:
en:
- geospatial
- data
- api
fr:
- géospatiale
- données
- api
keywords_type: theme
terms_of_service: https://creativecommons.org/licenses/by/4.0/
url: https://example.org
license:
name: CC-BY 4.0 license
url: https://creativecommons.org/licenses/by/4.0/
provider:
name: Organization Name
url: https://pygeoapi.io
contact:
name: Lastname, Firstname
position: Position Title
address: Mailing Address
city: City
stateorprovince: Administrative Area
postalcode: Zip or Postal Code
country: Country
phone: +xx-xxx-xxx-xxxx
fax: +xx-xxx-xxx-xxxx
email: you@example.org
url: Contact URL
hours: Mo-Fr 08:00-17:00
instructions: During hours of service. Off on weekends.
role: pointOfContact
resources:
obs:
type: collection
title: Observations
description: My cool observations
keywords:
- observations
- monitoring
context:
- datetime: https://schema.org/DateTime
- vocab: https://example.com/vocab#
stn_id: "vocab:stn_id"
value: "vocab:value"
links:
- type: text/csv
rel: canonical
title: data
href: https://github.com/mapserver/mapserver/blob/branch-7-0/msautotest/wxs/data/obs.csv
hreflang: en-US
- type: text/csv
rel: alternate
title: data
href: https://raw.githubusercontent.com/mapserver/mapserver/branch-7-0/msautotest/wxs/data/obs.csv
hreflang: en-US
extents:
spatial:
bbox: [-180,-90,180,90]
crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
temporal:
begin: 2000-10-30T18:24:39Z
end: 2007-10-30T08:57:29Z
providers:
- type: feature
name: CSV
data: tests/data/obs.csv
id_field: id
geometry:
x_field: long
y_field: lat
lakes:
type: collection
title:
en: Large Lakes
fr: Grands Lacs
description:
en: lakes of the world, public domain
fr: lacs du monde, domaine public
keywords:
en:
- lakes
- water bodies
fr:
- lacs
- plans d'eau
links:
- type: text/html
rel: canonical
title: information
href: http://www.naturalearthdata.com/
hreflang: en-US
extents:
spatial:
bbox: [-180,-90,180,90]
crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
temporal:
begin: 2011-11-11T11:11:11Z
end: null # or empty (either means open ended)
providers:
- type: feature
name: GeoJSON
data: tests/data/ne_110m_lakes.geojson
id_field: id
title_field: name
gdps-temperature:
type: collection
title: Global Deterministic Prediction System sample
description: Global Deterministic Prediction System sample
keywords:
- gdps
- global
extents:
spatial:
bbox: [-180,-90,180,90]
crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
links:
- type: text/html
rel: canonical
title: information
href: https://eccc-msc.github.io/open-data/msc-data/nwp_gdps/readme_gdps_en
hreflang: en-CA
providers:
- type: coverage
name: rasterio
data: tests/data/CMC_glb_TMP_TGL_2_latlon.15x.15_2020081000_P000.grib2
options:
DATA_ENCODING: COMPLEX_PACKING
format:
name: GRIB
mimetype: application/x-grib2
test-data:
type: stac-collection
title: pygeoapi test data
description: pygeoapi test data
keywords:
- poi
- portugal
links:
- type: text/html
rel: canonical
title: information
href: https://github.com/geopython/pygeoapi/tree/master/tests/data
hreflang: en-US
extents:
spatial:
bbox: [-180,-90,180,90]
crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
providers:
- type: stac
name: FileSystem
data: tests/data
file_types:
- .gpkg
- .sqlite
- .csv
- .grib2
- .tif
- .shp
canada-metadata:
type: collection
title:
en: Open Canada sample data
fr: Exemple de donn\u00e9es Canada Ouvert
description:
en: Sample metadata records from open.canada.ca
fr: Exemples d'enregistrements de m\u00e9tadonn\u00e9es sur ouvert.canada.ca
keywords:
en:
- canada
- open data
fr:
- canada
- donn\u00e9es ouvertes
links:
- type: text/html
rel: canonical
title: information
href: https://open.canada.ca/en/open-data
hreflang: en-CA
- type: text/html
rel: alternate
title: informations
href: https://ouvert.canada.ca/fr/donnees-ouvertes
hreflang: fr-CA
extents:
spatial:
bbox: [-180,-90,180,90]
crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84
providers:
- type: record
name: TinyDBCatalogue
data: tests/data/open.canada.ca/sample-records.tinydb
id_field: externalId
time_field: recordCreated
title_field: title
hello-world:
type: process
processor:
name: HelloWorld
@@ -0,0 +1,16 @@
"""
ASGI config for sample_project project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample_project.settings')
application = get_asgi_application()
@@ -0,0 +1,129 @@
"""
Django settings for sample_project project.
Generated by 'django-admin startproject' using Django 3.2.12.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
from pathlib import Path
from pygeoapi.django_app import config
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-um1sc7k4ovzdhp2r3kwz#%ta-l+kn$grk&9#7_(a0f)q$6u_ra' # noqa
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'sample_project.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'sample_project.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', # noqa
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', # noqa
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', # noqa
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', # noqa
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/'
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# pygeoapi
PYGEOAPI_CONFIG = config()
@@ -0,0 +1,24 @@
"""sample_project URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from pygeoapi.django_pygeoapi import urls as pygeoapi_urls
urlpatterns = [
path('admin/', admin.site.urls),
path('sample-project/', include(pygeoapi_urls)),
]
@@ -0,0 +1,16 @@
"""
WSGI config for sample_project project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample_project.settings')
application = get_wsgi_application()
+133
View File
@@ -0,0 +1,133 @@
"""
Django settings for django_pygeoapi project.
Generated by 'django-admin startproject' using Django 2.2.18.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
"""
import os
# pygeoapi specific
from pygeoapi.django_app import config
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = ")#cqm2jfato)gk((nm#%r7h&#n&aqy00_21pavfp=l8)4%cegb"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "django_pygeoapi.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "django_pygeoapi.wsgi.application"
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME":
"django.contrib.auth.password_validation.UserAttributeSimilarityValidator", # noqa
},
{
"NAME":
"django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME":
"django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME":
"django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static")
]
STATIC_ROOT = os.path.join(BASE_DIR, "assets")
STATIC_URL = "/static/"
# pygeoapi specific
PYGEOAPI_CONFIG = config()
+150
View File
@@ -0,0 +1,150 @@
"""django_pygeoapi URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.urls import (
path,
)
from django.conf import settings
from django.conf.urls.static import static
from . import views
urlpatterns = [
path("", views.landing_page, name="landing-page"),
path("openapi/", views.openapi, name="openapi"),
path("conformance/", views.conformance, name="conformance"),
path("collections/", views.collections, name="collections"),
path(
"collections/<str:collection_id>",
views.collections,
name="collection-detail",
),
path(
"collections/<str:collection_id>/queryables/",
views.collection_queryables,
name="collection-queryables",
),
path(
"collections/<str:collection_id>/items/",
views.collection_items,
name="collection-items",
),
path(
"collections/<str:collection_id>/items/<str:item_id>",
views.collection_item,
name="collection-item",
),
path(
"collections/<str:collection_id>/coverage/",
views.collection_coverage,
name="collection-coverage",
),
path(
"collections/<str:collection_id>/coverage/domainset/",
views.collection_coverage_domainset,
name="collection-coverage-domainset",
),
path(
"collections/<str:collection_id>/coverage/rangetype/",
views.collection_coverage_rangetype,
name="collection-coverage-rangetype",
),
path(
"collections/<str:collection_id>/tiles/",
views.collection_tiles,
name="collection-tiles",
),
path(
"collections/<str:collection_id>/tiles/<str:tileMatrixSetId>/metadata",
views.collection_tiles_metadata,
name="collection-tiles-metadata",
),
path(
"collections/<str:collection_id>/tiles/\
<str:tileMatrixSetId>/<str:tile_matrix>/<str:tileRow>/<str:tileCol>",
views.collection_item_tiles,
name="collection-item-tiles",
),
path(
"collections/<str:collection_id>/position",
views.get_collection_edr_query,
name="collection-edr-position",
),
path(
"collections/<str:collection_id>/area",
views.get_collection_edr_query,
name="collection-edr-area",
),
path(
"collections/<str:collection_id>/cube",
views.get_collection_edr_query,
name="collection-edr-cube",
),
path(
"collections/<str:collection_id>/trajectory",
views.get_collection_edr_query,
name="collection-edr-trajectory",
),
path(
"collections/<str:collection_id>/corridor",
views.get_collection_edr_query,
name="collection-edr-corridor",
),
path(
"collections/<str:collection_id>/instances/<str:instance_id>/position",
views.get_collection_edr_query,
name="collection-edr-instance-position",
),
path(
"collections/<str:collection_id>/instances/<str:instance_id>/area",
views.get_collection_edr_query,
name="collection-edr-instance-area",
),
path(
"collections/<str:collection_id>/instances/<str:instance_id>/cube",
views.get_collection_edr_query,
name="collection-edr-instance-cube",
),
path(
"collections/<str:collection_id>/instances/<str:instance_id>/trajectory", # noqa
views.get_collection_edr_query,
name="collection-edr-instance-trajectory",
),
path(
"collections/<str:collection_id>/instances/<str:instance_id>/corridor",
views.get_collection_edr_query,
name="collection-edr-instance-corridor",
),
path("processes/", views.processes, name="processes"),
path("processes/<str:process_id>", views.processes, name="process-detail"),
path("jobs/", views.jobs, name="jobs"),
path("jobs/<str:job_id>", views.jobs, name="job"),
path(
"jobs/<str:job_id>/results/",
views.job_results,
name="job-results",
),
path(
"jobs/<str:job_id>/results/<str:resource>",
views.job_results_resource,
name="job-results-resource",
),
path("stac/", views.stac_catalog_root, name="stac-catalog-root"),
path("stac/<str:path>", views.stac_catalog_path, name="stac-catalog-path"),
path(
"stac/search/", views.stac_catalog_search, name="stac-catalog-search"
),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
+449
View File
@@ -0,0 +1,449 @@
"""Integration module for Django"""
from typing import Tuple, Dict, Mapping, Optional
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from pygeoapi.api import API
from pygeoapi.openapi import get_oas
def landing_page(request: HttpRequest) -> HttpResponse:
"""
OGC API landing page endpoint
:request Django HTTP Request
:returns: Django HTTP Response
"""
response_ = _feed_response(request, "landing_page")
response = _to_django_response(*response_)
return response
def openapi(request: HttpRequest) -> HttpResponse:
"""
OpenAPI endpoint
:request Django HTTP Request
:returns: Django HTTP Response
"""
openapi_config = get_oas(settings.PYGEOAPI_CONFIG)
response_ = _feed_response(request, "openapi", openapi_config)
response = _to_django_response(*response_)
return response
def conformance(request: HttpRequest) -> HttpResponse:
"""
OGC API conformance endpoint
:request Django HTTP Request
:returns: Django HTTP Response
"""
response_ = _feed_response(request, "conformance")
response = _to_django_response(*response_)
return response
def collections(
request: HttpRequest,
collection_id: Optional[str] = None,
) -> HttpResponse:
"""
OGC API collections endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:returns: Django HTTP Response
"""
response_ = _feed_response(request, "describe_collections", collection_id)
response = _to_django_response(*response_)
return response
def collection_queryables(
request: HttpRequest,
collection_id: Optional[str] = None,
) -> HttpResponse:
"""
OGC API collections queryables endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:returns: Django HTTP Response
"""
response_ = _feed_response(
request, "get_collection_queryables", collection_id
)
response = _to_django_response(*response_)
return response
def collection_items(
request: HttpRequest,
collection_id: str,
) -> HttpResponse:
"""
OGC API collections items endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:returns: Django HTTP response
"""
response_ = _feed_response(
request,
"get_collection_items",
collection_id,
)
response = _to_django_response(*response_)
return response
def collection_item(
request: HttpRequest,
collection_id: str,
item_id: str,
) -> HttpResponse:
"""
OGC API collections items endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:param item_id: item identifier
:returns: Django HTTP response
"""
response_ = _feed_response(
request, "get_collection_item", collection_id, item_id
)
response = _to_django_response(*response_)
return response
def collection_coverage(
request: HttpRequest,
collection_id: str,
) -> HttpResponse:
"""
OGC API - Coverages coverage endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:returns: Django HTTP response
"""
response_ = _feed_response(
request, "get_collection_coverage", collection_id
)
response = _to_django_response(*response_)
return response
def collection_coverage_domainset(
request: HttpRequest,
collection_id: str,
) -> HttpResponse:
"""
OGC API - Coverages coverage domainset endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:returns: Django HTTP response
"""
response_ = _feed_response(
request, "get_collection_coverage_domainset", collection_id
)
response = _to_django_response(*response_)
return response
def collection_coverage_rangetype(
request: HttpRequest,
collection_id: str,
) -> HttpResponse:
"""
OGC API - Coverages coverage rangetype endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:returns: Django HTTP response
"""
response_ = _feed_response(
request, "get_collection_coverage_rangetype", collection_id
)
response = _to_django_response(*response_)
return response
def collection_tiles(
request: HttpRequest,
collection_id: str,
) -> HttpResponse:
"""
OGC API - Tiles collection tiles endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:returns: Django HTTP response
"""
response_ = _feed_response(request, "get_collection_tiles", collection_id)
response = _to_django_response(*response_)
return response
def collection_tiles_metadata(
request: HttpRequest,
collection_id: str,
tileMatrixSetId: str,
) -> HttpResponse:
"""
OGC API - Tiles collection tiles metadata endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:param tileMatrixSetId: identifier of tile matrix set
:returns: Django HTTP response
"""
response_ = _feed_response(
request,
"get_collection_tiles_metadata",
collection_id,
tileMatrixSetId,
)
response = _to_django_response(*response_)
return response
def collection_item_tiles(
request: HttpRequest,
collection_id: str,
tileMatrixSetId: str,
tileMatrix: str,
tileRow: str,
tileCol: str,
) -> HttpResponse:
"""
OGC API - Tiles collection tiles data endpoint
:request Django HTTP Request
:param collection_id: collection identifier
:param tileMatrixSetId: identifier of tile matrix set
:param tileMatrix: identifier of {z} matrix index
:param tileRow: identifier of {y} matrix index
:param tileCol: identifier of {x} matrix index
:returns: Django HTTP response
"""
response_ = _feed_response(
request,
"get_collection_tiles_metadata",
collection_id,
tileMatrixSetId,
tileMatrix,
tileRow,
tileCol,
)
response = _to_django_response(*response_)
return response
def processes(
request: HttpRequest,
process_id: Optional[str] = None,
) -> HttpResponse:
"""
OGC API - Processes description endpoint
:request Django HTTP Request
:param process_id: process identifier
:returns: Django HTTP response
"""
response_ = _feed_response(request, "describe_processes", process_id)
response = _to_django_response(*response_)
return response
def jobs(
request: HttpRequest,
job_id: Optional[str] = None,
) -> HttpResponse:
"""
OGC API - Jobs endpoint
:request Django HTTP Request
:param process_id: process identifier
:param job_id: job identifier
:returns: Django HTTP response
"""
response_ = _feed_response(request, "get_jobs", job_id)
response = _to_django_response(*response_)
return response
def job_results(
request: HttpRequest,
job_id: Optional[str] = None,
) -> HttpResponse:
"""
OGC API - Job result endpoint
:request Django HTTP Request
:param job_id: job identifier
:returns: Django HTTP response
"""
response_ = _feed_response(request, "get_job_result", job_id)
response = _to_django_response(*response_)
return response
def job_results_resource(
request: HttpRequest,
process_id: str,
job_id: str,
resource: str,
) -> HttpResponse:
"""
OGC API - Job result resource endpoint
:request Django HTTP Request
:param job_id: job identifier
:param resource: job resource
:returns: Django HTTP response
"""
response_ = _feed_response(
request,
"get_job_result_resource",
job_id,
resource
)
response = _to_django_response(*response_)
return response
def get_collection_edr_query(
request: HttpRequest,
collection_id: str,
instance_id: str,
) -> HttpResponse:
"""
OGC API - EDR endpoint
:request Django HTTP Request
:param job_id: job identifier
:param resource: job resource
:returns: Django HTTP response
"""
query_type = request.path.split('/')[-1]
response_ = _feed_response(
request,
"get_collection_edr_query",
collection_id,
instance_id,
query_type
)
response = _to_django_response(*response_)
return response
def stac_catalog_root(request: HttpRequest) -> HttpResponse:
"""
STAC root endpoint
:request Django HTTP Request
:returns: Django HTTP response
"""
response_ = _feed_response(request, "get_stac_root")
response = _to_django_response(*response_)
return response
def stac_catalog_path(
request: HttpRequest,
path: str,
) -> HttpResponse:
"""
STAC path endpoint
:request Django HTTP Request
:param path: path
:returns: Django HTTP response
"""
response_ = _feed_response(request, "get_stac_path", path)
response = _to_django_response(*response_)
return response
def stac_catalog_search(request: HttpRequest) -> HttpResponse:
pass
def _feed_response(
request: HttpRequest, api_definition: str, *args, **kwargs
) -> Tuple[Dict, int, str]:
"""Use pygeoapi api to process the input request"""
api_ = API(settings.PYGEOAPI_CONFIG)
api = getattr(api_, api_definition)
return api(request, *args, **kwargs)
def _to_django_response(
headers: Mapping,
status_code: int,
content: str,
) -> HttpResponse:
"""Convert API payload to a django response"""
response = HttpResponse(content, status=status_code)
for key, value in headers.items():
response[key] = value
return response
+16
View File
@@ -0,0 +1,16 @@
"""
WSGI config for django_pygeoapi project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_pygeoapi.settings")
application = get_wsgi_application()
+2
View File
@@ -0,0 +1,2 @@
Django <= 4, > 3
pydantic