Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "interfaces/ED-318"]
path = schemas/ED-318
url = https://github.com/UASGeoZones/ED-318.git
1 change: 1 addition & 0 deletions geospatial-utils/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ WORKDIR /app/geospatial-utils

# Add core content from repo
ADD ./geospatial-utils /app/geospatial-utils
ADD ./schemas /app/schemas

# Discover `geospatial-utils` module in Python
ENV PYTHONPATH=/app
Expand Down
20 changes: 10 additions & 10 deletions geospatial-utils/convert.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
from config import ED318Additions
from uas_standards.eurocae_ed269 import (
ApplicableTimePeriod,
ED269Schema,
HorizontalProjectionType,
Reason,
Restriction,
UASZoneAirspaceVolume,
UASZoneAuthority,
UomDimensions,
)
from uas_standards.eurocae_ed318 import (
Authority,
CodeVerticalReferenceType,
Expand All @@ -20,16 +30,6 @@
UomDistance,
VerticalLayer,
)
from uas_standards.eurocae_ed269 import (
ApplicableTimePeriod,
ED269Schema,
HorizontalProjectionType,
Reason,
Restriction,
UASZoneAirspaceVolume,
UASZoneAuthority,
UomDimensions,
)

COUNTRY_REGION_MAPPING = {"CHE": 0, "LIE": 27}

Expand Down
18 changes: 15 additions & 3 deletions geospatial-utils/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import config
import convert
import fileutils
import validate
from fileutils import ed269
from loguru import logger

Expand Down Expand Up @@ -44,12 +45,23 @@ def main():

ed269_data = ed269.loads(source)
# TODO: Move hard-coded configuration to a json file.
logger.warning("Additional data not provided in ED269 is hard-coded with Swiss FOCA information. This will be moved to a configurable file in the near future.")
logger.warning(
"Additional data not provided in ED269 is hard-coded with Swiss FOCA information. This will be moved to a configurable file in the near future."
)
ed318_data = convert.from_ed269_to_ed318(ed269_data, config=config.FOCA)
output = pathlib.Path(args.output_file)
output.write_text(json.dumps(ed318_data), encoding="utf-8")
json_output = json.dumps(ed318_data)
output.write_text(json_output, encoding="utf-8")
logger.debug(f"Successful conversion. File saved to: {output.absolute()}")

logger.info(f"Successful conversion. ED-318 saved to {output.absolute()}")
errors = validate.ed318(json.loads(json_output))
if len(errors) > 0:
for e in errors:
logger.error(f"{e.json_path}: {e.message}")
sys.exit(1)
logger.info(
f"Successful conversion and validation. ED-318 saved to {output.absolute()}"
)

else:
parser.print_help()
Expand Down
57 changes: 57 additions & 0 deletions geospatial-utils/validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Validation script extensively inspired from https://github.com/UASGeoZones/ED-318/blob/main/examples/validate_examples.py

import json
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any

import jsonschema
from referencing import Registry, Resource

SCHEMA_PATH = Path(os.path.dirname(__file__)).parent / "schemas/ED-318/schema"
ROOT_SCHEMA = Path(SCHEMA_PATH) / "Schema_GeoZones.json"


@dataclass
class ValidationErrorWithPath:
"""Error encountered while validating an instance against a schema."""

message: str
"""Validation error message."""

json_path: str
"""Location of the data causing the validation error."""


def _collect_errors(e: jsonschema.ValidationError) -> list[ValidationErrorWithPath]:
if e.context:
result: list[ValidationErrorWithPath] = []
for child in e.context:
result.extend(_collect_errors(child))
return result
else:
return [ValidationErrorWithPath(message=e.message, json_path=e.json_path)]


def _build_registry(schema_dir: Path) -> Registry:
def retrieve(uri: str):
local_ref = (schema_dir / Path(uri)).resolve()
return Resource.from_contents(json.loads(local_ref.read_text()))

registry: Registry = Registry(retrieve=retrieve)
return registry


def ed318(data: dict[str, Any]) -> list[ValidationErrorWithPath]:
"""Validate the data object using ED-318 jsonschemas"""
schema_content = json.loads(ROOT_SCHEMA.read_bytes())
registry = _build_registry(SCHEMA_PATH)
validator = jsonschema.Draft7Validator(schema=schema_content, registry=registry)
validator.check_schema(schema_content)

errors: list[ValidationErrorWithPath] = []
for e in validator.iter_errors(data): # type: ignore
errors.extend(_collect_errors(e))

return errors
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ readme = "README.md"
requires-python = "==3.13.5"
dependencies = [
"basedpyright>=1.31.1",
"jsonschema>=4.25.1",
"loguru>=0.7.3",
"requests>=2.32.5",
"ruff",
"types-jsonschema>=4.25.1.20250822",
"uas-standards>=4.1.0",
]

Expand All @@ -27,6 +29,7 @@ line-length = 88
typeCheckingMode = "standard"

exclude = [
"**/__pycache__",
"**/.*",
"**/__pycache__",
"**/.*",
"schemas/**",
]
1 change: 1 addition & 0 deletions schemas/ED-318
Submodule ED-318 added at e98b29
16 changes: 16 additions & 0 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.