├── .github └── workflows │ ├── deploy.docs.yml │ └── tests.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md └── workshop ├── content ├── docs │ ├── CNAME │ ├── advanced │ │ ├── administration.md │ │ ├── bridges.md │ │ ├── cloud.md │ │ ├── crs.md │ │ ├── downstream-applications.md │ │ ├── i18n.md │ │ ├── index.md │ │ ├── inspire.md │ │ ├── security-access-control.md │ │ ├── semantic-web-linked-data.md │ │ ├── seo.md │ │ └── ui-custom-templates.md │ ├── assets │ │ ├── favicon.ico │ │ ├── images │ │ │ ├── OGC_APIs_banner.jpg │ │ │ ├── collection-list.png │ │ │ ├── features-hyderabad.png │ │ │ ├── jupyter1.png │ │ │ ├── leaflet-estonia.png │ │ │ ├── leaflet-estonia2.png │ │ │ ├── leaflet-hyderabad.png │ │ │ ├── leaflet-hyderabad2.png │ │ │ ├── leaflet.png │ │ │ ├── macosx-airplay-disable.png │ │ │ ├── maps-response.png │ │ │ ├── new-connection.png │ │ │ ├── oaproc-squared-1.png │ │ │ ├── oaproc-squared-2.png │ │ │ ├── oaproc-squared-3.png │ │ │ ├── ogc-api-building-blocks.png │ │ │ ├── ogcapis.png │ │ │ ├── prepopulated-catalogues.png │ │ │ ├── pygeoapi-icon-notrans.png │ │ │ ├── pygeoapi-logo.png │ │ │ ├── qgis-addlayer.png │ │ │ ├── qgis-vtiles1.png │ │ │ ├── qgis-vtiles2-estonia.png │ │ │ ├── qgis-vtiles2-hyderabad.png │ │ │ ├── qgis-vtiles2.png │ │ │ ├── qgis-vtiles3.png │ │ │ ├── qgis-vtiles4-hyderabad.png │ │ │ ├── qgis-vtiles4.png │ │ │ ├── search-results.png │ │ │ ├── vtiles-attributes.png │ │ │ ├── vtiles-es.png │ │ │ ├── vtiles-estonia.png │ │ │ ├── vtiles-estonia2.png │ │ │ ├── vtiles-hyderabad.png │ │ │ └── vtiles.png │ │ ├── javascripts │ │ │ ├── custom.js │ │ │ └── termynal.js │ │ └── stylesheets │ │ │ ├── custom.css │ │ │ ├── pygeoapi.css │ │ │ └── termynal.css │ ├── conclusion.md │ ├── index.md │ ├── introduction.md │ ├── publishing │ │ ├── first.md │ │ ├── index.md │ │ ├── ogcapi-coverages.md │ │ ├── ogcapi-edr.md │ │ ├── ogcapi-features.md │ │ ├── ogcapi-maps.md │ │ ├── ogcapi-processes.md │ │ ├── ogcapi-records.md │ │ └── ogcapi-tiles.md │ ├── setup.md │ └── standards.md ├── mkdocs.yml └── requirements.txt └── exercises ├── README.md ├── data ├── README.md ├── airport.gpkg ├── airport.sld ├── airports.csv ├── bodenart.en.csv ├── brazil │ └── guama_river.gpkg.zip ├── coads_sst.nc ├── firenze │ ├── cycle-lanes-firenze.geojson │ ├── cycle-lanes-firenze.qmd │ └── metadata │ │ ├── catalogue.tinydb │ │ └── xml │ │ ├── carsharing-florence.xml │ │ └── free-wifi-florence.xml ├── hyderabad │ └── greater_hyderabad_municipal_corporation_ward_Boundaries.geojson └── tartu │ ├── bathingwater-estonia.csv │ ├── bathingwater-estonia.geojson │ ├── cptartu2.gpkg.zip │ ├── estonia_light.tif │ └── metadata │ ├── catalogue.tinydb │ └── xml │ ├── estonia-dams.xml │ ├── estonia-landcover.xml │ └── estonia-transport.xml ├── docker-compose.yml ├── elastic └── README.md ├── features-owslib.ipynb ├── html ├── mapscript-on-leaflet.html └── vector-tiles.html ├── plugins └── process │ └── squared.py └── pygeoapi.config.yml /.github/workflows/deploy.docs.yml: -------------------------------------------------------------------------------- 1 | name: Deploy documentation ⚙️ 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'workshop/content/**' 9 | 10 | defaults: 11 | run: 12 | working-directory: workshop/content 13 | 14 | jobs: 15 | build: 16 | name: Build and Deploy Documentation 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: actions/setup-python@v2 21 | with: 22 | python-version: '3.x' 23 | - name: Install requirements 📦 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install -r requirements.txt 27 | - name: Deploy 📦 28 | run: mkdocs gh-deploy --strict --force --message 'update website via GitHub Actions' 29 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Test documentation ⚙️ 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - 'workshop/content/**' 7 | 8 | defaults: 9 | run: 10 | working-directory: workshop/content 11 | 12 | jobs: 13 | build: 14 | name: Test documentation build 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install requirements 📦 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install -r requirements.txt 25 | - name: Deploy 📦 26 | run: mkdocs build --strict 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | workshop/exercises/data/tiles 3 | workshop/content/site 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | https://github.com/geopython/diving-into-pygeoapi/discussions. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to pygeoapi 2 | 3 | We welcome contributions to the Diving into pygeoapi workshop, in the form of issues, bug fixes, documentation or 4 | suggestions for enhancements. This document sets out our guidelines and best 5 | practices for such contributions. 6 | 7 | It's based on the [Contributing to Open Source Projects 8 | Guide](https://contribution-guide-org.readthedocs.io/). 9 | 10 | The Diving into pygeoapi workshop has the following modes of contribution: 11 | 12 | - GitHub Commit Access 13 | - GitHub Pull Requests 14 | 15 | ## Code of Conduct 16 | 17 | Contributors to this project are expected to act respectfully toward others in accordance with the [OSGeo Code of Conduct](https://www.osgeo.org/code_of_conduct). 18 | 19 | ## Submitting Bugs 20 | 21 | ### Due Diligence 22 | 23 | Before submitting a bug, please do the following: 24 | 25 | * Perform __basic troubleshooting__ steps: 26 | 27 | * __Make sure you're on the latest version.__ If you're not on the most 28 | recent version, your problem may have been solved already! Upgrading is 29 | always the best first step. 30 | * [__Search the issue 31 | tracker__](https://github.com/geopython/diving-into-pygeoapi/issues) 32 | to make sure it's not a known issue. 33 | 34 | ### What to put in your bug report 35 | 36 | Make sure your report gets the attention it deserves: bug reports with missing 37 | information may be ignored or punted back to you, delaying a fix. The below 38 | constitutes a bare minimum; more info is almost always better: 39 | 40 | * __What version of Python are you using?__ For example, are you using Python 41 | 2.7, Python 3.7, PyPy 2.0? 42 | * __What operating system are you using?__ Windows (7, 8, 10, 32-bit, 64-bit), 43 | Mac OS X, (10.7.4, 10.9.0), GNU/Linux (which distribution, which version?) 44 | Again, more detail is better. 45 | * __Which version or versions of the software are you using?__ Ideally, you've 46 | followed the advice above and are on the latest version, but please confirm 47 | this. 48 | * __How can the we recreate your problem?__ Imagine that we have never used 49 | the Diving into pygeoapi workshop before and have downloaded it for the first time. Exactly what steps 50 | do we need to take to reproduce your problem? 51 | 52 | ## Contributions and Licensing 53 | 54 | ### Contributor License Agreement 55 | 56 | Your contribution will be under our [license](https://github.com/geopython/diving-into-pygeoapi/blob/main/LICENSE.md) as per [GitHub's terms of service](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license). 57 | 58 | ### GitHub Commit Access 59 | 60 | * Proposals to provide developers with GitHub commit access shall be emailed to the Diving into pygeoapi [GitHub Discussions](https://github.com/geopython/diving-into-pygeoapi/discussions). Proposals shall be approved by the Diving into pygeoapi workshop team. Committers shall be added by the project admin. 61 | * Removal of commit access shall be handled in the same manner. 62 | 63 | ### GitHub Pull Requests 64 | 65 | * Pull requests may include copyright in the source code header by the contributor if the contribution is significant or the contributor wants to claim copyright on their contribution. 66 | * All contributors shall be listed at https://github.com/geopython/diving-into-pygeoapi/graphs/contributors 67 | * Unclaimed copyright, by default, is assigned to the main copyright holders as specified in https://github.com/geopython/diving-into-pygeoapi/blob/main/LICENSE.md 68 | 69 | ### Version Control Branching 70 | 71 | * Always __make a new branch__ for your work, no matter how small. This makes 72 | it easy for others to take just that one set of changes from your repository, 73 | in case you have multiple unrelated changes floating around. 74 | 75 | * __Don't submit unrelated changes in the same branch/pull request!__ If it 76 | is not possible to review your changes quickly and easily, we may reject 77 | your request. 78 | 79 | * __Base your new branch off of the appropriate branch__ on the main repository: 80 | 81 | * In general the released version of the Diving into pygeoapi workshop is based on the ``master`` 82 | (default) branch whereas development work is done under other non-default 83 | branches. Unless you are sure that your issue affects a non-default 84 | branch, __base your branch off the ``master`` one__. 85 | 86 | * Note that depending on how long it takes for the dev team to merge your 87 | patch, the copy of ``master`` you worked off of may get out of date! 88 | * If you find yourself 'bumping' a pull request that's been sidelined for a 89 | while, __make sure you rebase or merge to latest ``master``__ to ensure a 90 | speedier resolution. 91 | 92 | ### Documentation 93 | 94 | * documentation is managed in `workshop/content/docs/` using , in Markdown format 95 | * [MkDocs](https://www.mkdocs.org) is used to generate the documentation 96 | 97 | 98 | ## Suggesting Enhancements 99 | 100 | We welcome suggestions for enhancements, but reserve the right to reject them 101 | if they do not follow future plans for the Diving into pygeoapi workshop. 102 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright © 2018-2023 The pygeoapi community 4 | 5 | * * * 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Diving into pygeoapi 2 | 3 | [![Gitter](https://img.shields.io/gitter/room/geopython/diving-into-pygeoapi)](https://matrix.to/#/#geopython_diving-into-pygeoapi:gitter.im) 4 | 5 | Welcome to the Diving into pygeoapi workshop! 6 | 7 | [pygeoapi](https://pygeoapi.io) is a Python server implementation of the [OGC API](https://ogcapi.ogc.org) suite of standards. The project emerged as part of the next generation OGC API efforts in 2018 and provides the capability for organizations to deploy a RESTful OGC API endpoint using OpenAPI, GeoJSON, and HTML. pygeoapi is open source and released under an MIT license. 8 | 9 | ## For users 10 | 11 | Are you a workshop participant or want to dive-in individually? Go to [dive.pygeoapi.io](https://dive.pygeoapi.io/) to follow the lessons and exercises. 12 | 13 | ## For authors 14 | 15 | Below are guidelines for authoring and/or improving the workshop's content. 16 | 17 | ### Setting up the pygeoapi environment 18 | 19 | This workshop uses Docker (Docker, Docker Compose) to ensure a consistent environment 20 | to deploy pygeoapi and work through the various exercises. As with participants, follow 21 | the [Workshop environment setup](https://dive.pygeoapi.io/setup). 22 | 23 | ### Building the workshop content locally 24 | 25 | The workshop manual is powered by [MkDocs](https://www.mkdocs.org) which facilitates easy management 26 | of content and publishing. Workshop content is written in Markdown. 27 | 28 | 29 | ### Setting up the manual environment locally 30 | 31 | ```bash 32 | # build a virtual Python environment in isolation 33 | python3 -m venv . 34 | . bin/activate 35 | # fork or clone from GitHub 36 | git clone https://github.com/geopython/diving-into-pygeoapi.git 37 | cd diving-into-pygeoapi/workshop/content 38 | # install required dependencies 39 | pip install -r requirements.txt 40 | # build the website 41 | mkdocs build 42 | # serve locally 43 | mkdocs serve # website is made available on http://localhost:8000 44 | ``` 45 | 46 | ## Contributing updates 47 | 48 | To make contributions back to the workshop, fork the repository from GitHub. Contributions and Pull Requests are always welcome! 49 | 50 | Changes to the GitHub repository result in an automated build and deploy of the content to [dive.pygeoapi.io](https://dive.pygeoapi.io). 51 | 52 | ## Deploying to live site 53 | 54 | Website updates are automatically published via GitHub Actions. To publish manually: 55 | 56 | ```bash 57 | # NOTE: you require access privileges to the GitHub repository 58 | # to publish live updates 59 | mkdocs gh-deploy -m 'add new page on topic x' 60 | ``` 61 | -------------------------------------------------------------------------------- /workshop/content/docs/CNAME: -------------------------------------------------------------------------------- 1 | dive.pygeoapi.io 2 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/administration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Administration 3 | --- 4 | 5 | # Administration 6 | 7 | ## Overview 8 | 9 | pygeoapi provides an administration API (see the pygeoapi [documentation](https://docs.pygeoapi.io/en/latest/admin-api.html) for more information on how to enable, configure and use) in support of managing its configuration. The API (not an OGC API) is implementated as a RESTful service to help create, update, replace or delete various elements of pygeoapi configuration. A simple read-only UI is implemented as part of the admin API. 10 | 11 | ## User interface 12 | 13 | By design, pygeoapi does not provide a true user interface to administer the configuration. Given that the admin API exists, a few options can be considered for developing an admin UI: 14 | 15 | - standalone 16 | - simple application with no connectivity to the pygeoapi admin API 17 | - built off the pygeoapi configuration [schema](https://github.com/geopython/pygeoapi/blob/master/pygeoapi/schemas/config/pygeoapi-config-0.x.yml) 18 | - allows for paste of existing pygeoapi configuration 19 | - allows for generating pygeoapi configuration for copy/paste into a pygeoapi deployment 20 | - can be deployed anywhere (for example, GitHub Pages) 21 | - integrated 22 | - connected application to a pygeoapi deployment 23 | - built off the pygeoapi configuration [schema](https://github.com/geopython/pygeoapi/blob/master/pygeoapi/schemas/config/pygeoapi-config-0.x.yml) 24 | - reads/writes a live pygeoapi configuration via the pygeoapi admin API (access controlled) 25 | - deployed as part of a Docker Compose application 26 | 27 | !!! note 28 | 29 | Have your own idea for a pygeoapi admin UI? Connect with the [pygeoapi community](https://pygeoapi.io/community) to discuss your idea! 30 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/bridges.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exercise 9 - pygeoapi as a bridge to other services 3 | --- 4 | 5 | # Exercise 9 - pygeoapi as a bridge to other services 6 | 7 | In this section we explore how pygeoapi can be used as a facade, or a bridge, to re-publish web services with different interfaces. These bridges can help [organisations migrating from OWS to OGC API](https://ogcapi-workshop.ogc.org/transition-and-migration). 8 | 9 | ## Publishing WFS as OGC API - Features 10 | 11 | A powerful use case for pygeoapi is to provide an OGC API - Features interface over existing Web Feature Service (WFS) 12 | or ESRI FeatureServer endpoints. In this scenario, you lower the barrier and increase the usability of existing services to 13 | a wider audience. Let's set up an API on top of an existing WFS hosted by the city of Florence. 14 | 15 | !!! question "Update the pygeoapi configuration" 16 | 17 | Open the pygeoapi configuration in a text editor. 18 | Find the line `# START - EXERCISE 8 - WFS Proxy`. 19 | 20 | Add a new dataset section by uncommenting the lines up to `# END - EXERCISE 8 - WFS Proxy`: 21 | 22 | 23 | ``` {.yaml linenums="1"} 24 | suol_epicentri_storici: 25 | type: collection 26 | title: Epicenters of the main historical earthquakes 27 | description: Location of the epicenters of the main historical earthquakes in the territory of the Metropolitan City of Florence classified by year and intensity 28 | keywords: 29 | - earthquakes 30 | links: 31 | - type: text/xml 32 | rel: canonical 33 | title: Epicenters of the main historical earthquakes 34 | href: http://pubblicazioni.cittametropolitana.fi.it/geoserver/territorio/wfs?request=getCapabilities&service=WFS&version=2.0.0 35 | hreflang: it 36 | extents: 37 | spatial: 38 | bbox: [10.94, 43.52, 11.65, 44.17] 39 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 40 | providers: 41 | - type: feature 42 | name: OGR 43 | data: 44 | source_type: WFS 45 | source: WFS:http://pubblicazioni.cittametropolitana.fi.it/geoserver/territorio/wfs? 46 | source_capabilities: 47 | paging: True 48 | source_options: 49 | OGR_WFS_LOAD_MULTIPLE_LAYER_DEFN: NO 50 | gdal_ogr_options: 51 | EMPTY_AS_NULL: NO 52 | GDAL_CACHEMAX: 64 53 | CPL_DEBUG: NO 54 | id_field: cpti_id 55 | crs: 56 | - http://www.opengis.net/def/crs/OGC/1.3/CRS84 57 | - http://www.opengis.net/def/crs/EPSG/0/4258 58 | - http://www.opengis.net/def/crs/EPSG/0/3857 59 | - http://www.opengis.net/def/crs/EPSG/0/3003 60 | storage_crs: http://www.opengis.net/def/crs/EPSG/0/3003 61 | title_field: d 62 | layer: territorio:suol_epicentri_storici 63 | ``` 64 | 65 | Save the file and restart Docker Compose. Navigate to 66 | to evaluate whether the new dataset has been published. 67 | 68 | Note these important configuration slices under `providers`: 69 | 70 | * We use the pygeoapi [OGR Provider](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-features.html#ogr). 71 | This is the most versatile backend of pygeoapi for supporting numerous formats. Using the GDAL/OGR library (Python bindings) allows pygeoapi to connect to [around 80+ Vector Formats](https://gdal.org/drivers/vector). 72 | We could have used the `OGR` Provider instead of the `SQLiteGPKG` Provider above in the `osm_places-vec` exercise above. 73 | 74 | * `storage_crs` denotes the CRS (Coordinate Reference System) in which the dataset is stored (default is CRS84, i.e. 'longitude, latitude') 75 | * `crs` is an array of CRSs that can be specified for the Features to be returned (`crs=` parameter), or for their bounding box (`bbox-crs=` parameter). Default is also CRS84. 76 | 77 | CRS support effectively allows pygeoapi to *reproject* the data from its storage CRS (here EPSG:3003) 78 | according to [OGC API - Features - Part 2: Coordinate Reference Systems by Reference](https://docs.opengeospatial.org/is/18-058r1/18-058r1.html). 79 | The Advanced section of this workshop will further [elaborate pygeoapi CRS support](../advanced/crs.md). 80 | 81 | 82 | ## Publishing WMS as OGC API - Maps 83 | 84 | We can use the pygeoapi's WMSFacade provider to publish OGC Web Map Service (WMS) interfaces as OGC API - Maps. 85 | 86 | Let's set up an API on top of an existing WMS on the MapServer Demonstration Server: 87 | 88 | 89 | 90 | 91 | !!! note 92 | 93 | Feel free to use an WMS of your choice, as you wish! 94 | 95 | !!! question "Update the pygeoapi configuration" 96 | 97 | Open the pygeoapi configuration in a text editor. 98 | Find the line `## START - EXERCISE 8 - WMS Proxy`. 99 | 100 | Add a new dataset section by uncommenting the lines up to `## END - EXERCISE 8 - WMS Proxy`: 101 | 102 | Be sure to keep the proper YAML indentation. 103 | 104 | ``` {.yaml linenums="1"} 105 | wms-facade-demo: 106 | type: collection 107 | title: WMS Facade demo 108 | description: WMS Facade demo 109 | keywords: 110 | - WMS facade 111 | links: 112 | - type: text/html 113 | rel: canonical 114 | title: MapServer 115 | href: https://mapserver.org 116 | hreflang: en 117 | extents: 118 | spatial: 119 | bbox: [-180,-90,180,90] 120 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 121 | providers: 122 | - type: map 123 | name: WMSFacade 124 | data: https://demo.mapserver.org/cgi-bin/msautotest 125 | options: 126 | layer: world_latlong 127 | style: default 128 | format: 129 | name: png 130 | mimetype: image/png 131 | ``` 132 | 133 | Run the following requests in your web browser: 134 | 135 | - default map: 136 | - specific width/height: 137 | - specific area of interest (bbox of Canada): 138 | 139 | ![](../assets/images/maps-response.png){ width=80% } 140 | 141 | !!! tip 142 | 143 | Try with your own bbox and width/height values! 144 | 145 | ## Publishing CSW as OGC API - Records 146 | 147 | In this section we'll have a look at how to publish Catalogue Service for the Web (CSW) as OGC API - Records. For that, we will use the [pycsw OGC CITE demo](https://demo.pycsw.org/cite/) CSW service. 148 | 149 | !!! question "Update the pygeoapi configuration" 150 | 151 | Open the pygeoapi configuration in a text editor. 152 | Find the line `# START - EXERCISE 8 - CSW Proxy`. 153 | 154 | Add a new dataset section by uncommenting the lines up to `# END - EXERCISE 8 - CSW Proxy`: 155 | 156 | ``` {.yaml linenums="1"} 157 | cite_demo: 158 | type: collection 159 | title: pycsw OGC CITE demo and Reference Implementation 160 | description: pycsw is an OARec and OGC CSW server implementation written in Python. pycsw fully implements the OGC API - Records and OpenGIS Catalogue Service Implementation Specification (Catalogue Service for the Web). Initial development started in 2010 (more formally announced in 2011). The project is certified OGC Compliant, and is an OGC Reference Implementation. Since 2015, pycsw is an official OSGeo Project. pycsw allows for the publishing and discovery of geospatial metadata via numerous APIs (CSW 2/CSW 3, OpenSearch, OAI-PMH, SRU). Existing repositories of geospatial metadata can also be exposed, providing a standards-based metadata and catalogue component of spatial data infrastructures. pycsw is Open Source, released under an MIT license, and runs on all major platforms (Windows, Linux, Mac OS X) 161 | keywords: 162 | - ogc 163 | - cite 164 | - compliance 165 | - interoperability 166 | extents: 167 | spatial: 168 | bbox: [-180,-90,180,90] 169 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 170 | providers: 171 | - type: record 172 | name: CSWFacade 173 | data: https://demo.pycsw.org/cite/csw 174 | id_field: identifier 175 | time_field: datetime 176 | title_field: title 177 | ``` 178 | 179 | You can explore the proxied catalogue collection using this endpoints: 180 | 181 | * collection metadata page: 182 | * list of records: 183 | * record: 184 | 185 | !!! tip 186 | 187 | Remember that you can use the QGIS client suggested [here](https://dive.pygeoapi.io/publishing/ogcapi-records/#client-access) to explore this API. 188 | 189 | ## Publishing SensorThings API as OGC API - Features 190 | 191 | The [OGC SensorThings API standard](https://ogcapi-workshop.ogc.org/api-deep-dive/sensorthings/) offers RESTfull interfaces to interconnect IoT devices, data, in an open and unified way. Although there are some clients that support this standard, there are many more that support OGC API - Features. 192 | 193 | The pygeoapi SensorThings bridge enables to proxy the SensorThings entities (e.g.: `Thing` , `Sensor`, `DataStream`, `ObservedProperty` ) into feature collections. 194 | 195 | In this section we'll have a look at how to Publish a SensorThings API `Thing` as an OGC API - Features collection, which can then be consumed by various clients, like [the ones listed here](../publishing/ogcapi-features.md#client-access) 196 | 197 | !!! question "Update the pygeoapi configuration" 198 | 199 | Open the pygeoapi configuration in a text editor. 200 | Find the line `# START - EXERCISE 8 - SensorThings Proxy`. 201 | 202 | Add a new dataset section by uncommenting the lines up to `# END - EXERCISE 8 - SensorThings Proxy`: 203 | 204 | ``` {.yaml linenums="1"} 205 | toronto_bikes: 206 | type: collection 207 | title: Toronto Bikes SensorThings 208 | description: The geographic location with coordinates for the Toronto bike share station 209 | keywords: 210 | - sediments 211 | extents: 212 | spatial: 213 | bbox: [-180,-90,180,90] 214 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 215 | providers: 216 | - type: feature 217 | name: SensorThings 218 | data: https://toronto-bike-snapshot.sensorup.com/v1.0/ 219 | entity: Things 220 | ``` 221 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/cloud.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cloud deployment 3 | --- 4 | 5 | # Cloud deployment 6 | 7 | Deployment to cloud infratructure and concepts such as Microservices and [Twelve-Factor](https://12factor.net) present specific requirements to 8 | how software is designed and implemented. pygeoapi supports these concepts, having a low footprint on CPU and memory, and does not persist user 9 | state, therefore being able to scale without risks. 10 | 11 | ## pygeoapi and Docker 12 | 13 | A [Docker image](https://hub.docker.com/r/geopython/pygeoapi) is available for pygeoapi. You can run the image locally as: 14 | 15 | === "Linux/Mac" 16 | 17 |
18 | ```bash 19 | docker run -p 5000:80 geopython/pygeoapi:latest 20 | ``` 21 |
22 | 23 | === "Windows (PowerShell)" 24 | 25 |
26 | ```bash 27 | docker run -p 5000:80 geopython/pygeoapi:latest 28 | ``` 29 |
30 | 31 | !!! question "Review the pygeoapi Dockerfile" 32 | 33 | Notice in the [pygeoapi Dockerfile](https://github.com/geopython/pygeoapi/Dockerfile) how the open api file is generated as part of the Docker startup script. 34 | 35 | In a typical configuration one would override the default pygeoapi configuration file in the image with a customized one and include the data folder: 36 | 37 | !!! example "using custom configuration" 38 | 39 | === "Linux/Mac" 40 | 41 |
42 | ```bash 43 | docker run -p 5000:80 \ 44 | -v $(pwd)/pygeoapi-config.yml:/pygeoapi/local.config.yml \ 45 | -v $(pwd)/geodata:/geodata https://hub.docker.com/r/geopython/pygeoapi:latest 46 | ``` 47 |
48 | 49 | === "Windows (PowerShell)" 50 | 51 |
52 | ```bash 53 | docker run -p 5000:80 -v ${pwd}/pygeoapi-config.yml:/pygeoapi/local.config.yml -v ${pwd}/geodata:/geodata https://hub.docker.com/r/geopython/pygeoapi:latest 54 | ``` 55 |
56 | 57 | 58 | Alternatively, you can build a fresh Docker image including both the configuration and data for the service. 59 | 60 | ``` 61 | FROM geopython/pygeoapi:latest 62 | COPY ./my.config.yml /pygeoapi/local.config.yml 63 | ``` 64 | 65 | You may have noticed that the pygeoapi configuration file includes a reference to the endpoint on which pygeoapi is published. This configuration should 66 | match the public endpoint of the service (domain, path and port). 67 | 68 | By default the pygeoapi Docker image will run from the `root` path `/`. If you need to run from a sub-path and have all internal URLs correct you can 69 | set the `SCRIPT_NAME` environment variable. 70 | 71 | === "Linux/Mac" 72 | 73 |
74 | ```bash 75 | docker run -p 5000:80 -e SCRIPT_NAME='/mypygeoapi' \ 76 | -v $(pwd)/my.config.yml:/pygeoapi/local.config.yml -it geopython/pygeoapi 77 | # browse to http://localhost:5000/mypygeoapi 78 | ``` 79 |
80 | 81 | === "Windows (PowerShell)" 82 | 83 |
84 | ```bash 85 | docker run -p 5000:80 -e SCRIPT_NAME='/mypygeoapi' -v ${pwd}/my.config.yml:/pygeoapi/local.config.yml -it geopython/pygeoapi 86 | # browse to http://localhost:5000/mypygeoapi 87 | ``` 88 |
89 | 90 | # Summary 91 | 92 | Congratulations! You can now deploy pygeopi as a cloud native service. 93 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/crs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Coordinate Reference Systems (CRS) Support 3 | --- 4 | 5 | # CRS support 6 | 7 | Starting with version 0.15.0, pygeoapi fully supports [OGC API - Features - Part 2: Coordinate Reference Systems by Reference](https://docs.opengeospatial.org/is/18-058r1/18-058r1.html). 8 | This enables the import and export of any data according to dedicated projections. 9 | A "projection" is specified with a Coordinate Reference System (CRS) identifier. These are in URI formats 10 | like `http://www.opengis.net/def/crs/OGC/1.3/CRS84` (basically WGS84 in longitude, latitude axis order) 11 | or the "OpenGIS" format like `http://www.opengis.net/def/crs/EPSG/0/4258`. Note that the "EPSG:" format like `EPSG:4326` 12 | is outside the scope of the OGC standard. 13 | 14 | In particular CRS support allows for the following: 15 | 16 | - to specify the CRS in which the data is stored, in pygeoapi the `storageCRS` config option 17 | - to specify the list of CRSs in which Feature data can be retrieved, in pygeoapi the `crs` config option 18 | - to publish these CRSs in the collection metadata 19 | - the `crs=` query parameter for a collection or collection item 20 | - the `bbox-crs=` query parameter to indicate that the `bbox=` parameter is encoded in that CRS 21 | - the HTTP response header `Content-Crs` denotes the CRS of the Feature(s) in the data returned 22 | 23 | So although GeoJSON mandates WGS84 in longitude, latitude order, the client and server may still agree 24 | on other CRSs. 25 | 26 | Under the hood, pygeoapi uses the well-known [pyproj](https://pyproj4.github.io/pyproj/stable) Python wrapper to the [PROJ](https://proj.org) library. 27 | 28 | Read more in the pygeoapi documentation in the [CRS Chapter](https://docs.pygeoapi.io/en/latest/crs.html). 29 | 30 | # Exercise 31 | 32 | Adding CRS support to pygeoapi collections for the `provider` type `feature` is as simple as 33 | for example extending the [Exercise 2](../publishing/ogcapi-features.md) config with this snippet: 34 | 35 | ```yaml 36 | crs: 37 | - http://www.opengis.net/def/crs/OGC/1.3/CRS84 38 | - http://www.opengis.net/def/crs/EPSG/0/4258 39 | - http://www.opengis.net/def/crs/EPSG/0/3857 40 | - http://www.opengis.net/def/crs/EPSG/0/4326 41 | storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 42 | ``` 43 | 44 | 45 | !!! Axis order 46 | 47 | Axis order (are coordinates it longitude, latitude or latitude, longitude order?) in projections is often a source of confusion. 48 | However the URI format is quite clear on this, at least more than the `EPSG:` format. 49 | So http://www.opengis.net/def/crs/OGC/1.3/CRS84 is longitude, latitude order, while 50 | http://www.opengis.net/def/crs/EPSG/0/4326 is latitude, longitude order. 51 | 52 | 53 | In the config below, we basically indicate that the data is stored in WGS84 (longitude, latitude axis order) and can be retrieved 54 | in CRSs like `http://www.opengis.net/def/crs/EPSG/0/4258` (ETRS89 latitude, longitude axis order) etc. 55 | 56 | !!! question "Add CRS to a pygeoapi configuration" 57 | 58 | Open the pygeoapi configuration file in a text editor. 59 | Find the line `# START - EXERCISE 2 - firenze-terrain` 60 | 61 | Update the dataset section with CRS support by replacing it with the snippet below: 62 | 63 | ``` {.yaml linenums="1"} 64 | firenze-terrains-vec: 65 | type: collection 66 | title: Administrative boundaries before 2014 67 | description: Cadastral parcels (terrains) from the cadastre. Territory Agency; SIT and Information Networks; 68 | keywords: 69 | - Cadastral parcels 70 | links: 71 | - type: text/html 72 | rel: canonical 73 | title: Administrative boundaries before 2014 74 | href: http://dati.cittametropolitana.fi.it/geonetwork/srv/metadata/cmfi:c539d359-4387-4f83-a6f4-cd546b3d8443 75 | hreflang: it 76 | extents: 77 | spatial: 78 | bbox: [11.23,43.75,11.28,43.78] 79 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 80 | providers: 81 | - type: feature 82 | name: SQLiteGPKG 83 | data: /data/firenze_terrains.gpkg # place correct path here 84 | id_field: fid 85 | crs: 86 | - http://www.opengis.net/def/crs/OGC/1.3/CRS84 87 | - http://www.opengis.net/def/crs/EPSG/0/4258 88 | - http://www.opengis.net/def/crs/EPSG/0/3857 89 | - http://www.opengis.net/def/crs/EPSG/0/4326 90 | storage_crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 91 | title_field: codbo 92 | table: firenze_terrains 93 | ``` 94 | 95 | Now we can inspect the collection metadata and retrieve Features in various CRSs. 96 | We can even do this in the Swagger UI, but using the browser is quite fast and clear. 97 | 98 | ## Metadata 99 | 100 | !!! question "Collection Metadata" 101 | 102 | Open the URL: 103 | 104 | Your configured CRSs are displayed at the bottom of the page: "Reference Systems" and "Storage CRS". 105 | 106 | See these in JSON format, also at the bottom: 107 | 108 | ```yaml 109 | . 110 | . 111 | "crs":[ 112 | "http://www.opengis.net/def/crs/OGC/1.3/CRS84", 113 | "http://www.opengis.net/def/crs/EPSG/0/4258", 114 | "http://www.opengis.net/def/crs/EPSG/0/3857", 115 | "http://www.opengis.net/def/crs/EPSG/0/4326" 116 | ], 117 | "storageCRS":"http://www.opengis.net/def/crs/OGC/1.3/CRS84" 118 | } 119 | ``` 120 | 121 | ## Reproject Features 122 | 123 | !!! question "Using the CRS query parameter" 124 | 125 | Open the URL: 126 | 127 | 128 | This is ETRS89, similar to WGS84, but for the European Continent (Datum) and in lat,lon order. This is e.g. used in INSPIRE. 129 | 130 | See these in JSON format, also at the bottom: 131 | 132 | ```json 133 | "type":"FeatureCollection", 134 | "features":[ 135 | { 136 | "type":"Feature", 137 | "geometry":{ 138 | "type":"MultiPolygon", 139 | "coordinates":[ 140 | [ 141 | [ 142 | [ 143 | 43.77805936835436, 144 | 11.23486287997071 145 | ], 146 | [ 147 | 43.77809089595012, 148 | 11.2348943159564 149 | ], 150 | [ 151 | 43.77810038978989, 152 | 11.23491359066035 153 | ], 154 | [ 155 | 43.77705757917591, 156 | 11.2368990806804 157 | . 158 | . 159 | "crs":[ 160 | "http://www.opengis.net/def/crs/OGC/1.3/CRS84", 161 | "http://www.opengis.net/def/crs/EPSG/0/4258", 162 | "http://www.opengis.net/def/crs/EPSG/0/3857", 163 | "http://www.opengis.net/def/crs/EPSG/0/4326" 164 | ], 165 | "storageCRS":"http://www.opengis.net/def/crs/OGC/1.3/CRS84" 166 | } 167 | ``` 168 | 169 | If you open the browser development console, you can observe the HTTP response header: 170 | 171 | `Content-Crs: ` 172 | 173 | (The CRS URI is always enclosed in `<` `>`) 174 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/downstream-applications.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Using pygeoapi in downstream applications 3 | --- 4 | 5 | # Using pygeoapi in downstream applications 6 | 7 | While pygeoapi is typically run as a standalone application, it is also designed 8 | to enable direct usage via external Python applications in a number of different 9 | design patterns.at multiple levels. From the [official documentation](https://docs.pygeoapi.io/en/latest/how-pygeoapi-works.html), the below 10 | diagram provides an overview of how pygeoapi is designed and architected: 11 | 12 | [![how pygeoapi works](https://docs.pygeoapi.io/en/latest/_images/how-pygeoapi-works.png)](https://docs.pygeoapi.io/en/latest/how-pygeoapi-works.html) 13 | 14 | There are two main ways to create a downstream application: 15 | 16 | - Using the core API 17 | - Extending through the web interface of the frameworks supported out-of-the box 18 | 19 | ## Using the core API directly 20 | 21 | The core pygeoapi Python API entrypoint is `pygeoapi.api.API`, which is initialized with the pygeoapi configuration 22 | as a Python `dict`. 23 | 24 | !!! note 25 | 26 | The pygeoapi core API enables the developer to manage pygeoapi configuration in any number of ways 27 | (file on disk, object storage, database driven, etc.) 28 | 29 | From here, API objects provide a number of functions, most of which require a [`pygeoapi.api.APIRequest`](https://docs.pygeoapi.io/en/latest/api-documentation.html#pygeoapi.api.APIRequest) object 30 | according to the web framework. Examples include: 31 | 32 | - [Flask](https://flask.palletsprojects.com/en/latest/api/#incoming-request-data) 33 | - [Starlette](https://www.starlette.io/requests) 34 | - [FastAPI](https://fastapi.tiangolo.com/advanced/using-request-directly) 35 | - [Django](https://docs.djangoproject.com/en/4.1/ref/request-response/#django.http.HttpRequest) 36 | 37 | !!! note 38 | 39 | See the [official documentation](https://docs.pygeoapi.io/en/latest/api-documentation.html#pygeoapi.api.APIRequest) 40 | for more information about `pygeoapi.api.APIRequest` (you can even use your own custom request object as long as it 41 | satisfies the interface requirements of `pygeoapi.api.APIRequest`. 42 | 43 | Let's take a look at what a bare bones API integration would look like, using Flask as an example: 44 | 45 | ```python 46 | 47 | from flask import Flask, make_response, request 48 | 49 | from pygeoapi.api import API 50 | from pygeoapi.util import yaml_load 51 | 52 | my_flask_app = Flask(__name__) 53 | 54 | with open('my-pygeoapi-config.yml') as fh: 55 | my_pygeoapi_config = yaml_load(fh) 56 | 57 | my_pygeoapi_api = API(my_pygeoapi_config) 58 | 59 | @my_flask_app.route('/my-landing-page-route') 60 | def my_def(): 61 | 62 | headers, status, content = my_pygeoapi_api.landing_page(request) 63 | 64 | response = make_response(content, status) 65 | 66 | if headers: 67 | response.headers = headers 68 | 69 | return response 70 | ``` 71 | 72 | !!! note 73 | 74 | See the [official documentation](https://docs.pygeoapi.io/en/latest/api-documentation.html#module-pygeoapi.api) 75 | for more information on the core Python API 76 | 77 | ## Extending through a web framework 78 | 79 | pygeoapi can be installed and used at the web routing level as a dependency in your project. This is pretty much the easier way to leverage the flexibility and the modularity of its architecture. 80 | Once the interfaces are available then the developer can use the preferred framework for serving the frontend application. In practice 81 | the following modules: 82 | 83 | - `pygeoapi.flask_app.py` for Flask blueprints 84 | - `pygeoapi.starlette_app.py` for Starlette/FastAPI 85 | - `pygeoapi.django_app.py` for Django (ongoing [PR](https://github.com/geopython/pygeoapi/pull/630)) 86 | 87 | Some examples are available below for developers. 88 | 89 | ### Examples 90 | 91 | #### Flask blueprints 92 | 93 | ```python 94 | from flask import Flask 95 | 96 | from pygeoapi.flask_app import BLUEPRINT as pygeoapi_blueprint 97 | 98 | my_flask_app = Flask(__name__, static_url_path='/static') 99 | my_flask_app.url_map.strict_slashes = False 100 | 101 | # mount all pygeoapi endpoints to /oapi 102 | my_flask_app.register_blueprint(pygeoapi_blueprint, url_prefix='/oapi') 103 | 104 | 105 | @my_flask_app.route('/') 106 | def home(): 107 | return '

home page

' 108 | ``` 109 | 110 | #### Starlette and FastAPI 111 | 112 | ```python 113 | 114 | import uvicorn 115 | from fastapi import FastAPI 116 | from fastapi.exceptions import RequestValidationError 117 | from starlette.exceptions import HTTPException as StarletteHTTPException 118 | from starlette.middleware.cors import CORSMiddleware 119 | 120 | from pygeoapi.starlette_app import app as pygeoapi_app 121 | 122 | 123 | def create_app() -> FastAPI: 124 | """Handle application creation.""" 125 | app = FastAPI(title="my_pygeoapi", root_path="", debug=True) 126 | 127 | # Set all CORS enabled origins 128 | app.add_middleware( 129 | CORSMiddleware, 130 | allow_origins=["*"], 131 | allow_credentials=True, 132 | allow_methods=["*"], 133 | allow_headers=["*"], 134 | ) 135 | 136 | @app.exception_handler(StarletteHTTPException) 137 | async def custom_http_exception_handler(request, e): 138 | return await http_exception_handler(request, e) 139 | 140 | @app.exception_handler(RequestValidationError) 141 | async def custom_validation_exception_handler(request, e): 142 | return await request_validation_exception_handler(request, e) 143 | 144 | # mount all pygeoapi endpoints to /oapi 145 | app.mount(path="/oapi", app=pygeoapi_app) 146 | 147 | return app 148 | 149 | app = create_app() 150 | 151 | if __name__ == "__main__": 152 | uvicorn.run(app, port=5000) 153 | ``` 154 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/i18n.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multilingual support 3 | --- 4 | 5 | # Multilingual support 6 | 7 | pygeoapi supports multilinguality at three levels: 8 | 9 | - In the pygeoapi configuration you can provide titles and abstracts of the service and collections in multiple languages. 10 | - A set of translatable text-strings which are translated and introduced as part of the JSON and HTML output formats. Translations are managed by the [Babel framework](https://babel.pocoo.org) 11 | - Column names/values in feature based datasets. If a dataset contains columns in multiple languages, pygeoapi will try to return data responses in the requested language 12 | 13 | !!! note 14 | 15 | Error messages are not translated, to facilitate copy-paste of the error into 16 | [stackoverflow](https://stackoverflow.com/search?q=pygeoapi) and 17 | [GitHub issues](https://github.com/geopython/pygeoapi/issues). 18 | 19 | Language negotiation is triggered by the HTTP `Accept-Language` header as sent by the client, and can always be overridden with the `?lang=fr` url parameter. 20 | 21 | ## Multilingual configuration 22 | 23 | In the pygeoapi configuration you can indicate the languages supported by the instance. The first language is the default language. For most of the textual configuration properties you can provide a translation in alternative languages. 24 | 25 | ``` {.yaml linenums="1"} 26 | lakes: 27 | type: collection 28 | title: 29 | en: Large Lakes 30 | fr: Grands Lacs 31 | description: 32 | en: lakes of the world, public domain 33 | fr: lacs du monde, domaine public 34 | keywords: 35 | en: 36 | - lakes 37 | - water bodies 38 | fr: 39 | - lacs 40 | - plans d'eau 41 | ``` 42 | 43 | ## Text strings within Jinja2 HTML templates 44 | 45 | Most of the translatable text strings exist within the Jinja2 HTML templates. Text strings to be translated are placed in a `trans` tag, as follows: 46 | 47 | ``` {.html linenums="1"} 48 | {% trans %}Page title{% endtrans %} 49 | ``` 50 | 51 | Babel provides a utility which extracts all keys to be translated from the templates into a single `.pot` file. 52 | 53 | === "Linux/Mac" 54 | 55 |
56 | ```bash 57 | pybabel extract -F babel-mapping.ini -o locale/messages.pot ./ 58 | ``` 59 |
60 | 61 | === "Windows (PowerShell)" 62 | 63 |
64 | ```bash 65 | pybabel extract -F babel-mapping.ini -o locale/messages.pot ./ 66 | ``` 67 |
68 | 69 | The resulting `.pot` file is used to create or update existing `.po` files, which exist for each language, containing the actual translations. 70 | 71 | === "Linux/Mac" 72 | 73 |
74 | ```bash 75 | pybabel init -d locale -l it -i locale/messages.pot 76 | ``` 77 |
78 | 79 | === "Windows (PowerShell)" 80 | 81 |
82 | ```bash 83 | pybabel init -d locale -l it -i locale/messages.pot 84 | ``` 85 |
86 | 87 | The `.po` files are stored in pygeoapi's source code repository on GitHub. You can create a Pull Request to add or update your favourite languages. `.po` files can also be added to translation software such as [transifex.com](https://transifex.com). 88 | 89 | !!! question "Edit a `.po` file" 90 | 91 | Open a `.po` file from the [locale](https://github.com/geopython/pygeoapi/tree/master/locale) folder in a text editor. Edit some values. Save the file and restart the service. Verify that the updated content is available. You can also try to add a new key to a template and translate it via the `.po` mechanism. 92 | 93 | ## Annotating the language of data columns 94 | 95 | pygeoapi includes a meachanism to influence the API responses based on the requested language. If your service operates with multilingual requirements, it may make sense to add textual columns in multiple languages. For example, in the pygeoapi configuration you can then indicate which column should be used as the title field, for which language. 96 | 97 | !!! question "Publish a multilingual dataset" 98 | 99 | For this workshop we have prepared a multilingual dataset of `free wifi hotspots in Florence` (`workshop/exercises/data/free-wifi-florence.csv`). Add the dataset to the pygeoapi configuration using the CSV provider. Add a title-field configuration with for each translated column the relevant language. 100 | 101 | ``` {.yaml linenums="1"} 102 | data: /data/free-wifi-florence.csv 103 | id_field: id 104 | title_field: 105 | en: name-en 106 | it: name-it 107 | de: name-de 108 | ``` 109 | 110 | Test the configuration by navigating to the items page of the collection and switching the language by appending `?lang=it`, `?lang=de` to the URL. 111 | 112 | # Summary 113 | 114 | Congratulations! You've customized pygeoapi to support multiple languages. 115 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced topics 3 | --- 4 | 5 | # Advanced topics 6 | 7 | In this section, we will discuss more advanced pygeoapi topics primarily 8 | focused on extending pygeoapi via custom development and deployment. 9 | 10 | - [Multilingual support](i18n.md) 11 | - [CRS support](crs.md) 12 | - [UI customization and templating](ui-custom-templates.md) 13 | - [Using pygeoapi in downstream applications](downstream-applications.md) 14 | - [Search Engine Optimization (SEO)](seo.md) 15 | - [Security and access control](security-access-control.md) 16 | - [Semantic Web and Linked Data](semantic-web-linked-data.md) 17 | - [Cloud deployment](cloud.md) 18 | - [INSPIRE support](inspire.md) 19 | - [pygeoapi as a Bridge to Other Services](bridges.md) 20 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/inspire.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: INSPIRE support 3 | --- 4 | 5 | # INSPIRE support 6 | 7 | [INSPIRE](https://inspire.ec.europa.eu) is a European directive on data sharing in the environmental domain. EU member states 8 | have invested almost 20 years of effort to harmonize data in the environmental domain and publish it using OGC standards. 9 | The directive is at the end of its lifetime, but the expectation is that conventions from the INSPIRE directive will be adopted 10 | by upcoming directives, such as green deal and open data directives. 11 | 12 | In the past 20 years, the IT landscape has changed considerably. INSPIRE has followed these developments by adopting a 13 | series of [Good Practices](https://inspire.ec.europa.eu/portfolio/good-practice-library) which supersede the original 14 | [Technical Guidelines](https://inspire.ec.europa.eu/Technical-guidelines3). 15 | 16 | Some of the recent and upcoming good practices focus on the developments in the OGC API domain. 17 | One good practice has already been adopted on providing 18 | [download services using OGC API - Features](https://github.com/INSPIRE-MIF/gp-ogc-api-features) 19 | and others are in preparation, such as the 20 | [discovery service using OGC API - Records](https://github.com/INSPIRE-MIF/gp-ogc-api-records). 21 | These developments make pygeoapi an interesting option 22 | for providing INSPIRE services. 23 | 24 | 25 | ## INSPIRE services and their OGC API alternative 26 | 27 | INSPIRE services are typically categorized in view services, download services and discovery services. 28 | Download services are further devided in Vector sources, Coverage sources and Sensor sources. 29 | The OGC API initiative provides the related APIs for each service. 30 | The table below highlights for each service type the Technical Guidenace 31 | recommendation and the relevant Good Practices. 32 | 33 | | Service type | TG | OGC API | Good practice status | 34 | | -------------------------------- | ------ | --------------------------------- | -------------------- | 35 | | Discovery service | CSW | OGC API - Records | [In preparation](https://github.com/INSPIRE-MIF/gp-ogc-api-records) | 36 | | View service | WM(T)S | OGC API - Maps / OGC API - Tiles | Not scheduled
[In preparation](https://wikis.ec.europa.eu/display/InspireMIG/69th+MIG-T+meeting+2022-04-01) | 37 | | Download service - Vector | WFS | OGC API - Features | [Adopted](https://github.com/INSPIRE-MIF/gp-ogc-api-features) | 38 | | Download service - Coverage | WCS | OGC API - Coverages / STAC [^1] | Not scheduled
[In preparation](https://github.com/INSPIRE-MIF/gp-stac) | 39 | | Download service - Sensor | SOS | OGC API - EDR / Sensorthings API [^2] | Not scheduled
[Adopted](https://github.com/INSPIRE-MIF/gp-ogc-sensorthings-api) | 40 | 41 | [^1]: Sensorthings API and is not an OGC API standards and is currently not supported by pygeoapi. It is listed here for completeness 42 | [^2]: STAC is not OGC API standard but is supported by pygeoapi 43 | 44 | !!! note 45 | 46 | When adopting Good Practices, consider that the documentation and tools for validation are still limited. 47 | Also the INSPIRE GeoPortal may not be ready to harvest records from an OGC API - Records endpoint yet. 48 | 49 | !!! question "Publish metadata documents as an INSPIRE discovery service" 50 | 51 | In this exercise we will import a folder of metadata documents into a TinyDB database and configure the database as an OGC API - Records endpoint. 52 | Download the zipfile 'inspire-records.zip' from the repository. Expand the zipfile. The `/tests` folder contains a script 53 | [load_tinydb_records.py](https://github.com/geopython/pygeoapi/blob/master/tests/load_tinydb_records.py). The script has 2 parameters: 54 | 55 | === "Linux/Mac" 56 | 57 |
58 | ```bash 59 | python3 load_tinydb_records.py 60 | ``` 61 |
62 | 63 | === "Windows (PowerShell)" 64 | 65 |
66 | ```bash 67 | python3 load_tinydb_records.py 68 | ``` 69 |
70 | 71 | Now configure [TinyDB as a provider for OGC API - Records](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-records.html#tinydbcatalogue). Restart the service and verify the result. Verify also the XML output of some of the records. 72 | 73 | 74 | ## OGC API and the INSPIRE data models 75 | 76 | Most of the INSPIRE data models have a hierarchical structure, which is not common in the GeoJSON oriented OGC API community. 77 | In theory it is possible to provide hierarchical GML from an OGC API endpoint, but there are not many experiences yet currently. 78 | Two initiatives may bring improvement to this aspect: 79 | 80 | - pygeoapi facilitates to configure a JSON-LD encoding using an arbitrary ontology. The 81 | [good practice on semantic web](https://inspire-eu-rdf.github.io/inspire-rdf-guidelines) provides some of the data models 82 | in an RDF ontology 83 | - The [good practice on alternative encodings](https://github.com/INSPIRE-MIF/gp-geopackage-encodings) suggests an 84 | approach to publish datasets using a relational data model such as GeoPackage, which fits better with the OGC API community 85 | 86 | ## OGC API as a codelist registry 87 | 88 | A typical use case in INSPIRE is the option to extend an INSPIRE codelist to match a local requirement. For this use case the 89 | extended codelist has to be published in a registry. OGC API - Common provides mechanisms to publish lists of concepts as items 90 | in collections. pygeoapi also provides a mechanism to advertise the concepts using the SKOS ontology via its JSON-LD 91 | encoding. In the coincidence that a concept has a geometry property, the codelist can even be published as OGC API - Features 92 | (on a map). 93 | 94 | !!! question "Publish a codelist via OGC API" 95 | 96 | A German Soiltype codelist has been made available in CSV format in `workshop/exercises/data/bodenart.en.csv`. Use the [CSV provider](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-features.html#csv) to publish this dataset in pygeoapi. Which URL would you use to reference a concept in the published list? 97 | 98 | ``` {.yaml linenums="1"} 99 | SoilTypes: 100 | type: collection 101 | title: Soil types of Germany 102 | description: Bodenarten auf Basis der Bodenkundlichen Kartieranleitung 5. Auflage (KA5) 103 | keywords: 104 | - soiltype 105 | links: 106 | - type: text/html 107 | rel: canonical 108 | title: Soil types of Germany 109 | href: https://registry.gdi-de.org/codelist/de.bund.thuenen/bodenart 110 | hreflang: de 111 | extents: 112 | spatial: 113 | bbox: [0,0,0,0] 114 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 115 | providers: 116 | - type: feature 117 | name: CSV 118 | data: /data/bodenart.en.csv 119 | id_field: Label 120 | geometry: 121 | x_field: x 122 | y_field: y 123 | ``` 124 | 125 | # Summary 126 | 127 | Congratulations! You have worked with pygeoapi for INSPIRE compliance 128 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/security-access-control.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Security and access control 3 | --- 4 | 5 | # Security and access control 6 | 7 | ## Overview 8 | 9 | Security in general is a broad and complex topic, affecting the entire development lifecycle. 10 | It is recommended to follow security best practices during all development phases like design, coding and deployment. 11 | In this workshop we will focus only on API security and access control, rather than the full range of application security topics. 12 | 13 | ## API security 14 | 15 | API security is the whole process to protect APIs from attacks. It is part of the more general security guidelines that are being treated in the OWASP Top Ten document. So those recommendations still apply. 16 | 17 | !!! Note "Application Security" 18 | 19 | The Open Web Application Security Project (OWASP) [Top Ten document](https://owasp.org/www-project-top-ten/) is a very good tool to ensure the bare minimum against the security risks and manage critical treats that are most likely affecting your code. 20 | 21 | ## Access control 22 | 23 | Access control is another fundamental part of the Open Web Application Security Project and addresses the Identity and Access Management (IAM) of an API. 24 | IAM consists of two different parts of a security flow: 25 | 26 | - *Authentication* (AuthN) verifies the user's identity in order to allow or deny subsequent access (see next) 27 | - *Authorization* (AuthZ) validates permissions of a user (identity) to access a resource. The permissions of that identity are checked against a resource's policies to (dis)allow access to, for example, (parts of) an API. 28 | 29 | These parts are usually managed by dedicated infrastructures and solutions which usually provide most of the security requirements out-of-the-box. 30 | 31 | !!! Note "OpenAPI Security Specification" 32 | 33 | The OpenAPI specification has very well-defined elements for developers and adopters. The most relevant are: 34 | 35 | - [Security Scheme Object](https://swagger.io/specification/#security-scheme-object) defines the security schemes that can be used by the operations. Supported schemes are *HTTP Authentication*, an *API Key*, *OAuth2*'s flows and *OpenID Connect*. 36 | - [Security Requirement Object](https://swagger.io/specification/#security-requirement-object) defines the list of required security schemes to execute an operation. 37 | 38 | ## pygeoapi considerations 39 | 40 | pygeoapi does not yet support OpenAPI security elements. Future implementation could include generation of pygeoapi's OpenAPI document with a security configuration, or to generate from a known access control solution/application (such as [fastgeoapi](https://github.com/geobeyond/fastgeoapi) or [pygeoapi-auth](https://github.com/cartologic/pygeoapi-auth)). 41 | 42 | Direct access control implementation is not in scope for pygeoapi. The desired approach here would be to leverage an existing solution and define/integrate the secured endpoints accordingly. For example, fastgeoapi or pygeoapi-auth could be deployed downstream of pygeoapi, and govern access to specific endpoints (collections, items, etc.). 43 | 44 | 45 | !!! Note 46 | 47 | The [pygeoapi official documentation](https://docs.pygeoapi.io/en/latest/security.html) provides the project's official status on security implementation updates, and should be visited to keep up to date with the latest status on security implementation in the project. 48 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/semantic-web-linked-data.md: -------------------------------------------------------------------------------- 1 | --- 2 | Title: Semantic Web and Linked Data 3 | --- 4 | 5 | # Semantic Web and Linked Data 6 | 7 | This section touches on 3 aspects of the Semantic Web: 8 | 9 | - [Search engines](#search-engines) 10 | - [Publish spatial data in the semantic web](#publish-spatial-data-in-the-semantic-web) 11 | - [Proxy to semantic web](#proxy-to-the-semantic-web) 12 | 13 | ## Search engines 14 | 15 | Search engines use technology similar to the Semantic Web to facilitate capturing structured data (aka rich snippets) from web pages. 16 | pygeoapi supports this use case via embedding a `schema.org` JSON-LD snippet in the HTML encoding, 17 | 18 | !!! tip 19 | 20 | The `schema.org` ontology is not a formal Semantic Web ontology, it is therefore a bit disconnected from the rest of the Semantic Web 21 | 22 | !!! tip 23 | 24 | See more information at [Search Engine Optimization](./seo.md) 25 | 26 | ## Publish spatial data in the Semantic Web 27 | 28 | OGC API - Common adopted a number of W3C conventions, which bring OGC APIs closer to the standards of Semantic Web, 29 | compared to first generation OGC Web Service (OWS) standards. 30 | 31 | Currently, pygeaopi does not aim to be a full implementation of Semantic Web, however it is possible to advertise 32 | some aspects of the Semantic Web so the data can be traversed by Semantic Web aware clients. 33 | 34 | !!! question "Use a SPARQL client to query pygeoapi" 35 | 36 | [SPARQL](https://en.wikipedia.org/wiki/SPARQL) is commonly known as the query language to query triple stores. 37 | However you can also use SPARQL to query graphs of linked web resources. The SPARQL client traverses links between 38 | the resources to locate the requested triples. [Jena ARQ](https://jena.apache.org/documentation/query/) is a command 39 | line SPARQL client which is able to run such queries. Jena is quite difficult to set up, although there is a 40 | [Docker image](https://hub.docker.com/r/stain/jena) available. As an alternative we will use a web-based implementation 41 | of the ARQ engine. Navigate to [https://demos.isl.ics.forth.gr/sparql-ld-endpoint](https://demos.isl.ics.forth.gr/sparql-ld-endpoint/) 42 | and replace the query in the textbox with: 43 | 44 | 45 | ``` {.sql linenums="1"} 46 | SELECT * WHERE { 47 | SERVICE { 48 | { 49 | ?s ?p ?o 50 | } 51 | } 52 | } 53 | ``` 54 | 55 | A query to an item returns the item with its geometry: 56 | 57 | ``` {.sql linenums="1"} 58 | SELECT * WHERE { 59 | SERVICE { 60 | {{ ?s ?p ?o }} 61 | } 62 | } 63 | ``` 64 | 65 | Notice that the SPARQL client fails if you hardcode the HTML format. 66 | 67 | ``` {.sql linenums="1"} 68 | SELECT * WHERE { 69 | SERVICE { 70 | { ?s ?p ?o } 71 | } 72 | } 73 | ``` 74 | 75 | JSON-LD as expected by search engines has some challenges for semantic web tools. So how does it work if the format is not hardcoded? 76 | The SPARQL engine **negotiates** with the endpoint to evaluate which (RDF) encodings are available, and based on the content negotiation 77 | it requests the `JSON_LD` encoding via `f=jsonld`. 78 | 79 | pygeoapi adopted conventions of the [JSON-LD](https://json-ld.org) community to annotate JSON as RDF. For features, each property (column in a source table) 80 | is annotated by a semantic concept. The related configuration to apply the annotations is managed in the context element of a resource definition 81 | 82 | !!! tip 83 | 84 | Read more in the [pygeoapi documentation](https://docs.pygeoapi.io/configuration#Linked_data). 85 | 86 | ``` {.yaml linenums="1"} 87 | context: 88 | - schema: https://schema.org/ 89 | stn_id: schema:identifer 90 | datetime: 91 | "@id": schema:observationDate 92 | "@type": schema:DateTime 93 | value: 94 | "@id": schema:value 95 | "@type": schema:Number 96 | ``` 97 | 98 | ## Proxy to the Semantic Web 99 | 100 | Spatial data engineers are generally challenged when importing and visualizing fragments of the semantic web. The number of spatial 101 | [clients currently supporting SQARQL](https://plugins.qgis.org/plugins/sparqlunicorn) interaction is limited and requires expert knowledge to use. 102 | A group within the pygeoapi community aims to facilitate semantic web access for spatial data engineers by introducing pygeoapi as a proxy 103 | between the typical GIS clients and the semantic web. 104 | 105 | A [new feature](https://github.com/geopython/pygeoapi/pull/615) is being prepared which introduces a SPARQL provider to pygeoapi. 106 | The provider enables to browse the results of a SPARQL query as an OGC API - Features collection. 107 | 108 | # Summary 109 | 110 | Congratulations! You can now configure pygeoapi configurations with linked data concepts. 111 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/seo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Search Engine Optimization (SEO) 3 | --- 4 | 5 | # Search Engine Optimization (SEO) 6 | 7 | OGC API - Features adopted the Spatial Data on the Web [Best Practice 2: Make your spatial data indexable by search engines](https://www.w3.org/TR/sdw-bp/#indexable-by-search-engines) with the recommendation to [include HTML as an output format of any OGC API](http://docs.ogc.org/is/17-069r3/17-069r3.html#_requirements_class_html). It means that users can navigate an OGC API from within their browser and Search Engines are able to crawl the content. 8 | 9 | An aspect to consider is that, since the API becomes a webpage, common practices for web architecture and development become relevant: 10 | 11 | - does the website have a clear navigation? 12 | - is a company logo, branding, privacy statement, cookie warning included? 13 | - is the webpage [WCAG](https://www.w3.org/TR/WCAG21) accessable? 14 | 15 | !!! tip 16 | 17 | Notice that the pygeoapi configuration also has an option to disable HTML output. In that scenario, only the JSON output is available. 18 | 19 | On the Web, websites are typically visited by [web crawlers](https://en.wikipedia.org/wiki/Web_crawler) of popular search engines. Crawlers 20 | are automated processes which aid in building the index of the search engine. Crawlers follow links on the Web to identify new or updated 21 | content. Cross linking your API to other resources therefore increases the visibility (and ranking) of your API. 22 | 23 | The British Geo6 wrote an extensive [best practice on SEO for data publishers](https://www.gov.uk/government/publications/search-engine-optimisation-for-publishers-best-practice-guide) which offers a good overview of SEO in the scope of data publications. 24 | 25 | ## Tweaking Web Crawler behaviour 26 | 27 | This paragraph introduces you to some mechanisms which facilitate or block web crawlers to index your content. 28 | 29 | If you are not interested in having your content indexed by search engines, you can provide a [robots.txt](https://en.wikipedia.org/wiki/Robots_exclusion_standard) 30 | file at the root of your website, specifying which folders should not be indexed. More drastically is the option to block access for crawlers or bots to your content 31 | by filtering traffic to the website based on the HTTP [User-Agent header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent). Such a rule can 32 | be added to a firewall or web server configuration. 33 | 34 | A `robots.txt` file can also include a link to a [Sitemap](https://en.wikipedia.org/wiki/Sitemaps). Many search engines provide the option to submit a sitemap 35 | in order to speed up crawling and indexing. pygeoapi does not provide a sitemap of its content, but you can create your own sitemap (publish as `/sitemap.xml`), 36 | specifying URLs of your desired content to be indexed. 37 | 38 | Search engines provide tooling to evaluate the search behaviour of your website. These tools can provide valuable insight in the findability of your website 39 | and content (e.g. keywords used to locate your website). 40 | 41 | ## Schema.org/Dataset 42 | 43 | Search engines cooperate in the [Schema.org](https://schema.org) initiative. Schema.org enables you to annotate your website using the `schema.org` vocabulaire, in 44 | order for search engines to index the content in a structured manner. Google was the first to employ these annotations to provide a [dedicated search engine for datasets](https://datasetsearch.research.google.com/). pygeoapi adds `schema.org/Dataset` annotations to collection pages, so collections are automagically included in Google's dataset search. 45 | 46 | !!! question "Evaluate the schema.org annotations in collections" 47 | 48 | Google provides a [tool](https://search.google.com/test/rich-results) to evaluate `Schema.org` annotation in websites. Try evaluating a collection endpoint of pygeoapi 49 | in the tool. If you run pygeoapi locally (not accessible to google), you can copy the source of a page as HTML into the `` tab, otherwise you can paste the URL of the page in the `URL` tab. 50 | 51 | !!! note 52 | 53 | A similar tool is made available by [Yandex](https://webmaster.yandex.com/tools/microtest) (note that registration is required). 54 | -------------------------------------------------------------------------------- /workshop/content/docs/advanced/ui-custom-templates.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: UI customization and templating 3 | --- 4 | 5 | # UI customization and templating 6 | 7 | pygeoapi adopted the Jinja2 templating mechanism to style HTML output. Each element visualized on the HTML output is 8 | customizable by overriding the relevant template. Templates are located in the [`pygeoapi/templates`](https://github.com/geopython/pygeoapi/tree/master/pygeoapi/templates) folder. 9 | It is possible to override any template by copying it into a separate folder and adjust it to your needs. In the pygeoapi 10 | configuration you can then indicate the path to the override folder. Notice that for files which are not placed 11 | in the override folder, the original file is used. 12 | 13 | !!! caution 14 | 15 | For any customization, mind that with a new version of pygeoapi changes on the default templates are not automatically 16 | available on the overriden files. Upgrades need to be carefully tested and validated. 17 | 18 | ## Jinja2 19 | 20 | [Jinja2](https://jinja.palletsprojects.com) is a common templating concept in the Python community. With a minimal background 21 | in HTML you will be able to make minor but meaningful customizations. At the core of pygeoapi's template setup is the 22 | [`_base.html`](https://github.com/geopython/pygeoapi/blob/master/pygeoapi/templates/_base.html) template, which defines the 23 | header and footer of the page. The fragment below defines the footer of the page, notice the parameters in curly braces, 24 | which are replaced by dynamic content. 25 | 26 | ``` {.html linenums="1"} 27 |
28 | {% trans %}Powered by {% endtrans %} 29 | 30 | 31 | {{ version }} 32 |
33 | ``` 34 | 35 | !!! help "Customizing an HTML page" 36 | 37 | Copy `_base.html` to a separate folder. Adjust some elements on that page (e.g. logo image). Then, include a reference to the new folder in 38 | the pygeoapi configuration. Restart the service. Verify the result. 39 | 40 | ## CSS customizations 41 | 42 | From the customized HTML template you can reference a new stylesheet file with customizations or directly add your customizations to [/static/css/default.css](https://github.com/geopython/pygeoapi/blob/master/pygeoapi/static/css/default.css). 43 | 44 | # Summary 45 | 46 | Congratulations! You've added a custom look and feel to your pygeoapi deployment. 47 | -------------------------------------------------------------------------------- /workshop/content/docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/favicon.ico -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/OGC_APIs_banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/OGC_APIs_banner.jpg -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/collection-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/collection-list.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/features-hyderabad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/features-hyderabad.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/jupyter1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/jupyter1.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/leaflet-estonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/leaflet-estonia.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/leaflet-estonia2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/leaflet-estonia2.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/leaflet-hyderabad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/leaflet-hyderabad.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/leaflet-hyderabad2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/leaflet-hyderabad2.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/leaflet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/leaflet.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/macosx-airplay-disable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/macosx-airplay-disable.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/maps-response.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/maps-response.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/new-connection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/new-connection.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/oaproc-squared-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/oaproc-squared-1.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/oaproc-squared-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/oaproc-squared-2.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/oaproc-squared-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/oaproc-squared-3.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/ogc-api-building-blocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/ogc-api-building-blocks.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/ogcapis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/ogcapis.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/prepopulated-catalogues.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/prepopulated-catalogues.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/pygeoapi-icon-notrans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/pygeoapi-icon-notrans.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/pygeoapi-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/pygeoapi-logo.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-addlayer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-addlayer.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-vtiles1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-vtiles1.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-vtiles2-estonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-vtiles2-estonia.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-vtiles2-hyderabad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-vtiles2-hyderabad.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-vtiles2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-vtiles2.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-vtiles3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-vtiles3.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-vtiles4-hyderabad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-vtiles4-hyderabad.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/qgis-vtiles4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/qgis-vtiles4.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/search-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/search-results.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/vtiles-attributes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/vtiles-attributes.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/vtiles-es.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/vtiles-es.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/vtiles-estonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/vtiles-estonia.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/vtiles-estonia2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/vtiles-estonia2.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/vtiles-hyderabad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/vtiles-hyderabad.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/images/vtiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/content/docs/assets/images/vtiles.png -------------------------------------------------------------------------------- /workshop/content/docs/assets/javascripts/custom.js: -------------------------------------------------------------------------------- 1 | document.querySelectorAll(".use-termynal").forEach(node => { 2 | node.style.display = "block"; 3 | new Termynal(node, { 4 | lineDelay: 500 5 | }); 6 | }); 7 | const progressLiteralStart = "---> 100%"; 8 | const promptLiteralStart = "$ "; 9 | const customPromptLiteralStart = "# "; 10 | const termynalActivateClass = "termy"; 11 | let termynals = []; 12 | 13 | function createTermynals() { 14 | document 15 | .querySelectorAll(`.${termynalActivateClass} .highlight`) 16 | .forEach(node => { 17 | const text = node.textContent; 18 | const lines = text.split("\n"); 19 | const useLines = []; 20 | let buffer = []; 21 | function saveBuffer() { 22 | if (buffer.length) { 23 | let isBlankSpace = true; 24 | buffer.forEach(line => { 25 | if (line) { 26 | isBlankSpace = false; 27 | } 28 | }); 29 | dataValue = {}; 30 | if (isBlankSpace) { 31 | dataValue["delay"] = 0; 32 | } 33 | if (buffer[buffer.length - 1] === "") { 34 | // A last single
won't have effect 35 | // so put an additional one 36 | buffer.push(""); 37 | } 38 | const bufferValue = buffer.join("
"); 39 | dataValue["value"] = bufferValue; 40 | useLines.push(dataValue); 41 | buffer = []; 42 | } 43 | } 44 | for (let line of lines) { 45 | if (line === progressLiteralStart) { 46 | saveBuffer(); 47 | useLines.push({ 48 | type: "progress" 49 | }); 50 | } else if (line.startsWith(promptLiteralStart)) { 51 | saveBuffer(); 52 | const value = line.replace(promptLiteralStart, "").trimEnd(); 53 | useLines.push({ 54 | type: "input", 55 | value: value 56 | }); 57 | } else if (line.startsWith("// ")) { 58 | saveBuffer(); 59 | const value = "💬 " + line.replace("// ", "").trimEnd(); 60 | useLines.push({ 61 | value: value, 62 | class: "termynal-comment", 63 | delay: 0 64 | }); 65 | } else if (line.startsWith(customPromptLiteralStart)) { 66 | saveBuffer(); 67 | const promptStart = line.indexOf(promptLiteralStart); 68 | if (promptStart === -1) { 69 | console.error("Custom prompt found but no end delimiter", line) 70 | } 71 | const prompt = line.slice(0, promptStart).replace(customPromptLiteralStart, "") 72 | let value = line.slice(promptStart + promptLiteralStart.length); 73 | useLines.push({ 74 | type: "input", 75 | value: value, 76 | prompt: prompt 77 | }); 78 | } else { 79 | buffer.push(line); 80 | } 81 | } 82 | saveBuffer(); 83 | const div = document.createElement("div"); 84 | node.replaceWith(div); 85 | const termynal = new Termynal(div, { 86 | lineData: useLines, 87 | noInit: true, 88 | lineDelay: 500 89 | }); 90 | termynals.push(termynal); 91 | }); 92 | } 93 | 94 | function loadVisibleTermynals() { 95 | termynals = termynals.filter(termynal => { 96 | if (termynal.container.getBoundingClientRect().top - innerHeight <= 0) { 97 | termynal.init(); 98 | return false; 99 | } 100 | return true; 101 | }); 102 | } 103 | window.addEventListener("scroll", loadVisibleTermynals); 104 | createTermynals(); 105 | loadVisibleTermynals(); 106 | -------------------------------------------------------------------------------- /workshop/content/docs/assets/javascripts/termynal.js: -------------------------------------------------------------------------------- 1 | /** 2 | * termynal.js 3 | * A lightweight, modern and extensible animated terminal window, using 4 | * async/await. 5 | * 6 | * @author Ines Montani 7 | * @version 0.0.1 8 | * @license MIT 9 | */ 10 | 11 | 'use strict'; 12 | 13 | /** Generate a terminal widget. */ 14 | class Termynal { 15 | /** 16 | * Construct the widget's settings. 17 | * @param {(string|Node)=} container - Query selector or container element. 18 | * @param {Object=} options - Custom settings. 19 | * @param {string} options.prefix - Prefix to use for data attributes. 20 | * @param {number} options.startDelay - Delay before animation, in ms. 21 | * @param {number} options.typeDelay - Delay between each typed character, in ms. 22 | * @param {number} options.lineDelay - Delay between each line, in ms. 23 | * @param {number} options.progressLength - Number of characters displayed as progress bar. 24 | * @param {string} options.progressChar – Character to use for progress bar, defaults to █. 25 | * @param {number} options.progressPercent - Max percent of progress. 26 | * @param {string} options.cursor – Character to use for cursor, defaults to ▋. 27 | * @param {Object[]} lineData - Dynamically loaded line data objects. 28 | * @param {boolean} options.noInit - Don't initialise the animation. 29 | */ 30 | constructor(container = '#termynal', options = {}) { 31 | this.container = (typeof container === 'string') ? document.querySelector(container) : container; 32 | this.pfx = `data-${options.prefix || 'ty'}`; 33 | this.originalStartDelay = this.startDelay = options.startDelay 34 | || parseFloat(this.container.getAttribute(`${this.pfx}-startDelay`)) || 600; 35 | this.originalTypeDelay = this.typeDelay = options.typeDelay 36 | || parseFloat(this.container.getAttribute(`${this.pfx}-typeDelay`)) || 90; 37 | this.originalLineDelay = this.lineDelay = options.lineDelay 38 | || parseFloat(this.container.getAttribute(`${this.pfx}-lineDelay`)) || 1500; 39 | this.progressLength = options.progressLength 40 | || parseFloat(this.container.getAttribute(`${this.pfx}-progressLength`)) || 40; 41 | this.progressChar = options.progressChar 42 | || this.container.getAttribute(`${this.pfx}-progressChar`) || '█'; 43 | this.progressPercent = options.progressPercent 44 | || parseFloat(this.container.getAttribute(`${this.pfx}-progressPercent`)) || 100; 45 | this.cursor = options.cursor 46 | || this.container.getAttribute(`${this.pfx}-cursor`) || '▋'; 47 | this.lineData = this.lineDataToElements(options.lineData || []); 48 | this.loadLines() 49 | if (!options.noInit) this.init() 50 | } 51 | 52 | loadLines() { 53 | // Load all the lines and create the container so that the size is fixed 54 | // Otherwise it would be changing and the user viewport would be constantly 55 | // moving as she/he scrolls 56 | const finish = this.generateFinish() 57 | finish.style.visibility = 'hidden' 58 | this.container.appendChild(finish) 59 | // Appends dynamically loaded lines to existing line elements. 60 | this.lines = [...this.container.querySelectorAll(`[${this.pfx}]`)].concat(this.lineData); 61 | for (let line of this.lines) { 62 | line.style.visibility = 'hidden' 63 | this.container.appendChild(line) 64 | } 65 | const restart = this.generateRestart() 66 | restart.style.visibility = 'hidden' 67 | this.container.appendChild(restart) 68 | this.container.setAttribute('data-termynal', ''); 69 | } 70 | 71 | /** 72 | * Initialise the widget, get lines, clear container and start animation. 73 | */ 74 | init() { 75 | /** 76 | * Calculates width and height of Termynal container. 77 | * If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS. 78 | */ 79 | const containerStyle = getComputedStyle(this.container); 80 | this.container.style.width = containerStyle.width !== '0px' ? 81 | containerStyle.width : undefined; 82 | this.container.style.minHeight = containerStyle.height !== '0px' ? 83 | containerStyle.height : undefined; 84 | 85 | this.container.setAttribute('data-termynal', ''); 86 | this.container.innerHTML = ''; 87 | for (let line of this.lines) { 88 | line.style.visibility = 'visible' 89 | } 90 | this.start(); 91 | } 92 | 93 | /** 94 | * Start the animation and rener the lines depending on their data attributes. 95 | */ 96 | async start() { 97 | this.addFinish() 98 | await this._wait(this.startDelay); 99 | 100 | for (let line of this.lines) { 101 | const type = line.getAttribute(this.pfx); 102 | const delay = line.getAttribute(`${this.pfx}-delay`) || this.lineDelay; 103 | 104 | if (type == 'input') { 105 | line.setAttribute(`${this.pfx}-cursor`, this.cursor); 106 | await this.type(line); 107 | await this._wait(delay); 108 | } 109 | 110 | else if (type == 'progress') { 111 | await this.progress(line); 112 | await this._wait(delay); 113 | } 114 | 115 | else { 116 | this.container.appendChild(line); 117 | await this._wait(delay); 118 | } 119 | 120 | line.removeAttribute(`${this.pfx}-cursor`); 121 | } 122 | this.addRestart() 123 | this.finishElement.style.visibility = 'hidden' 124 | this.lineDelay = this.originalLineDelay 125 | this.typeDelay = this.originalTypeDelay 126 | this.startDelay = this.originalStartDelay 127 | } 128 | 129 | generateRestart() { 130 | const restart = document.createElement('a') 131 | restart.onclick = (e) => { 132 | e.preventDefault() 133 | this.container.innerHTML = '' 134 | this.init() 135 | } 136 | restart.href = '#' 137 | restart.setAttribute('data-terminal-control', '') 138 | restart.innerHTML = "restart ↻" 139 | return restart 140 | } 141 | 142 | generateFinish() { 143 | const finish = document.createElement('a') 144 | finish.onclick = (e) => { 145 | e.preventDefault() 146 | this.lineDelay = 0 147 | this.typeDelay = 0 148 | this.startDelay = 0 149 | } 150 | finish.href = '#' 151 | finish.setAttribute('data-terminal-control', '') 152 | finish.innerHTML = "fast →" 153 | this.finishElement = finish 154 | return finish 155 | } 156 | 157 | addRestart() { 158 | const restart = this.generateRestart() 159 | this.container.appendChild(restart) 160 | } 161 | 162 | addFinish() { 163 | const finish = this.generateFinish() 164 | this.container.appendChild(finish) 165 | } 166 | 167 | /** 168 | * Animate a typed line. 169 | * @param {Node} line - The line element to render. 170 | */ 171 | async type(line) { 172 | const chars = [...line.textContent]; 173 | line.textContent = ''; 174 | this.container.appendChild(line); 175 | 176 | for (let char of chars) { 177 | const delay = line.getAttribute(`${this.pfx}-typeDelay`) || this.typeDelay; 178 | await this._wait(delay); 179 | line.textContent += char; 180 | } 181 | } 182 | 183 | /** 184 | * Animate a progress bar. 185 | * @param {Node} line - The line element to render. 186 | */ 187 | async progress(line) { 188 | const progressLength = line.getAttribute(`${this.pfx}-progressLength`) 189 | || this.progressLength; 190 | const progressChar = line.getAttribute(`${this.pfx}-progressChar`) 191 | || this.progressChar; 192 | const chars = progressChar.repeat(progressLength); 193 | const progressPercent = line.getAttribute(`${this.pfx}-progressPercent`) 194 | || this.progressPercent; 195 | line.textContent = ''; 196 | this.container.appendChild(line); 197 | 198 | for (let i = 1; i < chars.length + 1; i++) { 199 | await this._wait(this.typeDelay); 200 | const percent = Math.round(i / chars.length * 100); 201 | line.textContent = `${chars.slice(0, i)} ${percent}%`; 202 | if (percent>progressPercent) { 203 | break; 204 | } 205 | } 206 | } 207 | 208 | /** 209 | * Helper function for animation delays, called with `await`. 210 | * @param {number} time - Timeout, in ms. 211 | */ 212 | _wait(time) { 213 | return new Promise(resolve => setTimeout(resolve, time)); 214 | } 215 | 216 | /** 217 | * Converts line data objects into line elements. 218 | * 219 | * @param {Object[]} lineData - Dynamically loaded lines. 220 | * @param {Object} line - Line data object. 221 | * @returns {Element[]} - Array of line elements. 222 | */ 223 | lineDataToElements(lineData) { 224 | return lineData.map(line => { 225 | let div = document.createElement('div'); 226 | div.innerHTML = `${line.value || ''}`; 227 | 228 | return div.firstElementChild; 229 | }); 230 | } 231 | 232 | /** 233 | * Helper function for generating attributes string. 234 | * 235 | * @param {Object} line - Line data object. 236 | * @returns {string} - String of attributes. 237 | */ 238 | _attributes(line) { 239 | let attrs = ''; 240 | for (let prop in line) { 241 | // Custom add class 242 | if (prop === 'class') { 243 | attrs += ` class=${line[prop]} ` 244 | continue 245 | } 246 | if (prop === 'type') { 247 | attrs += `${this.pfx}="${line[prop]}" ` 248 | } else if (prop !== 'value') { 249 | attrs += `${this.pfx}-${prop}="${line[prop]}" ` 250 | } 251 | } 252 | 253 | return attrs; 254 | } 255 | } 256 | 257 | /** 258 | * HTML API: If current script has container(s) specified, initialise Termynal. 259 | */ 260 | if (document.currentScript.hasAttribute('data-termynal-container')) { 261 | const containers = document.currentScript.getAttribute('data-termynal-container'); 262 | containers.split('|') 263 | .forEach(container => new Termynal(container)) 264 | } -------------------------------------------------------------------------------- /workshop/content/docs/assets/stylesheets/custom.css: -------------------------------------------------------------------------------- 1 | .termynal-comment { 2 | color: #4a968f; 3 | font-style: italic; 4 | display: block; 5 | } 6 | 7 | .termy [data-termynal] { 8 | white-space: pre-wrap; 9 | } 10 | 11 | a.external-link::after { 12 | /* \00A0 is a non-breaking space 13 | to make the mark be on the same line as the link 14 | */ 15 | content: "\00A0[↪]"; 16 | } 17 | 18 | a.internal-link::after { 19 | /* \00A0 is a non-breaking space 20 | to make the mark be on the same line as the link 21 | */ 22 | content: "\00A0↪"; 23 | } 24 | -------------------------------------------------------------------------------- /workshop/content/docs/assets/stylesheets/pygeoapi.css: -------------------------------------------------------------------------------- 1 | [data-md-color-scheme="pygeoapi"] { 2 | --md-primary-fg-color: #3b6a95; 3 | --md-primary-fg-color--light: #6c98c6; 4 | --md-primary-fg-color--dark: #004067; 5 | } -------------------------------------------------------------------------------- /workshop/content/docs/assets/stylesheets/termynal.css: -------------------------------------------------------------------------------- 1 | /** 2 | * termynal.js 3 | * 4 | * @author Ines Montani 5 | * @version 0.0.1 6 | * @license MIT 7 | */ 8 | 9 | :root { 10 | --color-bg: #252a33; 11 | --color-text: #eee; 12 | --color-text-subtle: #a2a2a2; 13 | } 14 | 15 | [data-termynal] { 16 | width: 750px; 17 | max-width: 100%; 18 | background: var(--color-bg); 19 | color: var(--color-text); 20 | font-size: 18px; 21 | /* font-family: 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; */ 22 | font-family: 'Roboto Mono', 'Fira Mono', Consolas, Menlo, Monaco, 'Courier New', Courier, monospace; 23 | border-radius: 4px; 24 | padding: 75px 45px 35px; 25 | position: relative; 26 | -webkit-box-sizing: border-box; 27 | box-sizing: border-box; 28 | } 29 | 30 | [data-termynal]:before { 31 | content: ''; 32 | position: absolute; 33 | top: 15px; 34 | left: 15px; 35 | display: inline-block; 36 | width: 15px; 37 | height: 15px; 38 | border-radius: 50%; 39 | /* A little hack to display the window buttons in one pseudo element. */ 40 | background: #d9515d; 41 | -webkit-box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; 42 | box-shadow: 25px 0 0 #f4c025, 50px 0 0 #3ec930; 43 | } 44 | 45 | [data-termynal]:after { 46 | content: 'bash'; 47 | position: absolute; 48 | color: var(--color-text-subtle); 49 | top: 5px; 50 | left: 0; 51 | width: 100%; 52 | text-align: center; 53 | } 54 | 55 | a[data-terminal-control] { 56 | text-align: right; 57 | display: block; 58 | color: #aebbff; 59 | } 60 | 61 | [data-ty] { 62 | display: block; 63 | line-height: 2; 64 | } 65 | 66 | [data-ty]:before { 67 | /* Set up defaults and ensure empty lines are displayed. */ 68 | content: ''; 69 | display: inline-block; 70 | vertical-align: middle; 71 | } 72 | 73 | [data-ty="input"]:before, 74 | [data-ty-prompt]:before { 75 | margin-right: 0.75em; 76 | color: var(--color-text-subtle); 77 | } 78 | 79 | [data-ty="input"]:before { 80 | content: '$'; 81 | } 82 | 83 | [data-ty][data-ty-prompt]:before { 84 | content: attr(data-ty-prompt); 85 | } 86 | 87 | [data-ty-cursor]:after { 88 | content: attr(data-ty-cursor); 89 | font-family: monospace; 90 | margin-left: 0.5em; 91 | -webkit-animation: blink 1s infinite; 92 | animation: blink 1s infinite; 93 | } 94 | 95 | 96 | /* Cursor animation */ 97 | 98 | @-webkit-keyframes blink { 99 | 50% { 100 | opacity: 0; 101 | } 102 | } 103 | 104 | @keyframes blink { 105 | 50% { 106 | opacity: 0; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /workshop/content/docs/conclusion.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Conclusion 3 | --- 4 | 5 | # Conclusion 6 | 7 | We hope this workshop provided a valuable overview of the many features of pygeoapi. The project's 8 | goal is enabling low barrier, simple and flexible data publishing, using the OGC API suite of standards. 9 | 10 | [![pygeoapi logo](assets/images/pygeoapi-logo.png){ width=40% }](https://pygeoapi.io) 11 | 12 | [![OGC APIs banner](assets/images/OGC_APIs_banner.jpg){ width=40% }](https://ogcapi.ogc.org) 13 | 14 | # FOSS4G 2024 15 | 16 | For those in attendance at [FOSS4G 2024](https://2024.foss4g.org): 17 | 18 | - come to the [pygeoapi project status](https://talks.osgeo.org/foss4g-2024/talk/VQNTSA) presentation on Wednesday 04 December at 17:45, in room II 19 | - or the other talks related to pygeoapi: 20 | - [Unifying Standards for Water Data Exchange: Leveraging OGC API - EDR and pygeoapi](https://talks.osgeo.org/foss4g-2024/talk/KRFWMJ) on Thursday 05 December at 15:00, in room III 21 | - [Rendering OGC API Compliant Vector Tiles on the Fly with pygeoapi + Elasticsearch](https://talks.osgeo.org/foss4g-2024/talk/ZLGZPA) on Friday 06 December at 12:00, in room I 22 | - [Wagtail CMS + pygeoapi = Modern SDI for the current needs ](https://talks.osgeo.org/foss4g-2024/talk/L9PB9K) on Friday 06 December at 12:30, in room I 23 | - the pygeoapi team will be at the [Community Code Sprint](https://2024.foss4g.org/en/additional-events) on 07-08 December, at the IFPA Campus Belém 24 | 25 | # Contributing 26 | 27 | Suggestions, improvements and fixes are always welcome. Please visit our [community](https://pygeoapi.io/community) 28 | page for more information on getting in touch. 29 | 30 | Thank you for your interest in pygeoapi! 31 | -------------------------------------------------------------------------------- /workshop/content/docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Diving into pygeoapi 3 | --- 4 | 5 | # Welcome to the Diving into pygeoapi workshop! 6 | 7 | Version: 1.5.0 8 | 9 | ![pygeoapi logo](assets/images/pygeoapi-logo.png) 10 | 11 | [pygeoapi](https://pygeoapi.io) is a Python server implementation of the [OGC API](https://ogcapi.ogc.org) suite of standards. The project emerged as part of the next generation OGC API efforts in 2018 and provides the capability for organizations to deploy a RESTful OGC API endpoint using OpenAPI, GeoJSON, and HTML. pygeoapi is open source and released under an MIT license. 12 | 13 | **Diving into pygeoapi** is a half day workshop designed for users to become familiar with installing, configuring, publishing data to and extending pygeoapi. This workshop will cover publishing geospatial data to the Web using pygeoapi in support of the suite of OGC API standards. 14 | 15 | 16 | This workshop covers a wide range of topics (install/setup/configuration, publishing, cloud, templating, plugins, etc.). Please see the left hand navigation for the table of contents. 17 | 18 | # Your [FOSS4G 2024](https://2024.foss4g.org) workshop team 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
Tom KralidisJoana SimoesAntonio CercielloBenjamin Webb
34 | 35 | # About this tutorial 36 | 37 | This tutorial is a combination of step-by-step explanations of various aspects of pygeoapi as well as a series of exercises to familiarize yourself with the project. 38 | 39 | Exercises are indicated as follows: 40 | 41 | !!! question "Example exercise" 42 | 43 | A section marked like this indicates that you can try out the exercise. 44 | 45 | !!! example "Example exercise with tabs" 46 | 47 | A section marked like this indicates that you can try out the exercise and choose your environment (Linux/Mac or Windows). 48 | 49 | === "Linux/Mac" 50 |
51 | ```bash 52 | docker run -p 5000:80 -v $(pwd)/default.config.yml:/pygeoapi/local.config.yml geopython/pygeoapi:latest 53 | ``` 54 |
55 | === "Windows" 56 |
57 | ```bash 58 | docker run -p 5000:80 -v ${pwd}/default.config.yml:/pygeoapi/local.config.yml geopython/pygeoapi:latest 59 | ``` 60 |
61 | 62 | Also you will notice tips and notes sections within the text: 63 | 64 | !!! tip 65 | 66 | Tips share additional help on how to best achieve tasks 67 | 68 | Examples are indicated as follows: 69 | 70 | Code 71 | ``` {.html linenums="1"} 72 | 73 | 74 | This is an HTML sample 75 | 76 | 77 | ``` 78 | 79 | Configuration 80 | ``` {.yaml linenums="1"} 81 | my-collection: 82 | type: collection 83 | title: my cool collection title 84 | description: my cool collection description 85 | ``` 86 | 87 | Snippets which need to be typed in a on a terminal/console are indicated as: 88 | 89 |
90 | ```bash 91 | echo 'Hello world' 92 | ``` 93 |
94 | 95 | # Workshop location and materials 96 | 97 | This workshop is always provided live at [https://dive.pygeoapi.io](https://dive.pygeoapi.io). 98 | 99 | The workshop contents, wiki and issue tracker are managed on GitHub at [https://github.com/geopython/diving-into-pygeoapi](https://github.com/geopython/diving-into-pygeoapi). 100 | 101 | ## Printing this workshop 102 | 103 | To print this workshop, navigate to the [print page](print_page) and select *File > Print > Save as PDF*. 104 | 105 | # Support 106 | 107 | A [Gitter](https://app.gitter.im/#/room/#geopython_diving-into-pygeoapi:gitter.im) channel exists for 108 | discussion and live support from the developers of the workshop and other workshop participants. 109 | 110 | For issues/bugs/suggestions or improvements/contributions, please use the [GitHub issue tracker](https://github.com/geopython/diving-into-pygeoapi/issues). 111 | 112 | All bugs, enhancements and issues can be reported on [GitHub](https://github.com/geopython/diving-into-pygeoapi/issues). 113 | 114 | As always, core pygeoapi support and community information can be found on the pygeoapi [website](https://pygeoapi.io/community). 115 | 116 | Contributions are always enncouraged and welcome! 117 | 118 | 119 | ## Now, on to the workshop. Let's go! 120 | -------------------------------------------------------------------------------- /workshop/content/docs/introduction.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction to pygeoapi 3 | --- 4 | 5 | # Introduction to pygeoapi 6 | 7 | The development team of pygeoapi (yes, spelled in lowercase) is excited to welcome you in this workshop! 8 | 9 | In this half day workshop, we will give you an introduction to pygeoapi, how to publish data, and provide 10 | resources and tips for future reading and reference (i.e. where to go when you don't know!). 11 | 12 | Although pygeoapi is written in Python and can be customizable and extensible (plugins) 13 | for Python developers, Python skills are not required to install, setup and publish your geospatial 14 | data as part of this workshop. All you need for the workshop is your favorite text editor and Docker (we will 15 | more information in the [setup section](setup.md)). 16 | 17 | ## Background reading 18 | 19 | The pygeoapi [website](https://pygeoapi.io) is the main entrypoint for both end-users and developers 20 | where you can find: 21 | 22 | * [official documentation](https://docs.pygeoapi.io) 23 | * the [default](https://pygeoapi.io/presentations/default) and [latest](https://pygeoapi.io/presentations/foss4g2023) presentations 24 | * [documentation and presentations archive](https://pygeoapi.io/documentation) 25 | * code on [GitHub](https://github.com/geopython/pygeoapi) 26 | * Docker images [available on Docker Hub](https://hub.docker.com/r/geopython/pygeoapi) 27 | * pygeoapi releases in the [Python Package Index (PyPI)](https://pypi.org/project/pygeoapi) 28 | 29 | Given pygeoapi implements a number of OGC API standards, you may also want to read about these 30 | on [ogcapi.ogc.org](https://ogcapi.ogc.org). 31 | 32 | ## Existing Deployments 33 | 34 | A number of organizations have deployed pygeoapi to their operations. To get a feel of how pygeoapi 35 | is used in practice, check out our up to date [live deployments page](https://github.com/geopython/pygeoapi/wiki/LiveDeployments). By 36 | default, the pygeoapi public demo at [demo.pygeoapi.io](https://demo.pygeoapi.io) is always maintained 37 | and made available by the development team. Check out the [main instance](https://demo.pygeoapi.io/master) which 38 | always runs the latest GitHub version. 39 | 40 | Interested in the demo site setup itself? [demo.pygeoapi.io](https://demo.pygeoapi.io) is developed in a [GitHub repository](https://github.com/geopython/demo.pygeoapi.io) using a 41 | DevOps continuous deployment (CD) workflow. 42 | Even more recent GitOps deployments were developed for [Geonovum](https://github.com/Geonovum/ogc-api-testbed) and the [European Commission Joint Research Center](https://github.com/justb4/ogc-api-jrc). 43 | 44 | The above examples may help as starting points for your own pygeoapi setup and deployment, so feel free to study and use them! 45 | 46 | ## History 47 | 48 | Starting in 2018, pygeoapi emerged as part of the initial efforts for the development of OGC API standards. OGC API 49 | code sprints were instrumental for agile development and pouring the foundation of the project. 50 | 51 | The core design principles are as follows: 52 | 53 | - simplicity / low barrier to entry 54 | - long term sustainability 55 | - modularity 56 | - extensibility 57 | - building on a large ecosystem of Free Open Source and OSGeo components such as GDAL, rasterio, Shapely, Pandas, Elasticsearch, PostGIS and many others 58 | 59 | The project was initiated by [Tom Kralidis](https://github.com/tomkralidis). Within weeks, several talented 60 | developers joined the project, which led to the formation of a core team and [Project Steering Committee (PSC)](https://pygeoapi.io/community/psc). Contributions continued 61 | as well from additional developers and users who happily provided new functionality, bug fixes, and documentation 62 | updates. As a result, a healthy community quickly emerged with a common interest in open source, OGC API standards, low barrier, modular and extensible. The 63 | rest, as they say, is history. 64 | 65 | pygeoapi is an [OSGeo Project](https://www.osgeo.org/projects/pygeoapi) and an OGC Reference Implementation. 66 | -------------------------------------------------------------------------------- /workshop/content/docs/publishing/first.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exercise 1 - Your first dataset 3 | --- 4 | 5 | # Exercise 1 - Your first dataset 6 | 7 | In this section you are going to publish a vector dataset. 8 | 9 | For this exercise, we will use a CSV dataset of [Bathing waters in Estonia](https://github.com/geopython/diving-into-pygeoapi/tree/main/workshop/exercises/data/tartu/bathingwater-estonia.csv), 10 | kindly provided by [Estonian Health Board](https://terviseamet.ee). 11 | 12 | You can find this dataset in `workshop/exercises/data/tartu/bathingwater-estonia.csv`. 13 | 14 | This exercise consists of two steps: 15 | 16 | * adjust `workshop/exercises/pygeoapi.config.yml` to define this dataset as an OGC API - Features *collection* 17 | * ensure that pygeoapi can find and connect to the data file 18 | 19 | We will use the `workshop/exercises/docker-compose.yml` file provided. 20 | 21 | ## Verify the existing Docker Compose config 22 | 23 | Before making any changes, we will make sure that the initial Docker Compose 24 | setup provided to you is actually working. Two files are relevant: 25 | 26 | * `workshop/exercises/docker-compose.yml` 27 | * `workshop/exercises/pygeoapi.config.yml` 28 | 29 | To test: 30 | 31 | !!! question "Test the workshop configuration" 32 | 33 | 1. In a terminal shell navigate to the workshop folder and type: 34 | 35 | === "Linux/Mac" 36 | 37 |
38 | ```bash 39 | cd workshop/exercises 40 | docker compose up 41 | ``` 42 |
43 | 44 | === "Windows (PowerShell)" 45 | 46 |
47 | ```bash 48 | cd workshop/exercises 49 | docker compose up 50 | ``` 51 |
52 | 53 | 1. Open in your browser, verify some collections 54 | 1. Close by typing `CTRL-C` 55 | 56 | !!! note 57 | 58 | You may also run the Docker container in the background (detached) as follows: 59 | 60 | === "Linux/Mac" 61 | 62 |
63 | ```bash 64 | docker compose up -d 65 | docker ps # verify that the pygeoapi container is running 66 | # visit http://localhost:5000 in your browser, verify some collections 67 | docker logs --follow pygeoapi # view logs 68 | docker compose stop 69 | ``` 70 |
71 | 72 | === "Windows (PowerShell)" 73 | 74 |
75 | ```bash 76 | docker compose up -d 77 | docker ps # verify that the pygeoapi container is running 78 | # visit http://localhost:5000 in your browser, verify some collections 79 | docker logs --follow pygeoapi # view logs 80 | docker compose stop 81 | ``` 82 |
83 | ## Publish first dataset 84 | 85 | You are now ready to publish your first dataset. 86 | 87 | !!! question "Setting up the pygeoapi config file" 88 | 89 | 1. Open the file `workshop/exercises/pygeoapi/pygeoapi.config.yml` in your text editor 90 | 1. Look for the commented config section starting with `# START - EXERCISE 1 - Your First Collection` 91 | 1. Uncomment all lines until `# END - EXERCISE 1 - Your First Collection` 92 | 93 | Make sure that the indentation aligns (hint: directly under `# START ...`) 94 | 95 | The config section reads: 96 | 97 | ``` {.yml linenums="185"} 98 | Bathing_Water_Estonia: 99 | type: collection 100 | title: Bathing Water Estonia 101 | description: Locations where the Estonian Health Board monitors the bathing water quality 102 | keywords: 103 | - bathing water 104 | - estonia 105 | links: 106 | - type: text/csv 107 | rel: canonical 108 | title: data 109 | href: https://avaandmed.eesti.ee/datasets/supluskohad 110 | hreflang: EE 111 | extents: 112 | spatial: 113 | bbox: [20,57,29,60] 114 | crs: http://www.opengis.net/def/crs/EPSG/0/4326 115 | providers: 116 | - type: feature 117 | name: CSV 118 | data: /data/tartu/bathingwater-estonia.csv 119 | id_field: id 120 | title_field: Name 121 | geometry: 122 | x_field: x 123 | y_field: y 124 | storage_crs: http://www.opengis.net/def/crs/EPSG/0/3300 125 | ``` 126 | 127 | The most relevant part is the `providers` section. Here, we define a `CSV Provider`, 128 | pointing the file path to the `/data` directory we will mount (see next) from the local 129 | directory into the Docker container above. Because a CSV is not a spatial file, we explicitly 130 | configure pygeoapi so that the longitude and latitude (x and y) is mapped from the columns `lon` 131 | and `lat` in the CSV file. Notice the `storage_crs` parameter, which indicates the coordinate system which is used in the source data. 132 | 133 | !!! Tip 134 | 135 | To learn more about the pygeoapi configuration syntax and conventions see 136 | the [relevant chapter in the documentation](https://docs.pygeoapi.io/en/latest/configuration.html). 137 | 138 | !!! Tip 139 | 140 | pygeoapi includes [numerous data providers](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-features.html#providers) which 141 | enable access to a variety of data formats. Via the OGR/GDAL plugin the number of supported formats is almost limitless. 142 | Consult the [data provider page](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-features.html#providers) how you can set up 143 | a connection to your dataset of choice. You can always copy a relevant example configuration and place it in the datasets section of 144 | the pygeoapi configuration file for your future project. 145 | 146 | ## Making data available in the Docker container 147 | 148 | As the Docker container (named `pygeoapi`) cannot directly access files on your 149 | local host system, we will use Docker volume mounts. This can be defined 150 | in the `docker-compose.yml` file as follows: 151 | 152 | !!! question "Configure access to the data" 153 | 154 | 1. Open the file `workshop/exercises/docker-compose.yml` 155 | 1. Look for the commented section `# Exercise 1 - ` 156 | 1. Uncomment that line `- ./data:/data` 157 | 158 | The relevant lines read: 159 | 160 | ``` {.yml linenums="43"} 161 | volumes: 162 | - ./pygeoapi/pygeoapi.config.yml:/pygeoapi/local.config.yml 163 | - ./data:/data # Exercise 1 - Ready to pull data from here 164 | - ./plugins/process/squared.py:/pygeoapi/pygeoapi/process/squared.py # Exercise 8 165 | ``` 166 | 167 | The local `./pygeoapi/pygeoapi.config.yml` file was already mounted. Now 168 | we have also mounted (made available) the entire local directory `./data`. 169 | 170 | ## Test 171 | 172 | !!! question "Start with updated configuration" 173 | 174 | 1. Start by typing `docker compose up` 175 | 1. Observe logging output 176 | 1. If no errors: open 177 | 1. Look for the Point of interest collection 178 | 1. Browse through the items of the collection 179 | 1. Check the json representation by adding ?f=json to url (or click 'json' in top right) 180 | 181 | ## Debugging configuration errors 182 | 183 | Incidentally you may run into errors, briefly discussed here: 184 | 185 | * A file cannot be found, a typo in the configuration 186 | * The format or structure of the spatial file is not fully supported 187 | * The port (5000) is already taken. Is a previous pygeoapi still running? If you change the port, consider that you also have to update the pygeoapi config file 188 | 189 | There are two parameters in the configuration file which help to address these issues. 190 | Set the logging level to `DEBUG` and indicate a path to a log file. 191 | 192 | !!! tip 193 | 194 | On Docker, set the path of the logfile to the mounted folder, so you can easily access it from your host system. You can also view the console logs from 195 | your Docker container as follows: 196 | 197 | === "Linux/Mac" 198 | 199 |
200 | ```bash 201 | docker logs --follow pygeoapi 202 | ``` 203 |
204 | 205 | === "Windows (PowerShell)" 206 | 207 |
208 | ```bash 209 | docker logs --follow pygeoapi 210 | ``` 211 |
212 | 213 | !!! tip 214 | 215 | Errors related to file paths typically happen on initial setup. However, they may also happen at unexpected moments, resulting in a broken service. 216 | Products such as [GeoHealthCheck](https://geohealthcheck.org) aim to monitor, detect and notify service health and availability. The OGC API - Features 217 | tests in GeoHealthCheck poll the availability of the service at intervals. Consult the [GeoHealthCheck documentation](https://docs.geohealthcheck.org) for more 218 | information. 219 | -------------------------------------------------------------------------------- /workshop/content/docs/publishing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Publishing data 3 | --- 4 | 5 | # Publishing Data 6 | 7 | !!! note 8 | 9 | Ensure that you have pygeoapi setup and can navigate the default configuration and service running at . 10 | 11 | In this section, you will learn how to publish different types of geospatial data and metadata 12 | through the following exercises: 13 | 14 | - [Exercise 1 - Your first dataset](first.md) 15 | - [Exercise 2 - Vector data via OGC API - Features](ogcapi-features.md) 16 | - [Exercise 3 - Raster data via OGC API - Coverages](ogcapi-coverages.md) 17 | - [Exercise 4 - Tiles of geospatial data via OGC API - Tiles](ogcapi-tiles.md) 18 | - [Exercise 5 - Maps of geospatial data via OGC API - Maps](ogcapi-maps.md) 19 | - [Exercise 6 - Metadata via OGC API - Records](ogcapi-records.md) 20 | - [Exercise 7 - Environmental data via OGC API - Environmental Data Retrieval](ogcapi-edr.md) 21 | - [Exercise 8 - Functions via OGC API - Processes](ogcapi-processes.md) 22 | - [Exercise 9 - pygeoapi as a bridge to other services](../advanced/bridges.md) 23 | -------------------------------------------------------------------------------- /workshop/content/docs/publishing/ogcapi-coverages.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exercise 3 - Raster data via OGC API - Coverages 3 | --- 4 | 5 | # Exercise 3 - Raster data via OGC API - Coverages 6 | 7 | [OGC API - Coverages](https://ogcapi.ogc.org/coverages) provides a Web API to access raster 8 | data (grids, remote sensing data, multidimensional data cubes): 9 | 10 | * [OGC API - Coverages](https://docs.ogc.org/DRAFTS/19-087.html) (**draft**) 11 | 12 | ## pygeoapi support 13 | 14 | pygeoapi supports the OGC API - Coverages draft specification, with [rasterio](https://rasterio.readthedocs.io) and [xarray](https://docs.xarray.dev) as core backends 15 | as well as [CoverageJSON](https://covjson.org) and native output. 16 | 17 | !!! note 18 | 19 | See [the official documentation](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-coverages.html) for more information on supported raster backends 20 | 21 | 22 | ## Publish a raster dataset 23 | 24 | In the previous exercises we have demonstrated the steps involved to publish vector data and update the pygeoapi configuration. In this section we are going to publish a raster file in GeoTIFF format, from a [rasterio](https://rasterio.readthedocs.io) source provider. 25 | 26 | 27 | !!! question "Update the pygeoapi configuration" 28 | 29 | Open the pygeoapi configuration file in a text editor. Add a new dataset section as follows: 30 | 31 | ``` {.yaml linenums="1"} 32 | tartu-ntl: 33 | type: collection 34 | title: NASA Blue Marble Night Lights Data sample over Estonia 35 | description: NASA Blue Marble Night Lights Data sample over Estonia 36 | keywords: 37 | - Blue Marble 38 | - Night Lights 39 | - NTL 40 | links: 41 | - type: text/html 42 | rel: about 43 | title: NASA Blue Marble Night Lights Data 44 | href: https://appliedsciences.nasa.gov/get-involved/training/english/arset-introduction-nasas-black-marble-night-lights-data 45 | hreflang: en 46 | extents: 47 | spatial: 48 | bbox: [26.6264,58.32569,26.82632,58.433989] 49 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 50 | providers: 51 | - type: coverage 52 | name: rasterio 53 | data: /data/tartu/estonia_light.tif # place correct path here 54 | format: 55 | name: GTiff 56 | mimetype: application/tiff 57 | ``` 58 | 59 | !!! tip 60 | 61 | The rasterio provider `format.name` directive **requires** a valid [GDAL raster driver short name](https://gdal.org/drivers/raster/index.html) 62 | 63 | Save the configuration and restart Docker Compose. Navigate to to evaluate whether the new dataset has been published. 64 | 65 | ## Client access 66 | 67 | ### GDAL/OGR 68 | 69 | [GDAL/OGR](https://gdal.org) provides support for [OGC API - Coverages](https://gdal.org/drivers/raster/ogcapi.html). This means you can use `gdalinfo` to query and convert data from OGC API - Coverages endpoints just like any other raster data source. This also means you can make connections to OGC API - Coverages endpoints from any software which has an interface to GDAL, such as MapServer, GeoServer, Manifold, FME, ArcGIS, etc. 70 | 71 | 72 | !!! question "Use GDAL to interact with OGC API - Coverages" 73 | 74 | - Verify you have a recent GDAL installed, else use GDAL from OSGeoLive 75 | - Run `gdalinfo` on the command line to verify a connection to OGC API - Coverages: 76 | 77 | === "Linux/Mac" 78 | 79 |
80 | ``` 81 | gdalinfo OGCAPI:https://maps.ecere.com/ogcapi/collections/SRTM_ViewFinderPanorama 82 | ``` 83 |
84 | 85 | === "Windows (PowerShell)" 86 | 87 |
88 | ``` 89 | gdalinfo OGCAPI:https://maps.ecere.com/ogcapi/collections/SRTM_ViewFinderPanorama 90 | ``` 91 |
92 | 93 | 94 | ### OWSLib 95 | 96 | [OWSLib](https://owslib.readthedocs.io) is a Python library to interact with OGC Web Services and supports a number of OGC APIs including OGC API - Coverages. 97 | 98 | !!! question "Interact with OGC API - Coverages via OWSLib" 99 | 100 | If you do not have Python installed, consider running this exercise in a Docker container. See the [Setup Chapter](../setup.md#using-docker-for-python-clients). 101 | 102 | === "Linux/Mac" 103 |
104 | ```bash 105 | pip3 install owslib 106 | ``` 107 |
108 | 109 | === "Windows (PowerShell)" 110 |
111 | ```bash 112 | pip3 install owslib 113 | ``` 114 |
115 | 116 | Then start a Python console session with: `python3` (stop the session by typing `exit()`). 117 | 118 | === "Linux/Mac" 119 | 120 |
121 | ```python 122 | >>> from owslib.ogcapi.coverages import Coverages 123 | >>> SERVICE_URL = 'https://demo.pygeoapi.io/master/' 124 | >>> w = Coverages(SERVICE_URL) 125 | >>> w.url 126 | 'https://demo.pygeoapi.io/master/' 127 | >>> gdps = w.collection('gdps-temperature') 128 | >>> gdps['id'] 129 | 'gdps-temperature' 130 | >>> gdps['title'] 131 | 'Global Deterministic Prediction System sample' 132 | >>> gdps['description'] 133 | 'Global Deterministic Prediction System sample' 134 | >>> schema = w.collection_schema('gdps-temperature') 135 | >>> len(schema['field']) 136 | 1 137 | >>> schema['properties']['1']['title'] 138 | 'Temperature [C]' 139 | >>> schema['properties']['1']['x-ogc-unit'] 140 | '[C]' 141 | >>> schema['properties']['1']['type'] 142 | 'number' 143 | ``` 144 |
145 | 146 | === "Windows (PowerShell)" 147 | 148 |
149 | ```python 150 | >>> from owslib.ogcapi.coverages import Coverages 151 | >>> SERVICE_URL = 'https://demo.pygeoapi.io/master/' 152 | >>> w = Coverages(SERVICE_URL) 153 | >>> w.url 154 | 'https://demo.pygeoapi.io/master/' 155 | >>> gdps = w.collection('gdps-temperature') 156 | >>> gdps['id'] 157 | 'gdps-temperature' 158 | >>> gdps['title'] 159 | 'Global Deterministic Prediction System sample' 160 | >>> gdps['description'] 161 | 'Global Deterministic Prediction System sample' 162 | >>> schema = w.collection_schema('gdps-temperature') 163 | >>> len(schema['field']) 164 | 1 165 | >>> schema['properties']['1']['title'] 166 | 'Temperature [C]' 167 | >>> schema['properties']['1']['x-ogc-unit'] 168 | '[C]' 169 | >>> schema['properties']['1']['type'] 170 | 'number' 171 | ``` 172 |
173 | 174 | !!! note 175 | 176 | See the official [OWSLib documentation](https://owslib.readthedocs.io/en/latest/usage.html#ogc-api) for more examples. 177 | 178 | # Summary 179 | 180 | Congratulations! You are now able to publish raster data to pygeoapi. 181 | -------------------------------------------------------------------------------- /workshop/content/docs/publishing/ogcapi-edr.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exercise 7 - Environmental data via OGC API - Environmental Data Retrieval 3 | --- 4 | 5 | # Exercise 7 - Environmental data via OGC API - Environmental Data Retrieval 6 | 7 | [OGC API - Environmental Data Retrieval](https://ogcapi.ogc.org/edr) provides a Web API to access 8 | environmental data using well defined query patterns: 9 | 10 | * [OGC API - Environmental Data Retrieval Standard](https://docs.ogc.org/is/19-086r4/19-086r4.html) 11 | 12 | OGC API - Environmental Data Retrieval uses OGC API - Features as a building block, thus enabling 13 | streamlined integration for clients and users. EDR can be considered a convenience API which does 14 | not require in depth knowledge about the underlying data store/model. 15 | 16 | ## pygeoapi support 17 | 18 | pygeoapi supports the OGC API - Environmental Data Retrieval specification by leveraging both feature 19 | and coverage provider plugins. 20 | 21 | !!! note 22 | 23 | See [the official documentation](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-edr.html) for more information on supported EDR backends 24 | 25 | 26 | ## Publish environmental data in pygeoapi 27 | 28 | Let's try publishing some ICOADS data via the EDR xarray plugin. The sample ICOADS data can be found in `workshop/exercises/data/coads_sst.nc`: 29 | 30 | 31 | !!! question "Update the pygeoapi configuration" 32 | 33 | Open the pygeoapi configuration file in a text editor. Add a new dataset section as follows: 34 | 35 | ``` {.yaml linenums="1"} 36 | icoads-sst: 37 | type: collection 38 | title: International Comprehensive Ocean-Atmosphere Data Set (ICOADS) 39 | description: International Comprehensive Ocean-Atmosphere Data Set (ICOADS) 40 | keywords: 41 | - icoads 42 | - sst 43 | - air temperature 44 | extents: 45 | spatial: 46 | bbox: [-180,-90,180,90] 47 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 48 | temporal: 49 | begin: 2000-01-16T06:00:00Z 50 | end: 2000-12-16T06:00:00Z 51 | links: 52 | - type: text/html 53 | rel: canonical 54 | title: information 55 | href: https://psl.noaa.gov/data/gridded/data.coads.1deg.html 56 | hreflang: en-US 57 | providers: 58 | - type: edr 59 | name: xarray-edr 60 | data: /data/coads_sst.nc 61 | format: 62 | name: NetCDF 63 | mimetype: application/x-netcdf 64 | ``` 65 | 66 | Save the configuration and restart Docker Compose. Navigate to to evaluate whether the new dataset has been published. 67 | 68 | At first glance, the `icoads-sst` collection appears as a normal OGC API - Coverages collection. Look a bit closer at the collection description, and notice 69 | that there is a `parameter_names' key that describes EDR parameter names for the collection queries. 70 | 71 | ### OWSLib - Advanced 72 | 73 | [OWSLib](https://owslib.readthedocs.io) is a Python library to interact with OGC Web Services and supports a number of OGC APIs including OGC API - Environmental Data Retrieval. 74 | 75 | !!! question "Interact with OGC API - Environmental Data Retrieval via OWSLib" 76 | 77 | If you do not have Python installed, consider running this exercise in a Docker container. See the [Setup Chapter](../setup.md#using-docker-for-python-clients). 78 | 79 | === "Linux/Mac" 80 | 81 |
82 | ```bash 83 | pip3 install owslib 84 | ``` 85 |
86 | 87 | === "Windows (PowerShell)" 88 | 89 |
90 | ```bash 91 | pip3 install owslib 92 | ``` 93 |
94 | 95 | Then start a Python console session with `python3` (stop the session by typing `exit()`). 96 | 97 | === "Linux/Mac" 98 | 99 |
100 | ```python 101 | >>> from owslib.ogcapi.edr import EnvironmentalDataRetrieval 102 | >>> w = EnvironmentalDataRetrieval('https://demo.pygeoapi.io/master') 103 | >>> w.url 104 | 'https://demo.pygeoapi.io/master' 105 | >>> api = w.api() # OpenAPI document 106 | >>> collections = w.collections() 107 | >>> len(collections['collections']) 108 | 13 109 | >>> icoads_sst = w.collection('icoads-sst') 110 | >>> icoads_sst['parameter-names'].keys() 111 | dict_keys(['SST', 'AIRT', 'UWND', 'VWND']) 112 | >>> data = w.query_data('icoads_sst', 'position', coords='POINT(-75 45)', parameter_names=['SST', 'AIRT']) 113 | >>> data # CoverageJSON data 114 | ``` 115 |
116 | 117 | === "Windows (PowerShell)" 118 | 119 |
120 | ```python 121 | >>> from owslib.ogcapi.edr import EnvironmentalDataRetrieval 122 | >>> w = EnvironmentalDataRetrieval('https://demo.pygeoapi.io/master') 123 | >>> w.url 124 | 'https://demo.pygeoapi.io/master' 125 | >>> api = w.api() # OpenAPI document 126 | >>> collections = w.collections() 127 | >>> len(collections['collections']) 128 | 13 129 | >>> icoads_sst = w.collection('icoads-sst') 130 | >>> icoads_sst['parameter-names'].keys() 131 | dict_keys(['SST', 'AIRT', 'UWND', 'VWND']) 132 | >>> data = w.query_data('icoads_sst', 'position', coords='POINT(-75 45)', parameter_names=['SST', 'AIRT']) 133 | >>> data # CoverageJSON data 134 | ``` 135 |
136 | 137 | !!! note 138 | 139 | See the official [OWSLib documentation](https://owslib.readthedocs.io/en/latest/usage.html#ogc-api) for more examples. 140 | 141 | # Summary 142 | 143 | Congratulations! You are now able to publish environmental data to pygeoapi. 144 | -------------------------------------------------------------------------------- /workshop/content/docs/publishing/ogcapi-maps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exercise 5 - Maps of geospatial data via OGC API - Maps 3 | --- 4 | 5 | # Exercise 5 - Maps of geospatial data via OGC API - Maps 6 | 7 | [OGC API - Maps](https://ogcapi.ogc.org/maps) provides a Web API to access 8 | any geospatial data as a georeferenced map image. 9 | 10 | * [OGC API - Maps](https://docs.ogc.org/DRAFTS/20-058.html) 11 | 12 | ## pygeoapi support 13 | 14 | pygeoapi supports the OGC API - Maps specification, using [MapServer MapScript](https://www.mapserver.org/mapscript) and a WMS facade as core backends. 15 | 16 | !!! note 17 | 18 | See [the official documentation](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-maps.html) for more information on supported map backends 19 | 20 | ## Publish a raster dataset 21 | 22 | In this section we'll be exposing a Geopackage file available at `workshop/exercises/data/airport.gpkg` location using [MapServer MapScript](https://www.mapserver.org/mapscript). This data can be consumed with various clients which are compliant with OGC APIs - Maps. List of few such clients can be found [here](https://github.com/opengeospatial/ogcapi-maps/blob/master/implementations.adoc#clients). Here we can also pass style in *.sld* format. Which can be generated on [Geoserver](https://docs.geoserver.org/stable/en/user/styling/index.html), [QGIS](https://www.qgistutorials.com/en/docs/3/basic_vector_styling.html), etc. 23 | 24 | !!! question "Interact with OGC API - Maps via MapScript" 25 | 26 | Open the pygeoapi configuration file in a text editor. Find the line `# START - EXERCISE 5 - Maps`. 27 | 28 | Uncomment section related to #airports. 29 | 30 | ```{.yaml linenums="1"} 31 | airports: 32 | type: collection 33 | title: airports of the world 34 | description: Point data representing airports around the world with various metadata such as name, Code, etc. 35 | keywords: 36 | - airports 37 | - natural earth 38 | links: 39 | - type: text/html 40 | rel: canonical 41 | title: information 42 | href: https://www.naturalearthdata.com/downloads/10m-cultural-vectors/airports/ 43 | hreflang: en-US 44 | extents: 45 | spatial: 46 | bbox: [-180,-90,180,90] 47 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 48 | temporal: 49 | begin: 50 | end: null # or empty 51 | providers: 52 | - type: map 53 | name: MapScript 54 | data: /data/airport.gpkg 55 | options: 56 | type: MS_LAYER_POINT 57 | layer: airport 58 | style: /data/airport.sld 59 | format: 60 | name: png 61 | mimetype: image/png 62 | ``` 63 | 64 | !!! note 65 | 66 | See [the official documentation](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-maps.html) for more information on supported map backends 67 | 68 | ## pygeoapi as a WMS proxy 69 | 70 | You can check the "pygeoapi as a Bridge to Other Services" section to learn how to [publish WMS as OGC API - Maps](../advanced/bridges.md#publishing-wms-as-ogc-api-maps). 71 | 72 | ## Client access 73 | 74 | ### QGIS 75 | 76 | QGIS added support for API's providing rendered image layers via its raster support. 77 | 78 | !!! question "Add OGC API - Maps layer to QGIS" 79 | 80 | - Install a recent version of QGIS (>3.28). 81 | - Open the `Add raster layer panel`. 82 | - Select `OGCAPI` for Source type. 83 | - Add the local endpoint as source `http://localhost:5000/collections/airports`. 84 | - Select `PNG` as image format. 85 | - Finally add the layer to the map. 86 | 87 | ### OWSLib 88 | 89 | [OWSLib](https://owslib.readthedocs.io) is a Python library to interact with OGC Web Services and supports a number of OGC APIs including OGC API - Maps. 90 | 91 | !!! question "Interact with OGC API - Maps via OWSLib" 92 | 93 | If you do not have Python installed, consider running this exercise in a Docker container. See the [Setup Chapter](../setup.md#using-docker-for-python-clients). 94 | 95 | === "Linux/Mac" 96 | 97 |
98 | ```bash 99 | pip3 install owslib 100 | ``` 101 |
102 | 103 | === "Windows (PowerShell)" 104 | 105 |
106 | ```bash 107 | pip3 install owslib 108 | ``` 109 |
110 | 111 | Now running in Python: 112 | 113 | === "Linux/Mac" 114 | 115 |
116 | ```python 117 | >>> from owslib.ogcapi.maps import Maps 118 | >>> m = Maps('http://localhost:5000') 119 | >>> data = m.map('wms-facade-demo', width=1200, height=800, transparent=False) 120 | >>> with open("output.png", "wb") as fh: 121 | ... fh.write(data.getbuffer()) 122 | ``` 123 |
124 | 125 | === "Windows (PowerShell)" 126 | 127 |
128 | ```python 129 | >>> from owslib.ogcapi.maps import Maps 130 | >>> m = Maps('http://localhost:5000') 131 | >>> data = m.map('wms-facade-demo', width=1200, height=800, transparent=False) 132 | >>> with open("output.png", "wb") as fh: 133 | ... fh.write(data.getbuffer()) 134 | ``` 135 |
136 | 137 | !!! note 138 | 139 | See the official [OWSLib documentation](https://owslib.readthedocs.io/en/latest/usage.html#ogc-api) for more examples. 140 | 141 | # Summary 142 | 143 | Congratulations! You are now able to serve an OGC WMS via pygeoapi and OGC API - Maps. 144 | -------------------------------------------------------------------------------- /workshop/content/docs/publishing/ogcapi-processes.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exercise 8 - Functions via OGC API - Processes 3 | --- 4 | 5 | # Exercise 8 - Functions via OGC API - Processes 6 | 7 | [OGC API - Processes](https://ogcapi.ogc.org/processes) supports the wrapping of computational tasks into 8 | executable processes that can be offered by a server through a Web API and be invoked by a client application. 9 | 10 | * [OGC API - Processes: Part 1: Core](https://docs.ogc.org/is/18-062r2/18-062r2.html) 11 | 12 | OGC API - Processes uses OGC API - Common as a building block, thus enabling streamlined deployment and integration 13 | for clients and users. 14 | 15 | ## pygeoapi support 16 | 17 | pygeoapi supports the OGC API - Processes specification, with the ability to publish Python code (no matter how 18 | simple or complex) as an OGC API Process definition. pygeoapi also support synchronous or asynchronous processing, 19 | with the ability to store and retrive the status/results of 'jobs'. 20 | 21 | !!! note 22 | 23 | See [the official documentation](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-processes.html) for more information on publishing processes in pygeoapi 24 | 25 | 26 | ## Publishing Python code as a process in pygeoapi 27 | 28 | With pygeoapi we can setup OGC API - Processes using Python code that implements the pygeoapi `BaseProcessor`, which is a core pygeoapi 29 | abstract base class. In this exercise we will implemented a "squared" function as a process using the sample Python code in 30 | `workshop/exercises/plugins/process/squared.py`. The process is already defined to be part of the pygeoapi environment and configuration. 31 | 32 | !!! question "Update the pygeoapi configuration" 33 | 34 | Open the pygeoapi configuration file in a text editor. Add a new process section as follows: 35 | 36 | ``` {.yaml linenums="1"} 37 | squared: 38 | type: process 39 | processor: 40 | name: pygeoapi.process.squared.SquaredProcessor 41 | ``` 42 | 43 | !!! question "Update Python code" 44 | 45 | Open the Python code at `workshop/exercises/plugins/process/squared.py`. Find the `execute` function and update the Python 46 | code to calculate the input value squared. 47 | 48 | 49 | Save the configuration and restart Docker Compose. Navigate to to evaluate whether the new process has 50 | been published. Inspect the detailed process metadata by navigating to to inspect how the process 51 | metadata defined in the Python code/file is made available in JSON.. 52 | 53 | ## Client access 54 | 55 | ### Swagger 56 | 57 | The easiest way to test the new process is by using pygeoapi's built in Swagger interface. Navigate to and try out 58 | the process in the Swagger UI. 59 | 60 | ![Squared function process execution](../assets/images/oaproc-squared-1.png){ width=120% } 61 | 62 | ![Squared function process execution](../assets/images/oaproc-squared-2.png){ width=120% } 63 | 64 | ![Squared function process execution](../assets/images/oaproc-squared-3.png){ width=120% } 65 | 66 | 67 | # Summary 68 | 69 | Congratulations! You are now able to publish Python code as a process to pygeoapi. 70 | -------------------------------------------------------------------------------- /workshop/content/docs/publishing/ogcapi-records.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Exercise 6 - Metadata via OGC API - Records 3 | --- 4 | 5 | # Exercise 6 - Metadata via OGC API - Records 6 | 7 | [OGC API - Records](https://ogcapi.ogc.org/records) provides a Web API with the capability to create, modify, 8 | and query metadata on the Web: 9 | 10 | * [OGC API - Records: Part 1: Core](https://docs.ogc.org/DRAFTS/20-004.html) (**draft**) 11 | 12 | OGC API - Records uses OGC API - Features as a building block, thus enabling streamlined deployment and integration 13 | for clients and users. 14 | 15 | ## pygeoapi support 16 | 17 | pygeoapi supports the OGC API - Records draft specification, using Elasticsearch and TinyDB [rasterio](https://rasterio.readthedocs.io) as core backends. 18 | 19 | !!! note 20 | 21 | See [the official documentation](https://docs.pygeoapi.io/en/latest/data-publishing/ogcapi-records.html) for more information on supported catalogue/metadata backends 22 | 23 | 24 | ## Publish metadata records in pygeoapi 25 | 26 | With pygeoapi we can setup OGC API - Records using any supported data provider. In this exercise we will use the [TinyDB](https://tinydb.readthedocs.io/en/latest/index.html) 27 | Catalogue backend. We will use the sample catalogue in `workshop/exercises/data/tartu/metadata/catalogue.tinydb`. 28 | 29 | !!! question "Update the pygeoapi configuration" 30 | 31 | Open the pygeoapi configuration file in a text editor. Add a new dataset section as follows: 32 | 33 | ``` {.yaml linenums="1"} 34 | example_catalogue: 35 | type: collection 36 | title: FOSS4G Europe Estonia national catalogue 37 | description: FOSS4G Europe Estonia national catalogue 38 | keywords: 39 | - estonia 40 | - catalogue 41 | - FOSS4G Europe 42 | links: 43 | - type: text/html 44 | rel: canonical 45 | title: information 46 | href: https://metadata.geoportaal.ee 47 | hreflang: en-US 48 | extents: 49 | spatial: 50 | bbox: [23.3397953631, 57.4745283067, 28.1316992531, 59.6110903998] 51 | crs: http://www.opengis.net/def/crs/OGC/1.3/CRS84 52 | providers: 53 | - type: record 54 | name: TinyDBCatalogue 55 | data: /data/tartu/metadata/catalogue.tinydb 56 | id_field: externalId 57 | time_field: recordCreated 58 | title_field: title 59 | ``` 60 | 61 | Save the configuration and restart Docker Compose. Navigate to to evaluate whether the new dataset has been published. 62 | 63 | ## Metadata formats 64 | 65 | By default, pygeoapi supports and expects the OGC API - Records core record model and queryables. For additional metadata formats, you can 66 | develop your own custom pygeoapi plugin, or convert your metadata to OGC API - Records core record model before adding to pygeoapi. 67 | 68 | !!! question "Install OWSLib" 69 | 70 | If you do not have Python installed, consider running this exercise in a Docker container. See the [Setup Chapter](../setup.md#using-docker-for-python-clients). 71 | 72 | === "Linux/Mac" 73 | 74 |
75 | ```bash 76 | pip3 install owslib 77 | ``` 78 |
79 | 80 | === "Windows (PowerShell)" 81 | 82 |
83 | ```bash 84 | pip3 install owslib 85 | ``` 86 |
87 | 88 | ### Sample ISO 19139 to TinyDBCatalogue loader 89 | 90 | It is possible to load more example ISO19139 metadata in a TinyDB database with [the following script](https://github.com/geopython/pygeoapi/blob/master/tests/load_tinydb_records.py) ([raw](https://raw.githubusercontent.com/geopython/pygeoapi/master/tests/load_tinydb_records.py)): 91 | 92 | === "Linux/Mac" 93 | 94 |
95 | ```bash 96 | cd workshop/exercises/data/tartu/metadata 97 | curl -O https://raw.githubusercontent.com/geopython/pygeoapi/master/tests/load_tinydb_records.py 98 | python3 load_tinydb_records.py xml catalogue.tinydb 99 | ``` 100 |
101 | 102 | === "Windows (PowerShell)" 103 | 104 |
105 | ```bash 106 | cd workshop/exercises/data/tartu/metadata 107 | curl https://raw.githubusercontent.com/geopython/pygeoapi/master/tests/load_tinydb_records.py 108 | python3 load_tinydb_records.py xml catalogue.tinydb 109 | ``` 110 |
111 | 112 | If you do not have curl installed, copy the URL above to your web browser and save locally. 113 | 114 | If you do not have Python installed, you can the loader by using the OWSLib Docker container. See the [Setup Chapter](../setup.md#using-docker-for-python-clients). 115 | 116 | !!! example "Using the OWSLib Docker container to load metadata" 117 | 118 | === "Linux/Mac" 119 | 120 |
121 | ```bash 122 | cd workshop/exercises 123 | docker run -it --rm --network=host --name owslib -v $(pwd)/data:/data python:3.10-slim /bin/bash 124 | pip3 install owslib 125 | apt-get update -y && apt-get install curl -y 126 | curl -O https://raw.githubusercontent.com/geopython/pygeoapi/master/tests/load_tinydb_records.py 127 | python3 load_tinydb_records.py /data/tartu/metadata/xml /data/tartu/metadata/catalogue.tinydb 128 | ``` 129 |
130 | 131 | === "Windows (PowerShell)" 132 | 133 |
134 | ```bash 135 | cd workshop/exercises 136 | docker run -it --rm --network=host --name owslib -v ${pwd}/data:/data python:3.10-slim /bin/bash 137 | pip3 install owslib 138 | apt-get update -y && apt-get install curl -y 139 | curl -O https://raw.githubusercontent.com/geopython/pygeoapi/master/tests/load_tinydb_records.py 140 | python3 load_tinydb_records.py /data/tartu/metadata/xml /data/tartu/metadata/catalogue.tinydb 141 | ``` 142 |
143 | 144 | Navigate to to evaluate whether the new metadata has been published 145 | to the collection. 146 | 147 | !!! tip pygeometa 148 | 149 | [pygeometa](https://geopython.github.io/pygeometa) is a Python package to generate metadata for geospatial 150 | datasets. pygeometa allows for managing metadata in simple YAML "metadata control files (MCF), and supports 151 | import, export as well as transformations for many geospatial metadata formats. OGC API - Records metadata 152 | can be produced using pygeometa, either from MCF files or transforming from other formats. 153 | 154 | Install and run pygeometa per below to get an idea of the various commands and functionality (as well, 155 | consult the [tutorial](https://geopython.github.io/pygeometa/tutorial)). 156 | 157 | === "Linux/Mac" 158 | 159 |
160 | ```bash 161 | pip3 install pygeometa 162 | pygeometa --help 163 | ``` 164 |
165 | 166 | === "Windows (PowerShell)" 167 | 168 |
169 | ```bash 170 | pip3 install pygeometa 171 | pygeometa --help 172 | ``` 173 |
174 | 175 | ## pygeoapi as a CSW proxy 176 | 177 | You can check the "pygeoapi as a Bridge to Other Services" section to learn how to [publish CSW as OGC API - Records](../advanced/bridges.md#publishing-csw-as-ogc-api-records). 178 | 179 | ## Client access 180 | 181 | ### QGIS 182 | 183 | QGIS supports OGC API - Records via the [MetaSearch plugin](https://docs.qgis.org/latest/en/docs/user_manual/plugins/core_plugins/plugins_metasearch.html). MetaSearch originally focused on Catalogue Service for the Web (OGC:CSW) only, but has been extended to OGC API - Records. MetaSearch is a default plugin in QGIS and requires no further installation. 184 | 185 | !!! question "Query OGC API - Records from QGIS" 186 | 187 | Follow these steps to connect to a service and query datasets: 188 | 189 | - Locate the MetaSearch plugin in the Web menu or on the Toolbar ![MetaSearch icon](https://docs.qgis.org/latest/en/_images/MetaSearch.png "MetaSearch icon"). The main search panel will appear with the default MetaSearch catalogue list already populated. 190 | 191 | ![Pre-populated catalogues](../assets/images/prepopulated-catalogues.png){ width=50% } 192 | 193 | - open the `Services` tab, to find the `New` button to create a new connection 194 | - add a connection to `https://demo.pygeoapi.io/master` 195 | - click `Service Info` to get information about the service 196 | - return to the Search tab 197 | - select the connection you have just created 198 | - type a search term and click `search` 199 | - notice that when you select a search result, a red footprint is drawn on the map highlighting the location of the dataset 200 | 201 | ![Search results](../assets/images/search-results.png){ width=50% } 202 | 203 | [OWSLib](https://owslib.readthedocs.io) is a Python library to interact with OGC Web Services and supports a number of OGC APIs including OGC API - Records. 204 | 205 | !!! question "Interact with OGC API - Records via OWSLib" 206 | 207 | If you do not have Python installed, consider running this exercise in a Docker container. See the [Setup Chapter](../setup.md#using-docker-for-python-clients). 208 | 209 | === "Linux/Mac" 210 | 211 |
212 | ```bash 213 | pip3 install owslib 214 | ``` 215 |
216 | 217 | === "Windows (PowerShell)" 218 | 219 |
220 | ```bash 221 | pip3 install owslib 222 | ``` 223 |
224 | 225 | Then start a Python console session with `python3` (stop the session by typing `exit()`). 226 | 227 | === "Linux/Mac" 228 | 229 |
230 | ```python 231 | >>> from owslib.ogcapi.records import Records 232 | >>> SERVICE_URL = 'https://demo.pygeoapi.io/master/' 233 | >>> w = Records(SERVICE_URL) 234 | >>> w.url 235 | 'https://demo.pygeoapi.io/master' 236 | >>> dutch_metacat = w.collection('dutch-metadata') 237 | >>> dutch_metacat['id'] 238 | 'dutch-metadata' 239 | >>> dutch_metacat['title'] 240 | 'Sample metadata records from Dutch Nationaal georegister' 241 | >>> dutch_metacat['description'] 242 | 'Sample metadata records from Dutch Nationaal georegister' 243 | >>> dutch_metacat_query = w.collection_items('dutch-metadata', limit=1) 244 | >>> dutch_metacat_query['numberMatched'] 245 | 198 246 | >>> dutch_metacat_query['numberReturned'] 247 | 1 248 | >>> dutch_metacat_query = w.collection_items('dutch-metadata', q='Wegpanorama') 249 | >>> dutch_metacat_query['numberMatched'] 250 | 2 251 | ``` 252 |
253 | 254 | === "Windows (PowerShell)" 255 | 256 |
257 | ```python 258 | >>> from owslib.ogcapi.records import Records 259 | >>> SERVICE_URL = 'https://demo.pygeoapi.io/master/' 260 | >>> w = Records(SERVICE_URL) 261 | >>> w.url 262 | 'https://demo.pygeoapi.io/master' 263 | >>> dutch_metacat = w.collection('dutch-metadata') 264 | >>> dutch_metacat['id'] 265 | 'dutch-metadata' 266 | >>> dutch_metacat['title'] 267 | 'Sample metadata records from Dutch Nationaal georegister' 268 | >>> dutch_metacat['description'] 269 | 'Sample metadata records from Dutch Nationaal georegister' 270 | >>> dutch_metacat_query = w.collection_items('dutch-metadata', limit=1) 271 | >>> dutch_metacat_query['numberMatched'] 272 | 198 273 | >>> dutch_metacat_query['numberReturned'] 274 | 1 275 | >>> dutch_metacat_query = w.collection_items('dutch-metadata', q='Wegpanorama') 276 | >>> dutch_metacat_query['numberMatched'] 277 | 2 278 | ``` 279 |
280 | 281 | !!! note 282 | 283 | See the official [OWSLib documentation](https://owslib.readthedocs.io/en/latest/usage.html#ogc-api) for more examples. 284 | 285 | 286 | # Summary 287 | 288 | Congratulations! You are now able to publish metadata to pygeoapi. 289 | -------------------------------------------------------------------------------- /workshop/content/docs/standards.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Standards 3 | --- 4 | 5 | # Overview 6 | 7 | This section provides a high level overview of standards support in pygeoapi. 8 | 9 | # Standards 10 | 11 | Open standards are core to pygeoapi, and allow for broad interoperability and plug and play capability. pygeoapi supports 12 | a number of open standards (both formal standards and defacto or community driven). 13 | 14 | ## API standards 15 | 16 | ### OGC API 17 | 18 | pygeoapi implements the [OGC API](https://ogcapi.ogc.org) suite of standards from the [Open Geospatial Consortium](https://www.ogc.org/) (OGC). From the OGC API website: 19 | 20 | !!! Citation 21 | 22 | The OGC API family of standards are being developed to make it easy for anyone to provide geospatial data to the web. These standards build upon the legacy of the OGC Web Service standards (WMS, WFS, WCS, WPS, etc.), but define resource-centric APIs that take advantage of modern web development practices. This web page provides information on these standards in a consolidated location. 23 | 24 | These standards are being constructed as "building blocks" that can be used to assemble novel APIs for web access to geospatial content. The building blocks are defined not only by the requirements of the specific standards, but also through interoperability prototyping and testing in OGC's Innovation Program. 25 | 26 | !!! Tip 27 | 28 | You can learn more about OGC APIs in the [OGC API workshop](https://ogcapi-workshop.ogc.org) 29 | 30 | #### OGC API - Common 31 | 32 | [OGC API - Common](https://ogcapi.ogc.org/common/) is a common framework used in all OGC API's. 33 | OGC API - Common provides the following functionality: 34 | 35 | - based on [OpenAPI 3.0](https://spec.openapis.org/oas/latest.html) 36 | - HTML and JSON as the dominant encodings, alternative encodings are possible 37 | - shared endpoints such as: 38 | - `/` (landing page) 39 | - `/conformance` 40 | - `/openapi` 41 | - `/collections` 42 | - `/collections/foo` 43 | - aspects such as pagination, links between resources, basic filtering, query parameters (`bbox`, `datetime`, etc.) 44 | - shared models (exceptions, links, etc.) 45 | 46 | OGC API - Common allows for specification developers to focus on the key functionality of a given API (i.e. data access, etc.) 47 | while using common constructs. This harmonizes OGC API standards and enables deeper integration with less code. This also 48 | allows for OGC API client software to be more streamlined. 49 | 50 | The `/conformance` endpoint indicates which standards and extensions are supported by a deployment of OGC API. 51 | 52 | #### OGC API building blocks 53 | 54 | The OGC API approach allows for modularity and "profiling" of APIs depending on your requirements. This means you 55 | can mix and match OGC APIs together. 56 | 57 | ![OGC API building blocks](assets/images/ogc-api-building-blocks.png) 58 | 59 | You can read more about this topic in the [building blocks website](https://blocks.ogc.org/). 60 | 61 | #### More OGC APIs 62 | 63 | The OGC API effort is rapidly evolving. Numerous OGC API standards are in development, and will be implemented in 64 | pygeoapi over time: 65 | 66 | - [Routes](https://ogcapi.ogc.org/routes) provides access to routing data 67 | - [Styles](https://ogcapi.ogc.org/styles) defines a Web API that enables map servers, clients as well as visual style editors, to manage and fetch styles 68 | - [3D GeoVolumes](https://ogcapi.ogc.org/geovolumes) facilitates efficient discovery of and access to 3D content in multiple formats based on a space-centric perspective 69 | - [Moving Features](https://ogcapi.ogc.org/movingfeatures) defines an API that provides access to data representing features that move as rigid bodies 70 | - [Joins](https://ogcapi.ogc.org/joins) supports the joining of data, from multiple sources, with feature collections or directly with other input files 71 | - [Discrete Global Grid System](https://ogcapi.ogc.org/dggs) enables applications to organise and access data arranged according to a Discrete Global Grid System (DGGS) 72 | 73 | ![Approved and candidate OGC API standards](assets/images/ogcapis.png) 74 | 75 | #### OGC APIs supported by pygeoapi 76 | 77 | pygeoapi implements numerous OGC API standards and draft standards. In addition, it is compliance certified and even a Reference Implementation (RI) for some of them. Compliance certification is important to remove interoperability risks. RI are always compliance certified. From OGC [Compliance Testing Program Policies & Procedures 08-134r11](https://docs.ogc.org/pol/08-134r11.html#toc26): 78 | 79 | !!! Citation 80 | 81 | Candidate Products that pass all the tests in a Compliance Test Package, and that OGC has reviewed and certified as having passed those tests, are considered compliant with that Implementation Standard version. 82 | 83 | !!! Citation 84 | 85 | A Reference Implementation is a fully functional, licensed copy of a tested, branded software that has passes the test for an associated conformance class in a version of an Implementation Standard and that is free and publicly available for testing via a web service or download. 86 | 87 | 88 | | Standard | pygeoapi status | Included in this workshop | 89 | |----------------------------------------|-----------------|---------------------------| 90 | | OGC API - Features | Reference | ✅ | 91 | | OGC API - Coverages | Implementing | ✅ | 92 | | OGC API - Tiles | Reference | ✅ | 93 | | OGC API - Maps | Implementing | ✅ | 94 | | OGC API - Processes | Certified | ✅ | 95 | | OGC API - Records | Implementing | ✅ | 96 | | OGC API - Environmental Data Retrieval | Reference | ✅ | 97 | | SpatioTemporal Asset Catalog | Implementing | | 98 | | OGC API - Routes | Planned | | 99 | | OGC API - Styles | Planned | | 100 | | OGC API - Moving Features | Planned | | 101 | | OGC API - DGGS | Planned | | 102 | 103 | In the next section we will dive into the dedicated API's related to specific types of information. You will 104 | notice that all APIs are combined and available via a single OGC API endpoint, thanks to OGC API - Common. 105 | 106 | #### OpenAPI 107 | 108 | Core to OGC API - Common is the [OpenAPI initiative](https://www.openapis.org/about) to help 109 | describe and document an API. OpenAPI defines its structure in an OpenAPI document. 110 | OGC API - Common suggests this document to be located at `/openapi`. With pygeoapi in a browser 111 | [this URL](https://demo.pygeoapi.io/master/openapi) opens an interactive HTML page which facilitates 112 | an API query. Append `?f=json` to view the document in JSON. The OpenAPI document indicates which 113 | endpoints are available in the service, which parameters it accepts and 114 | what types of responses can be expected. The OpenAPI document is a similar concept to Capabilities 115 | XML as part of the first genration OGC Web Service standards. 116 | 117 | !!! question "OpenAPI Specification parsing in a browser" 118 | 119 | A common approach to interact with Open API's using json is to use a program like 120 | [Postman](https://www.postman.com/). Also there are browser plugins which enable to define api 121 | requests interactively within a browser. For firefox download the plugin 122 | [poster](https://pluginsaddonsextensions.com/mozilla-firefox/poster-mozilla-addon). For Chrome 123 | and Edge use [Boomerang](https://microsoftedge.microsoft.com/addons/detail/boomerang-soap-rest-c/bhmdjpobkcdcompmlhiigoidknlgghfo?hl=en-US). 124 | In Boomerang you can create individual web requests, but also load the open api specification 125 | document and interact with any of the advertised endpoints. 126 | 127 | The OpenAPI community provides various tools, such as a validator for OAS documents or 128 | [generate code](https://swagger.io/tools/swagger-codegen/) as a starting point for client development. 129 | 130 | ## Content and format standards 131 | 132 | JSON is core in pygeoapi, providing a format that is machine readable and easy to parse and handle 133 | by client software and tools. OGC API - Common provides uniform JSON formats for the various 134 | endpoints it supports. Specific OGC API standards may specify domain specific formats (for example, 135 | GeoJSON for OGC API - Features, GeoTIFF for OGC API - Coverages) depending on the data type(s). 136 | 137 | # pygeoapi specific conventions 138 | 139 | pygeoapi provides some conventions that are not put forth by OGC API standards, however facilitate 140 | some features and capabilities. 141 | 142 | ## the `f` parameter 143 | 144 | The `f` parameter can be used with any pygeoapi endpoint to specify an output format for a given 145 | API request. Examples are `f=html`, `f=json`, etc. 146 | 147 | !!! question "Using a web browser to access OGC API" 148 | 149 | Use your web browser to navigate to [demo.pygeoapi.io](https://demo.pygeoapi.io/master). A browser by default opens 150 | any OGC API in HTML (as a webpage) due to the [HTTP Accept header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept) 151 | sent by the browser (`text/html`). On the right top corner you will notice a JSON link. The link 152 | adds the parameter to the url: `f=json`, which is a mechanism of pygeoapi to override the HTTP Accept 153 | header sent by the web browser. 154 | 155 | !!! note 156 | 157 | When calling an OGC API from javascript, and the aim is to receive JSON, you can use the `?f=json` pygeoapi convention, or the content 158 | negotiation as provided by the standard; include an HTTP header `Accept: "application/json"` in your request. 159 | 160 | In jQuery for example, this is represented by the dataType property: 161 | 162 | ``` {.js linenums="1"} 163 | $.ajax({ 164 | method: "GET", 165 | url: "https://demo.pygeoapi.io/master", 166 | dataType: "json" 167 | }); 168 | ``` 169 | 170 | Or using the native fetch API: 171 | 172 | ``` {.js linenums="1"} 173 | const response = await fetch('https://demo.pygeoapi.io/master', { 174 | method: 'GET', 175 | headers: { 176 | 'Accept': 'application/json' 177 | } 178 | }); 179 | ``` 180 | 181 | ## the `skipGeometry` parameter 182 | 183 | The `skipGeometry` (`true|false`, default is `false`) parameter can be used with feature data access to facilitate 184 | downloading vector data without geometry if desired. 185 | 186 | # Summary 187 | 188 | Standards are a cornerstone of pygeoapi, and will enable you to publish your data efficiently and with a low 189 | barrier for users. Now, let's get to the action: **Publishing data**! 190 | -------------------------------------------------------------------------------- /workshop/content/mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Diving into pygeoapi 2 | site_description: "pygeoapi is an OGC Reference Implementation supporting numerous OGC API specifications. This workshop will cover publishing geospatial data to the Web using pygeoapi in support of the suite of OGC API standards." 3 | site_author: The pygeoapi community 4 | copyright: "© 2023 pygeoapi community" 5 | site_url: https://geopython.github.io/diving-into-pygeoapi 6 | repo_url: https://github.com/geopython/diving-into-pygeoapi 7 | nav: 8 | - Home: index.md 9 | - Introduction to pygeoapi: introduction.md 10 | - Workshop environment setup: setup.md 11 | - Standards: standards.md 12 | - Publishing: 13 | - publishing/index.md 14 | - Exercise 1 - Your first dataset: publishing/first.md 15 | - Exercise 2 - Vector data via OGC API - Features: publishing/ogcapi-features.md 16 | - Exercise 3 - Raster data via OGC API - Coverages: publishing/ogcapi-coverages.md 17 | - Exercise 4 - Tiles of geospatial data via OGC API - Tiles: publishing/ogcapi-tiles.md 18 | - Exercise 5 - Maps of geospatial data via OGC API - Maps: publishing/ogcapi-maps.md 19 | - Exercise 6 - Metadata via OGC API - Records: publishing/ogcapi-records.md 20 | - Exercise 7 - Environmental data via OGC API - Environmental Data Retrieval: publishing/ogcapi-edr.md 21 | - Exercise 8 - Functions via OGC API - Processes: publishing/ogcapi-processes.md 22 | - Advanced topics: 23 | - advanced/index.md 24 | - Multilingual support: advanced/i18n.md 25 | - CRS support: advanced/crs.md 26 | - UI customization and templating: advanced/ui-custom-templates.md 27 | - Using pygeoapi in downstream applications: advanced/downstream-applications.md 28 | - Search Engine Optimization (SEO): advanced/seo.md 29 | - Security and access control: advanced/security-access-control.md 30 | - Semantic Web and Linked Data: advanced/semantic-web-linked-data.md 31 | - Cloud deployment: advanced/cloud.md 32 | - INSPIRE support: advanced/inspire.md 33 | - Administration: advanced/administration.md 34 | - Exercise 9 - pygeoapi as a bridge to other services: advanced/bridges.md 35 | - Conclusion: conclusion.md 36 | 37 | use_directory_urls: true 38 | 39 | theme: 40 | name: material 41 | palette: 42 | # See https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/#color-scheme 43 | # Default is indigo (blue) 44 | scheme: pygeoapi 45 | features: 46 | - navigation.indexes 47 | favicon: assets/favicon.ico 48 | logo: assets/images/pygeoapi-icon-notrans.png 49 | 50 | plugins: 51 | - search 52 | - print-site 53 | 54 | markdown_extensions: 55 | - meta # option to add some meta tags on top, title, author, date, etc 56 | - admonition # adds the note, question, tip boxes, eg: !!! tip "my tip" 57 | - pymdownx.details # advanced collapsible panels 58 | - pymdownx.superfences # advanced features; such as line number, flow chart, python shell 59 | - pymdownx.tabbed: 60 | alternate_style: true 61 | - footnotes # notes bottom of page 62 | - attr_list # used to size images 63 | - md_in_html # used to size images 64 | 65 | extra_css: 66 | # pygeoapi primary color with light and dark variations from material.io 67 | # https://material.io/resources/color/#!/?view.left=0&view.right=1 68 | - assets/stylesheets/pygeoapi.css 69 | - assets/stylesheets/termynal.css 70 | - assets/stylesheets/custom.css 71 | 72 | extra_javascript: 73 | - assets/javascripts/termynal.js 74 | - assets/javascripts/custom.js 75 | -------------------------------------------------------------------------------- /workshop/content/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-material 3 | mkdocs-print-site-plugin 4 | -------------------------------------------------------------------------------- /workshop/exercises/README.md: -------------------------------------------------------------------------------- 1 | # Environment 2 | 3 | ## Docker compose environment 4 | 5 | ### Start 6 | To start Docker Compose, from `workshop/exercises`, run the following command: 7 | 8 | ```bash 9 | docker compose up -d 10 | ``` 11 | 12 | This will start a Docker container with pygeoapi (reachable from the browser at ). 13 | 14 | To stop, use the command: 15 | 16 | ```bash 17 | docker compose down 18 | ``` 19 | -------------------------------------------------------------------------------- /workshop/exercises/data/README.md: -------------------------------------------------------------------------------- 1 | # Data 2 | 3 | This directory provides sample data to demonstrate functionality. 4 | 5 | ## Sources 6 | 7 | ### Bathingwater Estonia 8 | - title: [Supluskohad](./bathingwater-estonia.csv) (Bathing places) 9 | - url: https://avaandmed.eesti.ee/datasets/supluskohad 10 | - description: Locations where the Estonian Health Board monitors the bathing water quality 11 | - copyright: [Terviseamet](https://terviseamet.ee) 12 | - license: CC BY-SA 3.0 13 | - projection: epsg:3300 14 | 15 | ### Cadastral Parcels Estonia 16 | - title: [cptartu2.gpkg.zip](./cptartu2.gpkg.zip) 17 | - url: https://metadata.geoportaal.ee/geonetwork/srv/metadata/9949ec81-f758-42e6-9c1c-6fb604a3f053 18 | - description: Data converted to geopackage from WFS. 19 | - copyright: [Maa-amet](https://maaamet.ee) 20 | - license: CC BY 4.0 21 | 22 | ### Cycle Circulation Area in Florence 23 | - title: [cycle-lanes-firenze.geojson](cycle-lanes-firenze.geojson) 24 | - url: http://opendata.comune.firenze.it/?q=metarepo/datasetinfo&id=52d8d3ab-eae5-400e-8561-d974f8612de0 25 | - description: The dataset contains: acCic_s: Cycle Circulation Area with PolygonZ geometry (class acCic, has two spatial components, acCic_c with PolylineZ ring geometry and acCic_s with PolygonZ geometry). It consists of the longitudinal part of the road reserved for the circulation of cycles. It can be carried out: on its own when it is physically separated from the area reserved for motor vehicles and from that reserved for pedestrians, through suitable traffic dividers placed in the longitudinal direction and impassable; on a reserved lane, obtained from the roadway, separated from the lane reserved for motor vehicles simply by a longitudinal strip or by lane delimiters; on a reserved lane created on the pavement space. 26 | - copyright: Comune di Firenze 27 | - license: CC BY 4.0 28 | 29 | ### ICOADS 30 | - title: [coads_sst.nc](coads_sst.nc) 31 | - description: The International Comprehensive Ocean-Atmosphere Data Set (ICOADS) offers surface marine data spanning the past three centuries, and simple gridded monthly summary products for 2° latitude x 2° longitude boxes back to 1800 (and 1x1 boxes since 1960)° these data and products are freely distributed worldwide. As it contains observations from many different observing systems encompassing the evolution of measurement technology over hundreds of years, ICOADS is probably the most complete and heterogeneous collection of surface marine data in existence. 32 | - copyright: NOAA Physical Sciences Library 33 | - license: ICOADS data provided by the NOAA/OAR/ESRL PSL, Boulder, Colorado, USA, from their Web site at https://psl.noaa.gov. 34 | 35 | 36 | ## Metadata/Records 37 | 38 | ### Free Wifi Hotspots in Florence 39 | - title: [./firenze/metadata/xml/free-wifi-florence.xml](./firenze/metadata/xml/free-wifi-florence.xml) 40 | - url: https://opendata.comune.fi.it/?q=metarepo/datasetinfo&id=fb5b7bac-bcb0-4326-9388-7e3f3d671d71 41 | - description: The dataset shows the location of the places in the Municipality of Florence where a free wireless internet connection service (Wifi) is available. 42 | - copyright: Comune di Firenze 43 | - license: CC BY 4.0 44 | 45 | ### Carsharing spots in Florence 46 | - title: [./firenze/metadata/xml/carsharing-florence.xml](./firenze/metadata/xml/carsharing-florence.xml) 47 | - url: http://dati.cittametropolitana.fi.it/geonetwork/srv/ita/catalog.search#/metadata/cmfi:9c92966b-ae6b-42d4-9729-592b016e4b82 48 | - description: Punctual positioning of the car-sharing activities of the Metropolitan City of Florence. 49 | - copyright: Comune di Firenze 50 | - license: CC BY 4.0 51 | 52 | ### German Soil Types 53 | - title: [./bodenart.en.csv](./bodenart.en.csv) 54 | - url: https://registry.gdi-de.org/codelist/de.bund.thuenen/bodenart/bodenart.en.csv 55 | - description: Bodenarten auf Basis der Bodenkundlichen Kartieranleitung 5. Auflage (KA5) 56 | - copyright: Johann Heinrich von Thünen-Institut 57 | - license: CC BY 4.0 58 | 59 | ### Estonia Dams 60 | - title: [./tartu/metadata/xml/estonia-dams.xml](./tartu/metadata/xml/estonia-dams.xml) 61 | - url: https://metadata.geoportaal.ee/geonetwork/srv/eng/catalog.search#/metadata/937c4456-fd9f-40cf-89ba-30be47146858 62 | - description: Eesti paisude ruumiandmekogum koondab andmeid paisude asukohtade, tehniliste parameetrite ja seisundi kohta. Andmeid hallatakse Eesti Looduse Infosüsteemis (EELIS). 63 | - copyright: Keskkonnaagentuur 64 | - license: CC BY-SA 3.0 65 | 66 | ### Estonia Landcover 67 | - title: [./tartu/metadata/xml/estonia-landcover.xml](./tartu/metadata/xml/estonia-landcover.xml) 68 | - url: https://metadata.geoportaal.ee/geonetwork/srv/eng/catalog.search#/metadata/2cde09a2-ea86-4a73-9768-6c7ba78bc0e6 69 | - description: Eesti maakatte andmebaas. Koostatud ühtse üle-euroopalise Corine Land Cover metoodika alusel. Minimaalne kaardistusühik 25 ha, hierarhiline nomenklatuur IV taset. 70 | - copyright: Keskkonnaagentuur 71 | - license: CC BY-SA 3.0 72 | 73 | ### Estonia Transport 74 | - title: [./tartu/metadata/xml/estonia-transport.xml](./tartu/metadata/xml/estonia-transport.xml) 75 | - url: https://metadata.geoportaal.ee/geonetwork/srv/eng/catalog.search#/metadata/bf38a8fc-96f1-4d34-9130-18160e489514 76 | - description: Ruumiandmekogum "Eesti topograafia andmekogu - transport" hõlmab Eesti topograafia andmekogu (ETAK) reaalsusmudeli transpordi gruppi koondatud reaalse maailma nähtuseid, mis on klassifitseeritud nähtusklassideks 77 | - copyright: Keskkonnaagentuur 78 | - license: CC BY-SA 3.0 79 | 80 | ### NASA Blue Marble Night Lights Data sample over Estonia 81 | - title: [./tartu/estonia_light.tif](./tartu/estonia_light.tif) 82 | - url: https://blackmarble.gsfc.nasa.gov/ 83 | - description: At night, satellite images of Earth capture a uniquely human signal--artificial lighting. Remotely-sensed lights at night provide a new data source for improving our understanding of interactions between human systems and the environment. NASA has developed the Black Marble, a daily calibrated, corrected, and validated product suite, so nightlight data can be used effectively for scientific observations. Black Marble is playing a vital role in research on light pollution, illegal fishing, fires, disaster impacts and recovery, and human settlements and associated energy infrastructures. 84 | - copyright: NASA 85 | - license: CC0 86 | -------------------------------------------------------------------------------- /workshop/exercises/data/airport.gpkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/exercises/data/airport.gpkg -------------------------------------------------------------------------------- /workshop/exercises/data/airport.sld: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | airport 5 | 6 | airport 7 | 8 | 9 | Single symbol 10 | 11 | 12 | 13 | circle 14 | 15 | #91522d 16 | 17 | 18 | #232323 19 | 0.5 20 | 21 | 22 | 7 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /workshop/exercises/data/brazil/guama_river.gpkg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/exercises/data/brazil/guama_river.gpkg.zip -------------------------------------------------------------------------------- /workshop/exercises/data/coads_sst.nc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/exercises/data/coads_sst.nc -------------------------------------------------------------------------------- /workshop/exercises/data/firenze/cycle-lanes-firenze.qmd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Italian 6 | dataset 7 | Viabilità - Area di Circolazione Ciclabile con geometria PolygonZ 8 | Il dataset contiene: acCic_s: Area di Circolazione Ciclabile con geometria PolygonZ (classe acCic, presenta due componenti spaziali, acCic_c con geometria anello PolylineZ e acCic_s con geometria PolygonZ). E’ costituita da parte longitudinale della strada riservata alla circolazione dei velocipedi. Può essere realizzata: in sede propria quando e separata fisicamente dall’area riservata ai veicoli a motore e da quella riservata ai pedoni, attraverso idonei spartitraffico posti nel senso longitudinale ed invalicabili; su corsia riservata, ricavata dalla carreggiata stradale, separata dalla corsia riservata ai veicoli a motore semplicemente attraverso una striscia longitudinale o attraverso delimitatori di corsia; su corsia riservata ricavata sullo spazio del marciapiede. 9 | 10 | Society 11 | Transportation 12 | Utilities Communication 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | Creative Commons Attribuzione 4.0 Internazionale (CC-BY 4.0) 28 | 29 | 30 | 31 | 32 | 33 | 0 34 | 0 35 | 36 | 37 | 38 | 39 | false 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /workshop/exercises/data/firenze/metadata/catalogue.tinydb: -------------------------------------------------------------------------------- 1 | {"_default": {"1": {"id": "c_d612:fb5b7bac-bcb0-4326-9388-7e3f3d671d71:1", "conformsTo": ["http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core"], "type": "Feature", "time": ["2022-06-10", "2022-06-10"], "geometry": {"type": "Polygon", "coordinates": [[[11.145, 43.718], [11.145, 43.84], [11.348, 43.84], [11.348, 43.718], [11.145, 43.718]]]}, "properties": {"created": "2022-06-10", "updated": "2023-12-15T11:24:47Z", "type": "dataset", "title": "Wifi", "description": "Il dataset mostra la georeferenziazione puntuale su mappa dei luoghi nel Comune di Firenze in cui \u00e8 disponibile un servizio gratuito di connessione ad internet in modalit\u00e0 senza fili (Wifi).", "providers": [{"contactInfo": {"address": {"office": {}}, "phone": {"office": "+39 055 328 3858"}, "email": {"office": "sit@comune.fi.it"}}, "name": null, "roles": [{"name": "pointOfContact"}]}], "externalIds": [{"scheme": "default", "value": "c_d612:fb5b7bac-bcb0-4326-9388-7e3f3d671d71:1"}], "themes": [{"concepts": [{"id": "EU"}]}, {"concepts": [{"id": "wifi"}]}, {"concepts": [{"id": "connessione internet"}]}, {"concepts": [{"id": "reti"}]}, {"concepts": [{"id": "Servizi di pubblica utilit\u00e0 e servizi amministrativi"}], "scheme": null}, {"concepts": [{"id": "open data"}]}], "_metadata-anytext": "c_d612:fb5b7bac-bcb0-4326-9388-7e3f3d671d71:1 c_d612:fb5b7bac-bcb0-4326-9388-7e3f3d671d71:1 Comune di Firenze +39 055 328 3858 sit@comune.fi.it DM-Regole Tecniche RNDT 10 novembre 2011 3003 http://www.epsg-registry.org/ Wifi c_d612:fb5b7bac-bcb0-4326-9388-7e3f3d671d71 Comune di Firenze +39 055 328 3858 sit@comune.fi.it Comune di Firenze +39 055 328 3858 sit@comune.fi.it c_d612:fb5b7bac-bcb0-4326-9388-7e3f3d671d71 Il dataset mostra la georeferenziazione puntuale su mappa dei luoghi nel Comune di Firenze in cui \u00e8 disponibile un servizio gratuito di connessione ad internet in modalit\u00e0 senza fili (Wifi). Comune di Firenze +39 055 328 3858 sit@comune.fi.it EU wifi connessione internet reti Servizi di pubblica utilit\u00e0 e servizi amministrativi GEMET - INSPIRE themes, version 1.0 open data Creative Commons Attribuzione 4.0 Internazionale (CC-BY 4.0) wms Standard url Standard Comune di Firenze +39 055 328 3858 sit@comune.fi.it OGC:WMS catastoreti:access_point_wifi REGOLAMENTO (UE) N. 1089/2010 DELLA COMMISSIONE del 23 novembre 2010 recante attuazione della direttiva 2007/2/CE del Parlamento europeo e del Consiglio per quanto riguarda l'interoperabilit\u00e0 dei set di dati territoriali e dei servizi di dati territoriali See the referenced specification Il SIT del comune di Firenze \u00e8 attivo dagli anni 90 e promuove la georeferenziazione dei dati e la costruzione di archivi geografici. Il SIT attualmente fornisce l'infrastruttura tecnologica e le applicazioni. L'aggiornamento del dato \u00e8 a cura delle direzioni titolari del dato."}, "links": [{"href": "http://opendata.comune.fi.it/?q=metarepo/datasetinfo&id=fb5b7bac-bcb0-4326-9388-7e3f3d671d71", "rel": "item"}, {"href": "https://datigis.comune.fi.it/shp/wifi.zip", "rel": "item"}, {"href": "https://datigis.comune.fi.it/kml/wifi.kmz", "rel": "item"}, {"href": "http://tms.comune.fi.it/tiles/service/wms?request=GetCapabilities&service=WMS&tiled=TRUE&version=1.3.0", "rel": "item", "title": "catastoreti:access_point_wifi", "type": "OGC:WMS"}]}, "2": {"id": "cmfi:9c92966b-ae6b-42d4-9729-592b016e4b82", "conformsTo": ["http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core"], "type": "Feature", "time": [null, null], "geometry": {"type": "Polygon", "coordinates": [[[10.7, 43.43], [10.7, 44.25], [11.76, 44.25], [11.76, 43.43], [10.7, 43.43]]]}, "properties": {"created": "2021-11-25", "updated": "2023-12-15T11:24:47Z", "type": "dataset", "title": "Carsharing (anno 2012) - Citt\u00e0 Metropolitana di Firenze", "description": "Posizionamento puntuale delle attivit\u00e0 di carsharing della Citt\u00e0 Metropolitana di Firenze", "providers": [{"contactInfo": {"address": {"office": {"deliveryPoint": "Via Cavour 9", "city": "Firenze", "postalCode": "50129", "country": "Italia"}}, "email": {"office": "sit@cittametropolitana.fi.it"}}, "name": "Direzione SIT e reti informative", "roles": [{"name": "pointOfContact"}]}], "externalIds": [{"scheme": "default", "value": "cmfi:9c92966b-ae6b-42d4-9729-592b016e4b82"}], "themes": [{"concepts": [{"id": "Carsharing"}, {"id": "Mobilit\u00e0"}, {"id": "Viabilit\u00e0"}]}, {"concepts": [{"id": "Reti di trasporto"}], "scheme": "http://www.eionet.europa.eu/gemet/inspire_themes"}, {"concepts": [{"id": "Trasporti"}], "scheme": null}, {"concepts": [{"id": "Locale"}], "scheme": "http://inspire.ec.europa.eu/metadata-codelist/SpatialScope"}, {"concepts": [{"id": "Reti di trasporto"}], "scheme": "https://registry.geodati.gov.it/rndt-all1"}, {"concepts": [{"id": "opendata"}, {"id": "EU"}]}], "_metadata-anytext": "cmfi:9c92966b-ae6b-42d4-9729-592b016e4b82 Citt\u00e0 Metropolitana di Firenze Direzione SIT e reti informative Via Cavour 9 Firenze 50129 Italia sit@cittametropolitana.fi.it Linee Guida RNDT 2.0 Carsharing (anno 2012) - Citt\u00e0 Metropolitana di Firenze cmfi:9c92966b-ae6b-42d4-9729-592b016e4b82_resource Direzione SIT e reti informative Citt\u00e0 Metropolitana di Firenze 055 2760199 Via Cavour 9 Firenze 50129 Italia sit@cittametropolitana.fi.it cmfi:9c92966b-ae6b-42d4-9729-592b016e4b82_resource Posizionamento puntuale delle attivit\u00e0 di carsharing della Citt\u00e0 Metropolitana di Firenze (da compilare) Direzione SIT e reti informative Citt\u00e0 Metropolitana di Firenze 055 2760199 Via Cavour 9 Firenze 50129 Italia sit@cittametropolitana.fi.it Carsharing Mobilit\u00e0 Viabilit\u00e0 Trasporti theme.data-theme-skos.rdf opendata EU Dato pubblico http://publications.europa.eu/resource/authority/licence/CC_BY_4_0 ESRI Shapefile 1.0 WMS OGC Web Map Service (ver. 1.3) Citt\u00e0 Metropolitana di Firenze Direzione SIT e reti informative Via Cavour 9 Firenze 50129 Italia sit@cittametropolitana.fi.it Carsharing.zip infomob_poi_carsharing Fare riferimento alle specifiche indicate (da compilare)"}, "links": [{"href": "http://dati.cittametropolitana.fi.it/geonetwork/srv/api/records/cmfi:9c92966b-ae6b-42d4-9729-592b016e4b82/attachments/Carsharing.zip", "rel": "item", "title": "Carsharing.zip"}, {"href": "http://pubblicazioni.cittametropolitana.fi.it/geoserver/trasporti/infomob_poi_carsharing/wms?request=getCapabilities&service=WMS&version=1.3.0", "rel": "item", "title": "infomob_poi_carsharing"}]}}} -------------------------------------------------------------------------------- /workshop/exercises/data/tartu/cptartu2.gpkg.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/exercises/data/tartu/cptartu2.gpkg.zip -------------------------------------------------------------------------------- /workshop/exercises/data/tartu/estonia_light.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geopython/diving-into-pygeoapi/a459ce7999bf94a04ab5503aaceb3333de476064/workshop/exercises/data/tartu/estonia_light.tif -------------------------------------------------------------------------------- /workshop/exercises/data/tartu/metadata/catalogue.tinydb: -------------------------------------------------------------------------------- 1 | {"_default": {"1": {"id": "2cde09a2-ea86-4a73-9768-6c7ba78bc0e6", "conformsTo": ["http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core"], "type": "Feature", "time": ["1990-01-01", "2006-01-01"], "geometry": {"type": "Polygon", "coordinates": [[[21.77, 57.47], [21.77, 59.68], [28.21, 59.68], [28.21, 57.47], [21.77, 57.47]]]}, "properties": {"created": "2024-04-16T12:21:18", "updated": "2024-06-09T02:15:07Z", "type": "dataset", "title": "Eesti CORINE maakatte andmekogu", "description": "Eesti maakatte andmebaas. Koostatud \u00fchtse \u00fcle-euroopalise Corine Land Cover metoodika alusel. Minimaalne kaardistus\u00fchik 25 ha, hierarhiline nomenklatuur IV taset.", "providers": [{"contactInfo": {"address": {"office": {}}, "email": {"office": "kaur@envir.ee"}}, "name": null, "roles": [{"name": "pointOfContact"}]}], "externalIds": [{"scheme": "default", "value": "2cde09a2-ea86-4a73-9768-6c7ba78bc0e6"}], "themes": [{"concepts": [{"id": "Maakate"}], "scheme": "http://inspire.ec.europa.eu/theme"}, {"concepts": [{"id": "Riiklik"}], "scheme": "http://inspire.ec.europa.eu/metadata-codelist/SpatialScope"}, {"concepts": [{"id": "maakate"}], "scheme": null}, {"concepts": [{"id": "CORINE maakate"}, {"id": "maakatte klass"}, {"id": "INSPIRE"}]}], "_metadata-anytext": "2cde09a2-ea86-4a73-9768-6c7ba78bc0e6 Keskkonnaagentuur kaur@envir.ee ISO19115 2003/Cor.1:2006 http://www.opengis.net/def/crs/EPSG/0/4258 http://www.opengis.net/def/crs/EPSG/0/3301 Eesti CORINE maakatte andmekogu KTK_Corine_maakate EE Eesti maakatte andmebaas. Koostatud \u00fchtse \u00fcle-euroopalise Corine Land Cover metoodika alusel. Minimaalne kaardistus\u00fchik 25 ha, hierarhiline nomenklatuur IV taset. Keskkonnaagentuur kaur@envir.ee https://metadata.geoportaal.ee/geonetwork/srv/api/records/2cde09a2-ea86-4a73-9768-6c7ba78bc0e6/attachments/keskkonnainfo-clc_2012.png CORINE maakate (2012) Eesti m\u00e4rks\u00f5nastik CORINE maakate maakatte klass INSPIRE Teenusest v\u00f5i selles jagatavatest andmetest tuletatud t\u00f6\u00f6de tegemisel palume viidata andmete omanikule (vt viidet konkreetse kaardikihi juures). Juhul kui kihi kohta pole eraldi tingimusi m\u00e4\u00e4ratud, on siinolevad andmed avaldatud CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/ litsentsi alusel viitega Keskkonnaministeeriumile kui andmete allikale. GML 3.2.1 Corine maakattekaardi kaardirakendus OGC:WMS Keskkonnainfo WMS teenuse getCapabilities WWW:LINK-1.0-http--link Keskkonnainfo WFS teenuse getCapabilities Komisjoni m\u00e4\u00e4rus (E\u00dc) nr 1205/2008, 3. detsember 2008, millega rakendatakse Euroopa Parlamendi ja n\u00f5ukogu direktiivi 2007/2/E\u00dc seoses metaandmetega See the referenced specification Komisjoni m\u00e4\u00e4rus (EL) nr 1089/2010, 23. november 2010, millega rakendatakse Euroopa Parlamendi ja n\u00f5ukogu direktiivi 2007/2/E\u00dc seoses ruumiandmekogumite ja -teenuste ristkasutatavusega See the referenced specification Vastab Corine Land Cover metoodikale."}, "links": [{"href": "http://corine.keskkonnaagentuur.ee/", "rel": "item", "title": "Corine maakattekaardi kaardirakendus"}, {"href": "https://gsavalik.envir.ee/geoserver/keskkonnainfo/ows?service=WMS&request=GetCapabilities", "rel": "item", "title": "Keskkonnainfo WMS teenuse getCapabilities", "type": "OGC:WMS"}, {"href": "https://gsavalik.envir.ee/geoserver/keskkonnainfo/ows?service=WFS&request=GetCapabilities", "rel": "item", "title": "Keskkonnainfo WFS teenuse getCapabilities", "type": "WWW:LINK-1.0-http--link"}]}, "2": {"id": "937c4456-fd9f-40cf-89ba-30be47146858", "conformsTo": ["http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core"], "type": "Feature", "time": [null, null], "geometry": {"type": "Polygon", "coordinates": [[[21.77, 57.47], [21.77, 59.68], [28.21, 59.68], [28.21, 57.47], [21.77, 57.47]]]}, "properties": {"created": "2024-04-03T10:55:14", "updated": "2024-06-09T02:15:07Z", "type": "dataset", "title": "Eesti paisud (EELIS)", "description": "Eesti paisude ruumiandmekogum koondab andmeid paisude asukohtade, tehniliste parameetrite ja seisundi kohta. Andmeid hallatakse Eesti Looduse Infos\u00fcsteemis (EELIS).", "providers": [{"contactInfo": {"address": {"office": {}}, "email": {"office": "ruta.tamre@envir.ee"}}, "name": null, "roles": [{"name": "pointOfContact"}]}], "externalIds": [{"scheme": "default", "value": "937c4456-fd9f-40cf-89ba-30be47146858"}], "themes": [{"concepts": [{"id": "H\u00fcdrograafia"}], "scheme": "http://inspire.ec.europa.eu/theme"}, {"concepts": [{"id": "Riiklik"}], "scheme": "http://inspire.ec.europa.eu/metadata-codelist/SpatialScope"}, {"concepts": [{"id": "paisud"}], "scheme": null}, {"concepts": [{"id": "tamm"}, {"id": "EELIS"}, {"id": "INSPIRE"}]}], "_metadata-anytext": "937c4456-fd9f-40cf-89ba-30be47146858 Keskkonnaagentuur ruta.tamre@envir.ee ISO19115 Technical Guidelines based on EN ISO 19115 and EN ISO 19119 (Version 1.2) http://www.opengis.net/def/crs/EPSG/0/4258 http://www.opengis.net/def/crs/EPSG/0/3301 Eesti paisud (EELIS) KTK_paisud ee.kaur.hy-eelis-pais EE Eesti paisude ruumiandmekogum koondab andmeid paisude asukohtade, tehniliste parameetrite ja seisundi kohta. Andmeid hallatakse Eesti Looduse Infos\u00fcsteemis (EELIS). Keskkonnaagentuur ruta.tamre@envir.ee https://inspire.geoportaal.ee/geoserver/HY_pais/wms?service=WMS&version=1.1.0&request=GetMap&layers=HY_pais%3AHY.PhysicalWaters.ManMadeObject&bbox=312124.7722400007%2C6346638.837460000%2C780617.3364400003%2C6647158.187640000&width=768&height=464&srs=EPSG%3A3301&styles=&format=image/png Eesti paisud (INSPIRE teenus) https://gsavalik.envir.ee/geoserver/eelis/wms?service=WMS&version=1.1.0&request=GetMap&layers=eelis%3Apais&bbox=282560.6696905027%2C6375308.737417143%2C751541.0976252693%2C6658861.367557161&width=768&height=464&srs=EPSG%3A3301&styles=&format=image/png Paisud (EELISe teenus) Eesti m\u00e4rks\u00f5nastik tamm EELIS INSPIRE tingimusi ei rakendata GML ESRI Shapefile 1.0 GeoJSON OGC:WMS EELIS-e WMS teenuse getCapabilities Paisud WWW:LINK-1.0-http--link EELIS-e WFS teenuse getCapabilities Paisud OGC:WMS INSPIRE WMS teenuse getCapabilities WWW:LINK-1.0-http--link INSPIRE WFS teenuse getCapabilities WWW:DOWNLOAD-1.0-http--download Kaardikihid ja failid alla laadimiseks Keskkonnaportaalis Komisjoni m\u00e4\u00e4rus (E\u00dc) nr 1205/2008, 3. detsember 2008, millega rakendatakse Euroopa Parlamendi ja n\u00f5ukogu direktiivi 2007/2/E\u00dc seoses metaandmetega See the referenced specification Komisjoni m\u00e4\u00e4rus (EL) nr 1089/2010, 23. november 2010, millega rakendatakse Euroopa Parlamendi ja n\u00f5ukogu direktiivi 2007/2/E\u00dc seoses ruumiandmekogumite ja -teenuste ristkasutatavusega See the referenced specification Andmed on p\u00e4rit Eesti looduse infos\u00fcsteemist (EELIS)."}, "links": [{"href": "https://gsavalik.envir.ee/geoserver/eelis/ows?service=WMS&version=1.3.0&request=GetCapabilities", "rel": "item", "title": "EELIS-e WMS teenuse getCapabilities", "type": "OGC:WMS"}, {"href": "https://gsavalik.envir.ee/geoserver/eelis/ows?service=WFS&version=2.0.0&request=GetCapabilities", "rel": "item", "title": "EELIS-e WFS teenuse getCapabilities", "type": "WWW:LINK-1.0-http--link"}, {"href": "https://inspire.geoportaal.ee/geoserver/HY_pais/wms?service=WMS&version=1.3.0&request=GetCapabilities", "rel": "item", "title": "INSPIRE WMS teenuse getCapabilities", "type": "OGC:WMS"}, {"href": "https://inspire.geoportaal.ee/geoserver/HY_pais/wfs?service=WFS&version=2.0.0&request=GetCapabilities", "rel": "item", "title": "INSPIRE WFS teenuse getCapabilities", "type": "WWW:LINK-1.0-http--link"}, {"href": "https://register.keskkonnaportaal.ee/register", "rel": "item", "title": "Kaardikihid ja failid alla laadimiseks Keskkonnaportaalis", "type": "WWW:DOWNLOAD-1.0-http--download"}]}, "3": {"id": "bf38a8fc-96f1-4d34-9130-18160e489514", "conformsTo": ["http://www.opengis.net/spec/ogcapi-records-1/1.0/req/record-core"], "type": "Feature", "time": [null, null], "geometry": {"type": "Polygon", "coordinates": [[[21.77, 57.47], [21.77, 59.68], [28.21, 59.68], [28.21, 57.47], [21.77, 57.47]]]}, "properties": {"created": "2024-04-12T06:06:06", "updated": "2024-06-09T02:15:07Z", "type": "dataset", "title": "Eesti topograafia andmekogu - transport", "description": "Ruumiandmekogum \"Eesti topograafia andmekogu - transport\" h\u00f5lmab Eesti topograafia andmekogu (ETAK) reaalsusmudeli transpordi gruppi koondatud reaalse maailma n\u00e4htuseid, mis on klassifitseeritud n\u00e4htusklassideks: teed (seal hulgas teealad), r\u00f6\u00f6basteed, sihid ja liikluskorralduslikud rajatised. ---> N\u00e4htusklassi teed atribuudid on: tee nimetus, tee nimetus aadressiandmete s\u00fcsteemis, tee number, s\u00f5idutee kood, tee loogilise osa kood, tee numbri s\u00fcnon\u00fc\u00fcm, tee numbri teine s\u00fcnon\u00fc\u00fcm, tee loogilise osa koodi s\u00fcnon\u00fc\u00fcm, tee loogilise osa koodi teine s\u00fcnon\u00fc\u00fcm, tee s\u00f5iduosa koodi s\u00fcnon\u00fc\u00fcm, tee s\u00f5iduosa koodi teine s\u00fcnon\u00fc\u00fcm, tee laius, teel\u00f5igu alguse tasand, teel\u00f5igu l\u00f5pu tasand, liikluse suunalisus, tee t\u00fc\u00fcp, tee kattematerjal, tee t\u00e4htsus, tee kartograafiline nimi. N\u00e4htusklassi r\u00f6\u00f6basteed atribuudid on: r\u00f6\u00f6bastee t\u00fc\u00fcp, elektrifitseeritud r\u00f6\u00f6bastee, r\u00f6\u00f6bastee t\u00e4htsus. N\u00e4htusklassi liikluskkorralduslikud rajatised atribuudid on: liikluskorraldusliku rajatise t\u00fc\u00fcp, t\u00f5kke suletus. N\u00e4htusklassil sihid atribuudid puuduvad. ---> Eesti topograafia andmekogu asutamise ja pidamise aluseks on \"Eesti topograafia andmekogu p\u00f5him\u00e4\u00e4rus\" ja \"Topograafiliste andmete h\u00f5ive kord ja \u00fcldist t\u00e4hendust omavad topograafilised n\u00e4htused\". ---> Peamisteks andmeallikateks on: aeropildistamise ja laserskaneerimise teel saadud andmed v\u00f5i nende tuletised, sealhulgas ortofotod; teabevaldajate poolt andmevahetuse k\u00e4igus edastatud andmed; vastutava t\u00f6\u00f6tleja tehtud m\u00f5\u00f5distuste andmed; muude vaatluste ja m\u00f5\u00f5distamiste tulemusel, sealhulgas avalikest andmebaasidest ja muudest allikatest saadud andmed. Nt Eesti topograafia andmekogu maanteed on seotud Eesti riikliku teeregistri andmetega.", "providers": [{"contactInfo": {"address": {"office": {}}, "email": {"office": "inspire@maaamet.ee"}}, "name": null, "roles": [{"name": "pointOfContact"}]}], "externalIds": [{"scheme": "default", "value": "bf38a8fc-96f1-4d34-9130-18160e489514"}], "themes": [{"concepts": [{"id": "Transpordiv\u00f5rgud"}], "scheme": "http://inspire.ec.europa.eu/theme"}, {"concepts": [{"id": "Riiklik"}], "scheme": "http://inspire.ec.europa.eu/metadata-codelist/SpatialScope"}, {"concepts": [{"id": "teed"}, {"id": "maanteed"}, {"id": "r\u00f6\u00f6basteed"}, {"id": "k\u00f6isteed"}, {"id": "t\u00e4navad"}], "scheme": null}, {"concepts": [{"id": "tee"}, {"id": "p\u00f5himaantee"}, {"id": "tugimaantee"}, {"id": "k\u00f5rvalmaantee"}, {"id": "ramp"}, {"id": "\u00fchendustee"}, {"id": "riigimaantee"}, {"id": "t\u00e4nav"}, {"id": "muu tee"}, {"id": "rada"}, {"id": "sihid"}, {"id": "liikluskorralduslikud rajatised"}, {"id": "k\u00f6istee"}, {"id": "trammitee"}, {"id": "kitsar\u00f6\u00f6pmeline"}, {"id": "laiar\u00f6\u00f6pmeline"}, {"id": "r\u00f6\u00f6basteed"}, {"id": "sild"}, {"id": "purre"}, {"id": "tunnel"}, {"id": "autotunnel"}, {"id": "s\u00f5idutakistus"}, {"id": "\u00fclevedu"}, {"id": "INSPIRE"}, {"id": "avaandmed"}]}], "_metadata-anytext": "bf38a8fc-96f1-4d34-9130-18160e489514 Maa-amet inspire@maaamet.ee ISO19115 2003/Cor.1:2006 http://www.opengis.net/def/crs/EPSG/0/3301 http://www.opengis.net/def/crs/EPSG/0/4258 Eesti topograafia andmekogu - transport 70003098-ETAK-transport EE http://maaamet.ee/andmekogu/transport/etak_transport Ruumiandmekogum \"Eesti topograafia andmekogu - transport\" h\u00f5lmab Eesti topograafia andmekogu (ETAK) reaalsusmudeli transpordi gruppi koondatud reaalse maailma n\u00e4htuseid, mis on klassifitseeritud n\u00e4htusklassideks: teed (seal hulgas teealad), r\u00f6\u00f6basteed, sihid ja liikluskorralduslikud rajatised. ---> N\u00e4htusklassi teed atribuudid on: tee nimetus, tee nimetus aadressiandmete s\u00fcsteemis, tee number, s\u00f5idutee kood, tee loogilise osa kood, tee numbri s\u00fcnon\u00fc\u00fcm, tee numbri teine s\u00fcnon\u00fc\u00fcm, tee loogilise osa koodi s\u00fcnon\u00fc\u00fcm, tee loogilise osa koodi teine s\u00fcnon\u00fc\u00fcm, tee s\u00f5iduosa koodi s\u00fcnon\u00fc\u00fcm, tee s\u00f5iduosa koodi teine s\u00fcnon\u00fc\u00fcm, tee laius, teel\u00f5igu alguse tasand, teel\u00f5igu l\u00f5pu tasand, liikluse suunalisus, tee t\u00fc\u00fcp, tee kattematerjal, tee t\u00e4htsus, tee kartograafiline nimi. N\u00e4htusklassi r\u00f6\u00f6basteed atribuudid on: r\u00f6\u00f6bastee t\u00fc\u00fcp, elektrifitseeritud r\u00f6\u00f6bastee, r\u00f6\u00f6bastee t\u00e4htsus. N\u00e4htusklassi liikluskkorralduslikud rajatised atribuudid on: liikluskorraldusliku rajatise t\u00fc\u00fcp, t\u00f5kke suletus. N\u00e4htusklassil sihid atribuudid puuduvad. ---> Eesti topograafia andmekogu asutamise ja pidamise aluseks on \"Eesti topograafia andmekogu p\u00f5him\u00e4\u00e4rus\" ja \"Topograafiliste andmete h\u00f5ive kord ja \u00fcldist t\u00e4hendust omavad topograafilised n\u00e4htused\". ---> Peamisteks andmeallikateks on: aeropildistamise ja laserskaneerimise teel saadud andmed v\u00f5i nende tuletised, sealhulgas ortofotod; teabevaldajate poolt andmevahetuse k\u00e4igus edastatud andmed; vastutava t\u00f6\u00f6tleja tehtud m\u00f5\u00f5distuste andmed; muude vaatluste ja m\u00f5\u00f5distamiste tulemusel, sealhulgas avalikest andmebaasidest ja muudest allikatest saadud andmed. Nt Eesti topograafia andmekogu maanteed on seotud Eesti riikliku teeregistri andmetega. Maa-amet hanno.kuus@maaamet.ee Maa-amet avaandmed@maaamet.ee https://inspire.geoportaal.ee/geoserver/TN_transportetak/wms?service=WMS&version=1.1.0&request=GetMap&layers=TN_transportetak%3ATN.RoadTransportNetwork.RoadLink&bbox=657462.4299800007%2C6473000.505280000%2C661122.5281300003%2C6475200.381750000&width=768&height=464&srs=EPSG%3A3301&styles=&format=image/png Teed Eesti m\u00e4rks\u00f5nastik tee p\u00f5himaantee tugimaantee k\u00f5rvalmaantee ramp \u00fchendustee riigimaantee t\u00e4nav muu tee rada sihid liikluskorralduslikud rajatised k\u00f6istee trammitee kitsar\u00f6\u00f6pmeline laiar\u00f6\u00f6pmeline r\u00f6\u00f6basteed sild purre tunnel autotunnel s\u00f5idutakistus \u00fclevedu INSPIRE avaandmed Andmete kasutamisel n\u00f5ustute maa-ameti avaandmete litsentsiga: https://geoportaal.maaamet.ee/avaandmete-litsents (Creative Commons vaste CC BY 4.0) \nOpen data licence: https://geoportaal.maaamet.ee/opendata-licence (Creative Commons equivalent CC BY 4.0) ESRI GDB unknown ESRI Shapefile 1.0 Mapinfo TAB GML 3.2.1 GPKG unknown Autocad DXF unknown DGN WWW:LINK-1.0-http--related Eesti topograafia andmekogu Maa-ameti geoportaalis WWW:DOWNLOAD-1.0-http--download Andmete allalaadimine veebilehelt OGC:WMS ETAK andmete WMS getCapabilities WWW:LINK-1.0-http--link ETAK andmete WFS teenuse getCapabilities OGC:WMS WMS teenuse getCapabilities WWW:LINK-1.0-http--related WFS teenuse getCapabilities Komisjoni m\u00e4\u00e4rus (E\u00dc) nr 1205/2008, 3. detsember 2008, millega rakendatakse Euroopa Parlamendi ja n\u00f5ukogu direktiivi 2007/2/E\u00dc seoses metaandmetega Metaandmed on koostatud vastavalt INSPIRE metaandmete m\u00e4\u00e4rusele. Komisjoni m\u00e4\u00e4rus (EL) nr 1089/2010, 23. november 2010, millega rakendatakse Euroopa Parlamendi ja n\u00f5ukogu direktiivi 2007/2/E\u00dc seoses ruumiandmekogumite ja -teenuste ristkasutatavusega See the referenced specification Topograafiliste andmete kaardistusjuhend http://geoportaal.maaamet.ee/docs/ETAK/ETAK_juhend2016.pdf Ruumiandmekogumi \"Eesti topograafi andmekogu - transport\" andmete kvaliteet vastab \"Topograafiliste andmete kaardistusjuhendile\"."}, "links": [{"href": "https://geoportaal.maaamet.ee/est/Ruumiandmed/Eesti-topograafia-andmekogu-p79.html", "rel": "item", "title": "Eesti topograafia andmekogu Maa-ameti geoportaalis", "type": "WWW:LINK-1.0-http--related"}, {"href": "https://geoportaal.maaamet.ee/est/Ruumiandmed/Eesti-topograafia-andmekogu/Laadi-ETAK-andmed-alla-p609.html", "rel": "item", "title": "Andmete allalaadimine veebilehelt", "type": "WWW:DOWNLOAD-1.0-http--download"}, {"href": "https://gsavalik.envir.ee/geoserver/etak/wms?service=WMS&request=GetCapabilities", "rel": "item", "title": "ETAK andmete WMS getCapabilities", "type": "OGC:WMS"}, {"href": "https://gsavalik.envir.ee/geoserver/etak/wfs?service=WFS&request=GetCapabilities", "rel": "item", "title": "ETAK andmete WFS teenuse getCapabilities", "type": "WWW:LINK-1.0-http--link"}, {"href": "https://inspire.geoportaal.ee/geoserver/TN_transportetak/wms?service=WMS&version=1.3.0&request=GetCapabilities", "rel": "item", "title": "WMS teenuse getCapabilities", "type": "OGC:WMS"}, {"href": "https://inspire.geoportaal.ee/geoserver/TN_transportetak/wfs?service=WFS&version=2.0.0&request=GetCapabilities", "rel": "item", "title": "WFS teenuse getCapabilities", "type": "WWW:LINK-1.0-http--related"}]}}} -------------------------------------------------------------------------------- /workshop/exercises/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # ================================================================= 2 | # 3 | # Authors: Just van den Broecke > 4 | # Jorge Samuel Mendes de Jesus 5 | # Tom Kralidis 6 | # 7 | # Copyright (c) 2019 Just van den Broecke 8 | # Copyright (c) 2019 Jorge Samuel Mendes de Jesus 9 | # Copyright (c) 2023 Tom Kralidis 10 | # 11 | # Permission is hereby granted, free of charge, to any person 12 | # obtaining a copy of this software and associated documentation 13 | # files (the "Software"), to deal in the Software without 14 | # restriction, including without limitation the rights to use, 15 | # copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | # copies of the Software, and to permit persons to whom the 17 | # Software is furnished to do so, subject to the following 18 | # conditions: 19 | # 20 | # The above copyright notice and this permission notice shall be 21 | # included in all copies or substantial portions of the Software. 22 | # 23 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 24 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 25 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 26 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 27 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 28 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 29 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 30 | # OTHER DEALINGS IN THE SOFTWARE. 31 | # 32 | # ================================================================= 33 | 34 | services: 35 | pygeoapi: 36 | image: geopython/pygeoapi:latest 37 | 38 | container_name: pygeoapi 39 | 40 | ports: 41 | - 5000:80 42 | 43 | volumes: 44 | - ./pygeoapi.config.yml:/pygeoapi/local.config.yml 45 | # - ./data:/data # Exercise 1 - First - Ready to pull data from here 46 | - ./plugins/process/squared.py:/pygeoapi/pygeoapi/process/squared.py # Exercise 8 47 | -------------------------------------------------------------------------------- /workshop/exercises/elastic/README.md: -------------------------------------------------------------------------------- 1 | # pygeoapi with Elasticsearch (ES) 2 | 3 | For those interested in working with Elasticsearch, please see [the pygeoapi GitHub repository](https://github.com/geopython/pygeoapi-examples/tree/main/docker/elastic) for a full example Docker setup. 4 | -------------------------------------------------------------------------------- /workshop/exercises/html/mapscript-on-leaflet.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 16 | Leaflet.js 17 | 18 | 19 |
20 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /workshop/exercises/html/vector-tiles.html: -------------------------------------------------------------------------------- 1 | 2 | OGC API - Tiles exercise 3 | 4 |
5 | 6 | 7 | 8 | 47 | 48 | -------------------------------------------------------------------------------- /workshop/exercises/plugins/process/squared.py: -------------------------------------------------------------------------------- 1 | # ================================================================= 2 | # 3 | # Authors: Tom Kralidis 4 | # 5 | # Copyright (c) 2023 Tom Kralidis 6 | # 7 | # Permission is hereby granted, free of charge, to any person 8 | # obtaining a copy of this software and associated documentation 9 | # files (the "Software"), to deal in the Software without 10 | # restriction, including without limitation the rights to use, 11 | # copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | # copies of the Software, and to permit persons to whom the 13 | # Software is furnished to do so, subject to the following 14 | # conditions: 15 | # 16 | # The above copyright notice and this permission notice shall be 17 | # included in all copies or substantial portions of the Software. 18 | # 19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 | # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 | # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 | # OTHER DEALINGS IN THE SOFTWARE. 27 | # 28 | # ================================================================= 29 | 30 | import logging 31 | 32 | from pygeoapi.process.base import BaseProcessor, ProcessorExecuteError 33 | 34 | 35 | LOGGER = logging.getLogger(__name__) 36 | 37 | #: Process metadata and description 38 | PROCESS_METADATA = { 39 | 'version': '0.1.0', 40 | 'id': 'squared', 41 | 'title': { 42 | 'en': 'Squared processor' 43 | }, 44 | 'description': { 45 | 'en': 'An example process that takes a number or integer and returns ' 46 | 'the squared result' 47 | }, 48 | 'jobControlOptions': ['sync-execute', 'async-execute'], 49 | 'keywords': ['squared'], 50 | 'links': [{ 51 | 'type': 'text/html', 52 | 'rel': 'about', 53 | 'title': 'information', 54 | 'href': 'https://example.org/process', 55 | 'hreflang': 'en-US' 56 | }], 57 | 'inputs': { 58 | 'number-or-integer': { 59 | 'title': 'Number', 60 | 'description': 'number or integer', 61 | 'schema': { 62 | 'oneOf': ['number', 'integer'], 63 | }, 64 | 'minOccurs': 1, 65 | 'maxOccurs': 1, 66 | 'metadata': None, # TODO how to use? 67 | 'keywords': ['number'] 68 | } 69 | }, 70 | 'outputs': { 71 | 'squared': { 72 | 'title': 'Squared', 73 | 'description': 'An example process that takes a number or ' 74 | 'integer and returns the squared result', 75 | 'schema': { 76 | 'type': 'object', 77 | 'contentMediaType': 'application/json' 78 | } 79 | } 80 | }, 81 | 'example': { 82 | 'inputs': { 83 | 'number-or-integer': 3 84 | } 85 | } 86 | } 87 | 88 | 89 | class SquaredProcessor(BaseProcessor): 90 | """Squared Processor example""" 91 | 92 | def __init__(self, processor_def): 93 | """ 94 | Initialize object 95 | 96 | :param processor_def: provider definition 97 | 98 | :returns: pygeoapi.process.squared.SquaredProcessor 99 | """ 100 | 101 | super().__init__(processor_def, PROCESS_METADATA) 102 | 103 | def execute(self, data): 104 | 105 | value = None 106 | mimetype = 'application/json' 107 | number_or_integer = data.get('number-or-integer') 108 | 109 | if number_or_integer is None: 110 | raise ProcessorExecuteError('Cannot process without input') 111 | 112 | # EXERCISE 8: fill in code to calculate the number or integer squared 113 | # and save to the "value" variable (that is defined as "None" at the 114 | # top of this function) 115 | # tip: ensure the input is indeed a number or integer! 116 | 117 | outputs = { 118 | 'id': 'squared', 119 | 'value': value 120 | } 121 | 122 | return mimetype, outputs 123 | 124 | def __repr__(self): 125 | return f' {self.name}' 126 | --------------------------------------------------------------------------------