OGC API - EDR Part 3 Service Profile Generator
Authoritative tooling for creating OGC API - Environmental Data Retrieval (EDR) Part 3 Service Profiles, built on Pydantic and edr-pydantic.
Overview
Profile structure is defined as Pydantic models (src/ogc_edr_profile/models.py). Instantiating a ServiceProfile validates the entire profile — cross-model validators catch referential errors — before any files are written.
Collections use edr-pydantic's authoritative Collection model directly, meaning a profile config is simultaneously a valid EDR collection descriptor and a Part 3 profile definition.
Installation
pip install ogc-edr-profile
Workflow
1. Author a Profile Config
A profile config is a YAML or JSON file. Start with the minimal example:
cp examples/minimal_profile.yaml my_profile.yaml
The minimal valid config:
name: my_profile
title: My EDR Profile
# OGC API - EDR Part 3 compliance fields (recommended)
required_conformance_classes:
- "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/core"
extent_requirements:
minimum_bbox: [-180, -90, 180, 90]
allowed_crs:
- "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
output_formats:
- name: GeoJSON
media_type: application/geo+json
schema_ref: https://geojson.org/schema/FeatureCollection.json
collections:
- id: my_collection
links:
- href: https://example.com/collections/my_collection
rel: self
type: application/json
extent:
spatial:
bbox:
- [-180, -90, 180, 90]
crs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
parameter_names:
temp:
type: Parameter
observedProperty:
label: Temperature
unit: # REQUIRED per OGC API - EDR Part 3
label: Celsius
symbol: C
See examples/minimal_profile.yaml for a complete working example and examples/nwsviz_profile.yaml for a full profile with 13 collections, 3 processes, requirements, abstract tests, and document metadata.
2. Generate Profile Artifacts
ogc-edr-profile generate \
--config my_profile.yaml \
--output ./my_profile
Produces:
my_profile/
├── openapi.yaml
├── profile_config.json
├── document.adoc # Metanorma root document
├── sections/
│ ├── 00-abstract.adoc
│ ├── 01-preface.adoc
│ ├── 02-scope.adoc
│ ├── 03-conformance.adoc
│ ├── 04-references.adoc
│ ├── 05-terms.adoc
│ ├── 06-requirements.adoc
│ └── 07-abstract-tests.adoc
├── requirements/
│ ├── requirements_class_core.adoc
│ └── core/REQ_<id>.adoc
└── abstract_tests/
├── ATS_class_core.adoc
└── core/ATS_<id>.adoc
Validate a config without generating output:
ogc-edr-profile validate --config my_profile.yaml
3. Compile OGC PDF
Requires Docker. Shells out to the official metanorma/metanorma image — no Ruby or LaTeX install needed.
ogc-edr-profile generate \
--config my_profile.yaml \
--output ./my_profile \
--pdf
The document_metadata block in the profile config drives the Metanorma document header:
document_metadata:
doc_number: "24-nwsviz"
doc_subtype: implementation
copyright_year: 2026
editors:
- Shane Mill
submitting_orgs:
- NOAA/NWS/MDL
keywords:
- ogcdoc
- OGC API
- EDR
- NWSViz
- service profile
external_id: http://www.opengis.net/doc/dp/ogcapi-edr-nwsviz/1.0
Produces my_profile/document.pdf — a fully compliant OGC draft-standard PDF with Abstract, Preface, Scope, Conformance, References, Terms, Requirements class, and normative Abstract Test Suite annex.
4. Validate Against a Live Server
ogc-edr-profile validate-server \
--config my_profile.yaml \
--url https://edr-api-desi-c.mdl.nws.noaa.gov \
--max-examples 3
Results:
Operations: 100 selected / 106 total
Tested: 47
Test cases: 1002 generated, 1002 passed
No issues found in 49.51s
Use --stateful to additionally test job lifecycle endpoints (/jobs/{jobId}, DELETE /jobs/{jobId}) via POST /execution chaining.
Add collection_examples to your config to supply real instanceId values for schemathesis path parameters:
collection_examples:
my_collection:
instanceId: "2025-04-02T00:00:00Z"
5. OGC CITE Conformance Testing
EDR Conformance Testing
Run the official OGC API - EDR Part 1 conformance test suite (ets-ogcapi-edr10):
ogc-edr-profile cite-test \
--url https://edr-api-desi-c.mdl.nws.noaa.gov \
--report ./cite_results
Results:
OGC API - EDR CITE Results
Passed: 76/84
Failed: 0
Skipped: 8
✓ All CITE tests passed.
The tool automatically:
- Clones and builds ets-ogcapi-edr10 from GitHub on first run
- Caches Docker image (
ogccite/ets-ogcapi-edr10:local) for subsequent runs - Runs TestNG tests via
docker exec - Supports localhost testing with
--network hostmode - Generates JSON report with detailed test results
The skipped tests are optional features not implemented by the server.
Features Conformance Testing
Run the official OGC API - Features Part 1 conformance test suite (ets-ogcapi-features10):
ogc-edr-profile cite-test-features \
--url https://api.example.com \
--report ./cite_features_results
Results:
OGC API - Features CITE Results
Passed: 639/712
Failed: 18
Skipped: 55
✗ 18 test(s) failed.
The tool automatically:
- Pulls pre-built Docker image (
ogccite/ets-ogcapi-features10:latest) from Docker Hub - Runs TestNG tests via
docker exec - Supports localhost testing with
--network hostmode - Generates JSON report with detailed test results
The skipped tests are optional features not implemented by the server.
Config Reference
Top-level fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
yes | Lowercase identifier using only a-z, 0-9, _. Used in OGC URIs and OpenAPI operationIds. e.g. water_gauge |
title |
string |
yes | Human-readable profile title |
version |
string |
no | Profile version. Defaults to 1.0 |
server_url |
string |
no | Base URL of the live server (for documentation only - not used in profile OpenAPI per OGC API - EDR Part 3) |
collections |
list |
yes | One or more EDR collections (see below) |
processes |
list |
no | OGC API Processes to include in the OpenAPI (see below) |
requirements |
list |
no | Normative requirements (see below) |
abstract_tests |
list |
no | Conformance tests — each must reference a valid requirement id (see below) |
pubsub |
object |
no | OGC API - EDR Part 2 PubSub configuration (see below) |
collection_examples |
object |
no | Map of collection id → example parameter values (e.g. instanceId) for server validation |
document_metadata |
object |
no | Metanorma document header fields for PDF compilation (see below) |
required_conformance_classes |
list[string] |
no | Conformance classes that implementations must declare. Defaults to EDR Core |
extent_requirements |
object |
no | Profile-level extent restrictions (see below) |
output_formats |
list |
no | Profile-level output format definitions with schema references (see below) |
collection_id_pattern |
string |
no | Regex pattern for valid collection IDs |
collections[]
Each collection uses the edr-pydantic Collection schema — the same model an EDR server returns at /collections/{id}. Key fields:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | Collection identifier |
title |
string |
no | Human-readable collection name |
description |
string |
no | Collection description |
links |
list |
yes | At minimum a self link |
extent.spatial.bbox |
list |
yes | Bounding box as [[minLon, minLat, maxLon, maxLat]] |
extent.spatial.crs |
string |
yes | CRS URI, typically http://www.opengis.net/def/crs/OGC/1.3/CRS84 |
data_queries |
object |
no | Which EDR query types this collection supports |
output_formats |
list |
no | Supported output format labels e.g. GeoJSON, CoverageJSON |
parameter_names |
object |
no | Map of parameter id → Parameter object |
data_queries
Supported keys: items · position · area · radius · cube · trajectory · corridor · locations · instances
data_queries:
position:
link:
href: https://example.com/collections/water_gauge/position
rel: data
variables:
query_type: position
output_formats:
- CoverageJSON
items:
link:
href: https://example.com/collections/water_gauge/items
rel: data
variables:
query_type: items
output_formats:
- GeoJSON
parameter_names
Note: Per OGC API - EDR Part 3 requirements, all parameters must specify unit and observedProperty. The tool validates this automatically.
parameter_names:
gauge_height:
type: Parameter
observedProperty:
label: Gauge Height
unit:
label: feet
symbol: ft
extent_requirements
Profile-level extent restrictions per OGC API - EDR Part 3 REQ_extent.
| Field | Type | Required | Description |
|---|---|---|---|
minimum_bbox |
list[float] |
yes | Minimum spatial bounds [minLon, minLat, maxLon, maxLat] |
allowed_crs |
list[string] |
no | Enumerated list of valid CRS values |
crs_pattern |
string |
no | Regular expression defining valid CRS string patterns |
allowed_trs |
list[string] |
no | Enumerated list of valid TRS values |
trs_pattern |
string |
no | Regular expression defining valid TRS string patterns |
allowed_vrs |
list[string] |
no | Enumerated list of valid VRS values |
vrs_pattern |
string |
no | Regular expression defining valid VRS string patterns |
Note: Either allowed_crs or crs_pattern must be specified.
extent_requirements:
minimum_bbox: [-180, -90, 180, 90]
allowed_crs:
- "http://www.opengis.net/def/crs/OGC/1.3/CRS84"
- "http://www.opengis.net/def/crs/EPSG/0/4326"
allowed_trs:
- "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"
output_formats[]
Profile-level output format definitions with schema references per OGC API - EDR Part 3 REQ_output-format.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
yes | Format name (e.g., GeoJSON, CoverageJSON) |
media_type |
string |
yes | MIME type (e.g., application/geo+json) |
schema_ref |
string |
no | URL to schema definition |
output_formats:
- name: GeoJSON
media_type: application/geo+json
schema_ref: https://geojson.org/schema/FeatureCollection.json
- name: CoverageJSON
media_type: application/prs.coverage+json
schema_ref: https://schemas.opengis.net/ogcapi/edr/1.0/openapi/schemas/coverageJSON.yaml
required_conformance_classes[]
Conformance classes that implementations must declare at /conformance per OGC API - EDR Part 3 REQ_api.
Defaults to:
required_conformance_classes:
- "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/core"
Add additional conformance classes as needed:
required_conformance_classes:
- "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/core"
- "http://www.opengis.net/spec/ogcapi-edr-1/1.0/conf/oas30"
- "http://www.opengis.net/spec/ogcapi-edr-2/1.0/conf/pubsub"
processes[]
OGC API Processes to expose in the generated OpenAPI. Each entry produces /processes/{id} and /processes/{id}/execution paths, plus /processes, /jobs, /jobs/{jobId}, and /jobs/{jobId}/results.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | Process identifier e.g. edr-zarr-difference |
title |
string |
no | Human-readable process name |
description |
string |
no | Process description |
output_content |
object |
no | OpenAPI content map for the 200 response. Defaults to application/json |
processes:
- id: edr-zarr-difference
title: EDR Zarr Dataset Difference
description: Calculates the difference between two EDR Zarr datasets.
output_content:
application/zip:
schema:
type: object
requirements[]
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | Lowercase, hyphen-separated. Must match ^[a-z0-9][a-z0-9\-]*$ |
statement |
string |
yes | One-sentence normative statement |
parts |
list[string] |
yes | One or more SHALL/MUST clauses |
requirements:
- id: position-coveragejson
statement: The position query SHALL return CoverageJSON.
parts:
- The service SHALL provide a /collections/{id}/position endpoint.
- The response Content-Type SHALL be application/prs.coverage+json.
abstract_tests[]
Every requirement_id must match an existing requirement id — the model validator will reject the profile if not.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
yes | Must equal requirement_id |
requirement_id |
string |
yes | The id of the requirement this test validates |
steps |
list[string] |
yes | Ordered test steps |
abstract_tests:
- id: position-coveragejson
requirement_id: position-coveragejson
steps:
- Send GET request to /collections/{id}/position?coords=POINT(lon lat).
- Verify the response Content-Type is application/prs.coverage+json.
pubsub
When present, an asyncapi.yaml is generated.
| Field | Type | Default | Description |
|---|---|---|---|
broker_host |
string |
localhost |
Message broker hostname |
broker_port |
integer |
5672 |
Broker port (1–65535) |
protocol |
string |
amqp |
One of amqp, mqtt, kafka |
filters |
list |
[] |
Subscription filters |
Each filter: name (required), description (required), type (one of string, number, array, boolean, default string).
document_metadata
Controls the Metanorma document header when compiling a PDF with --pdf.
| Field | Type | Required | Description |
|---|---|---|---|
doc_number |
string |
yes | OGC document number e.g. 24-nwsviz |
doc_subtype |
string |
yes | One of implementation, best-practice, engineering-report |
editors |
list[string] |
yes | Editor names |
submitting_orgs |
list[string] |
yes | Submitting organization names |
keywords |
list[string] |
no | Document keywords |
copyright_year |
integer |
no | Defaults to current year |
external_id |
string |
no | OGC external document URI |
OGC API - EDR Part 3 Compliance
This tool implements the requirements of OGC API - EDR Part 3: Service Profiles (draft standard):
Key Compliance Features
-
Profile OpenAPI Document (REQ_publishing)
- Generated OpenAPI has empty
serversarray (profile is implementation-independent) - Landing page schema requires
profilelink relation - Profile URI advertised in
x-ogc-profileinfo field
- Generated OpenAPI has empty
-
Conformance Classes (REQ_api)
/conformanceendpoint schema specifies required conformance classes- Defaults to EDR Core, customizable via
required_conformance_classes
-
Parameter Names (REQ_parameter-names)
- Validates that all parameters specify
unitandobservedProperty - Automatically enforced during profile validation
- Validates that all parameters specify
-
Extent Requirements (REQ_extent)
- Profile-level
extent_requirementsspecify minimum bounds - CRS/TRS/VRS restrictions via enumerated lists or regex patterns
- Profile-level
-
Output Formats (REQ_output-format)
- Profile-level
output_formatswith schema references - Links to JSON Schema, XML Schema, or format specifications
- Profile-level
-
Pub/Sub (REQ_pubsub)
- Automatically adds Part 2 conformance requirement when
pubsubis present - AsyncAPI document specifies channels and payloads
- Automatically adds Part 2 conformance requirement when
Profile Types
The tool supports both:
- Class 1 Profiles: Restrictive profiles that constrain EDR Core
- Class 2 Profiles: Extended profiles that add custom requirements (e.g., processes)
Both remain compliant with EDR Core - extensions are optional, not mandatory.
Programmatic Use
from ogc_edr_profile.models import ServiceProfile
from ogc_edr_profile.generate import generate
from pathlib import Path
profile = ServiceProfile.model_validate(config_dict)
generate(profile, Path("./output"))
Repository Structure
├── src/
│ └── ogc_edr_profile/
│ ├── models.py # Authoritative Pydantic schema
│ ├── generate.py # Validated model → OpenAPI, AsyncAPI, AsciiDoc
│ ├── compile.py # PDF compilation via metanorma/metanorma Docker image
│ ├── cite.py # OGC CITE test suite orchestration
│ └── cli.py # CLI entry point
├── examples/
│ ├── water_gauge.yaml # Minimal example profile config
│ └── nwsviz_profile.yaml # Full NWSViz profile: 13 collections, 3 processes, PDF metadata
├── profile.schema.json # Machine-readable JSON Schema for profile configs
└── pyproject.toml
Standards
- OGC API - EDR Part 1: Core
- OGC API - EDR Part 2: PubSub
- OGC API - EDR Part 3: Service Profiles (draft)
- OGC API - Processes Part 1
- OpenAPI 3.0 / AsyncAPI 3.0
- Metanorma/AsciiDoc documentation format
License
MIT — See LICENSE for details.
Contact
- Author: Shane Mill (NOAA/NWS/MDL)
- Email: shane.mill@noaa.gov
- Issues: https://github.com/ShaneMill1/OGC-Service-Profile-Creation/issues
Metadata
Release files for ogc-edr-profile 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ogc_edr_profile-2.0.0.tar.gz | 32.5 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ogc_edr_profile-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 69.0 kB
Release files / ogc_edr_profile-2.0.0.tar.gz
| Download URL | ogc_edr_profile-2.0.0.tar.gz |
|---|---|
| Size | 32.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
029d86d4017e50f4c2f0194a81749da20ab4d4c3f11b6fa23e7be1094ebeed75
|
|
BLAKE2b-256 checksum How to use checksums |
6d24b120180bc26b524f085a5945d92d6234cc826fb29b6da0af776b12f93466
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|
Release files / ogc_edr_profile-2.0.0-py3-none-any.whl
| Download URL | ogc_edr_profile-2.0.0-py3-none-any.whl |
|---|---|
| Size | 36.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
111f83dfe1959ba01a737d883caacfc2f21aa8ca20d24383e74be3dcc4f023fa
|
|
BLAKE2b-256 checksum How to use checksums |
6f35bbf6af055ffeed1edd03e9443889f3c8937d7fa47cbe637d3cf64d539823
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.13
|