Read-only mirror of https://github.com/swissgeo/service-oa-records — SWISSGEO. Issues & pull requests at the source.
  • Python 90.3%
  • Makefile 5.5%
  • Dockerfile 2%
  • Just 1.1%
  • Shell 1.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-08-21 13:26:30 +02:00
.github Added/Modified github workflows pr-auto-semver.yml #skip-tagging-and-release 2026-08-06 10:43:40 +02:00
.zed
config-files GPS-827: fix openapi generation 2026-07-21 16:26:20 +02:00
pygeoapi-swissgeo-extensions GPS-918: fix rewrites 2026-08-21 13:21:01 +02:00
scripts GPS-646: cleanup code 2026-07-21 11:03:39 +02:00
static-s3/api/oar/staticv2
tests GPS-918: fix rewrites 2026-08-21 13:21:01 +02:00
.dockerignore
.env-docker GPS-827: fix openapi generation 2026-07-21 16:26:20 +02:00
.gitignore
docker-compose.yml
docker-entrypoint.sh GPS-827: fix openapi generation 2026-07-21 16:26:20 +02:00
Dockerfile
justfile
LICENSE.md
Makefile
pygeoapi-config.yml
pyproject.toml
README.md
renovate.json
uv.lock

service-oa-records

OGC API Records service for SwissGeo, built on pygeoapi with an OpenSearch backend and multilingual record support.

Branch Status Coverage
develop Build Status codecov-develop
main Build Status codecov-main

Note

This is still in POC phase

Overview

This service exposes Swiss geospatial catalogue data as an OGC API Records endpoint. pygeoapi handles the OGC API layer; records are stored in OpenSearch and queried via SwissGeoProvider, a custom provider that adds language-aware field selection and link patching on top of pygeoapi's built-in OpenSearchCatalogueProvider.

Client
  │  ?lang=de&f=json
  ▼
uvicorn (app.py)          ← patches call_api_threadsafe to inject lang/fmt
  │                          into the executor thread-local before each call
  ▼
pygeoapi Starlette app
  │
  ▼
SwissGeoProvider          ← extends OpenSearchCatalogueProvider
  │  query() / get()
  ├─ reads lang from thread-local (set by app.py)
  ├─ calls super().query() / super().get()
  ├─ _apply_lang()   overwrites title/description with localised variants
  └─ _patch_links()  appends ?lang=…&f=… to same-host links
  │
  ▼
OpenSearch  (swissgeo-catalog / swissgeo-distributions index)

swissgeo_provider.py

Language handling

OpenSearch records carry per-language fields alongside the generic ones:

Generic field Localised variants
title title_de, title_fr, title_it, title_en
description description_de, description_fr, description_it, description_en

SwissGeoProvider._apply_lang() promotes the requested language's variant into the generic title/description fields and strips all per-language fields before pygeoapi serialises the response. Supported languages: en, de, fr, it (falls back to en).

Why app.py is needed

pygeoapi's Starlette integration runs provider calls in a thread pool. By the time the provider executes, the Starlette request context is no longer accessible. app.py monkey-patches call_api_threadsafe to call set_request_params(lang, fmt) just before dispatching each call, storing the values in a threading.local that SwissGeoProvider reads. Flask users are handled via a fallback flask.request.args read inside _get_lang_and_fmt().

_patch_links() appends ?lang=<lang>&f=<fmt> to any link whose href is relative or starts with PYGEOAPI_SERVER_URL. External links are left untouched.

Configuration

Provider registration in pygeoapi-config.yml:

providers:
  - type: record
    name: swissgeo_provider.SwissGeoProvider
    data: ${OPENSEARCH_URL:-http://localhost:9200}/swissgeo-catalog
    id_field: externalId
    time_field: recordCreated
    title_field: title
    languages:
      - en
      - de
      - fr
      - it

Key environment variables:

Variable Default Purpose
PYGEOAPI_SERVER_URL / Base URL used to identify same-host links for patching
OPENSEARCH_URL http://localhost:9200 OpenSearch base URL
PYGEOAPI_CONFIG /pygeoapi/pygeoapi-config.yml pygeoapi config file path

Running locally

docker compose up

This starts:

  • pygeoapi on http://localhost:8080/api/oar/r/ (uvicorn, via app.py)
  • OpenSearch on port 9200
  • catalogue-loader — one-shot container that loads records from static-s3/ into OpenSearch
  • OpenSearch Dashboards on http://localhost:5602

Copy .env-docker (or create one from .env-local) to configure environment variables before starting.

Debugging

Have ENV PYDEBUG=true set.

PYDEBUG=true docker compose --profile debug up

This runs pygeoapi under debugpy listening on port 5678, with the local pygeoapi-swissgeo-extensions/ directory mounted into the container so edits are reflected without a rebuild.

Then attach your debugger (e.g. "Attach to Docker (swissgeo_provider)" in Zed) to localhost:5678.

Project structure

pygeoapi-swissgeo-extensions/
  app.py                  # Starlette entrypoint; patches call_api_threadsafe
  swissgeo_provider.py    # SwissGeoProvider: language selection + link patching
pygeoapi-config.yml       # pygeoapi server + collection configuration
scripts/                  # Data loading scripts
static-s3/                # 1:1 catalog data from S3