Skip to content
Draft
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
62 changes: 62 additions & 0 deletions api/BUFR_out.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# E-SOH BUFR Output Format Sequence

E-SOH provides a BUFR output file when you select the BUFR format from the dropdown menu. The following table describes the BUFR sequence.

| Descriptor | Extended/Repeated | Name | Data Source | Note |
|------------|-------------------|------|-------------|------|
|||
||| **STATION IDENTIFICATION**
301150 || WIGOS identifier | metocean:wigosId | Mandatory
|| 0 01 125 | WIGOS identifier series
|| 0 01 126 | WIGOS issuer of identifier
|| 0 01 127 | WIGOS issue number
|| 0 01 128 | WIGOS local identifier (character)
301090 || Surface station identification || Mandatory
|| 3 01 004 | Surface station identification
|| 3 01 011 | Year, month, day
|| 3 01 012 | Hour, minute
|| 3 01 021 | Latitude/longitude (high accuracy)
|| 0 07 030 | Height of station ground above mean sea level
|| 0 07 031 | Height of barometer above mean sea level
||| **BASIC SURFACE OBSERVATIONS**
302031 || Pressure information | air_pressure, air_pressure_at_mean_sea_level | Optional
302032 || Temperature and humidity data | air_temperature, dew_point_temperature, relative_humidity | Optional
302033 || Visibility data || Optional
|||
||| **EXTREME TEMPERATURES** (Optional)
105000 || Replication descriptor
031001 || Delayed descriptor replication factor
|| 007032 | Height of sensor above local ground
|| 004025 | Time period or displacement
|| 012111 | Maximum temperature, at height and over period specified | air_temperature
|| 004025 | Time period or displacement
|| 012112 | Minimum temperature, at height and over period specified | air_temperature
|||
||| **WIND DATA** (Optional)
302042 ||| wind_speed, wind_from_direction, wind_speed_of_gust, wind_gust_from_direction
|||
||| **RADIATION** (Optional)
106000 || Replication descriptor
031101 || Delayed descriptor replication factor
|| 007032 | Height of sensor above local ground
|| 004025 | Time period or displacement
|| 014002 | Long-wave radiation, integrated over period specified | integral_wrt_time_of_surface_downwelling_longwave_flux_in_air
|| 014004 | Short-wave radiation, integrated over period specified | integral_wrt_time_of_surface_downwelling_shortwave_flux_in_air
|| 014012 | Net long-wave radiation, integrated over period specified | integral_wrt_time_of_surface_net_downward_longwave_flux
|| 014014 | Net short-wave radiation, integrated over period specified | integral_wrt_time_of_surface_net_downward_shortwave_flux
|||
||| **RADIATION** (Optional)
106000 || Replication descriptor
031101 || Delayed descriptor replication factor
|| 007032 | Height of sensor above local ground
|| 014002 | Downward long-wave radiation, integrated over period specified | integral_wrt_time_of_surface_net_downward_longwave_flux | Positive
|| 014002 | Upward long-wave radiation, integrated over period specified | integral_wrt_time_of_surface_net_downward_longwave_flux | Negative
|| 014004 | Downward short-wave radiation, integrated over period specified | integral_wrt_time_of_surface_downwelling_shortwave_flux_in_air | Positive
|| 014004 | Upward short-wave radiation, integrated over period specified | integral_wrt_time_of_surface_downwelling_shortwave_flux_in_air | Negative
|||
||| **PRECIPITATION** (Optional)
103000 || Replication descriptor
031001 || Delayed descriptor replication factor
|| 007032 | Height of sensor above local ground
|| 004025 | Time period or displacement
|| 013011 | Total precipitation/total, water equivalent | precipitation_amount
22 changes: 11 additions & 11 deletions api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@ SHELL ["/bin/bash", "-eux", "-o", "pipefail", "-c"]
ENV DOCKER_PATH="/app"

RUN apt-get update \
&& apt-get -y upgrade \
&& apt-get install -y --no-install-recommends git curl \
# Cleanup
&& rm -rf /usr/tmp \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
&& apt-get -y upgrade \
&& apt-get install -y --no-install-recommends git curl libeccodes-data \
# Cleanup
&& rm -rf /usr/tmp \
&& apt-get autoremove -y \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

COPY "./protobuf/datastore.proto" "/protobuf/datastore.proto"
COPY "./requirements.txt" "${DOCKER_PATH}/requirements.txt"

# hadolint ignore=DL3013
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir --upgrade -r "${DOCKER_PATH}/requirements.txt"
&& pip install --no-cache-dir --upgrade -r "${DOCKER_PATH}/requirements.txt"

# Compiling the protobuf file
RUN python -m grpc_tools.protoc \
--proto_path="protobuf" "protobuf/datastore.proto" \
--python_out="${DOCKER_PATH}" \
--grpc_python_out="${DOCKER_PATH}"
--proto_path="protobuf" "protobuf/datastore.proto" \
--python_out="${DOCKER_PATH}" \
--grpc_python_out="${DOCKER_PATH}"

COPY "." "${DOCKER_PATH}/"

Expand Down
62 changes: 33 additions & 29 deletions api/dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ annotated-types==0.7.0
# via
# -r requirements.txt
# pydantic
anyio==4.9.0
anyio==4.12.1
# via
# -r requirements.txt
# httpx
# starlette
# watchfiles
brotli==1.1.0
brotli==1.2.0
# via
# -r requirements.txt
# brotli-asgi
brotli-asgi==1.4.0
brotli-asgi==1.6.0
# via -r requirements.txt
certifi==2025.4.26
# via
# httpcore
# httpx
click==8.2.0
click==8.3.1
# via
# -r requirements.txt
# uvicorn
Expand All @@ -36,15 +36,15 @@ deepdiff==7.0.1
# via -r dev_requirements.in
edr-pydantic==0.7.0
# via -r requirements.txt
fastapi==0.115.12
fastapi==0.115.14
# via -r requirements.txt
geojson-pydantic==1.2.0
# via -r requirements.txt
grpcio==1.71.0
grpcio==1.78.0
# via
# -r requirements.txt
# grpcio-tools
grpcio-tools==1.71.0
grpcio-tools==1.78.0
# via -r requirements.txt
gunicorn==23.0.0
# via -r requirements.txt
Expand All @@ -55,13 +55,13 @@ h11==0.16.0
# uvicorn
httpcore==1.0.9
# via httpx
httptools==0.6.4
httptools==0.7.1
# via
# -r requirements.txt
# uvicorn
httpx==0.27.2
# via -r dev_requirements.in
idna==3.10
idna==3.11
# via
# -r requirements.txt
# anyio
Expand All @@ -72,45 +72,49 @@ isodate==0.7.2
# via -r requirements.txt
jinja2==3.1.6
# via -r requirements.txt
markupsafe==3.0.2
markupsafe==3.0.3
# via
# -r requirements.txt
# jinja2
numpy==2.2.6
numpy==2.4.3
# via
# -r requirements.txt
# shapely
ordered-set==4.1.0
# via deepdiff
packaging==25.0
packaging==26.0
# via
# -r requirements.txt
# gunicorn
# pytest
pluggy==1.6.0
# via pytest
prometheus-client==0.22.0
prometheus-client==0.24.1
# via
# -r requirements.txt
# prometheus-fastapi-instrumentator
prometheus-fastapi-instrumentator==7.0.2
# via -r requirements.txt
protobuf==5.29.4
protobuf==6.33.6
# via
# -r requirements.txt
# grpcio-tools
pydantic==2.11.4
pybind11==2.11.2
# via
# -r requirements.txt
# rodeo-bufr-tools
pydantic==2.12.5
# via
# -r requirements.txt
# covjson-pydantic
# edr-pydantic
# fastapi
# geojson-pydantic
pydantic-core==2.33.2
pydantic-core==2.41.5
# via
# -r requirements.txt
# pydantic
pyparsing==3.2.3
pyparsing==3.3.2
# via
# -r requirements.txt
# rdflib
Expand All @@ -123,53 +127,53 @@ pytest-cov==5.0.0
# via -r dev_requirements.in
pytest-timeout==2.4.0
# via -r dev_requirements.in
python-dotenv==1.1.0
python-dotenv==1.2.2
# via
# -r requirements.txt
# uvicorn
pyyaml==6.0.2
pyyaml==6.0.3
# via
# -r requirements.txt
# uvicorn
rdflib==7.1.4
# via -r requirements.txt
shapely==2.1.1
rodeo-bufr-tools==0.4.8
# via -r requirements.txt
shapely==2.1.2
# via -r requirements.txt
sniffio==1.3.1
# via
# -r requirements.txt
# anyio
# httpx
# via httpx
starlette==0.46.2
# via
# -r requirements.txt
# brotli-asgi
# fastapi
# prometheus-fastapi-instrumentator
typing-extensions==4.13.2
typing-extensions==4.15.0
# via
# -r requirements.txt
# anyio
# edr-pydantic
# fastapi
# grpcio
# pydantic
# pydantic-core
# typing-inspection
typing-inspection==0.4.0
typing-inspection==0.4.2
# via
# -r requirements.txt
# pydantic
uvicorn[standard]==0.32.1
# via -r requirements.txt
uvloop==0.21.0
uvloop==0.22.1
# via
# -r requirements.txt
# uvicorn
watchfiles==1.0.5
watchfiles==1.1.1
# via
# -r requirements.txt
# uvicorn
websockets==15.0.1
websockets==16.0
# via
# -r requirements.txt
# uvicorn
Expand Down
11 changes: 10 additions & 1 deletion api/formatters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,28 @@

from . import covjson
from . import geojson
from . import bufr

logger = logging.getLogger(__name__)


class Formats(str, Enum):
covjson = "CoverageJSON" # According to EDR spec
bufr = "bufr"


class Metadata_Formats(str, Enum):
geojson = "GeoJSON"


formatters = {
"CoverageJSON": covjson.convert_to_covjson,
"CoverageJSON": {
"format_function": covjson.convert_to_covjson,
"response_format": "application/prs.coverage+json",
},
"bufr": {
"format_function": bufr.convert_to_bufr,
"response_format": "application/bufr",
},
} # observations
metadata_formatters = {"GeoJSON": geojson.convert_to_geojson} # metadata
13 changes: 13 additions & 0 deletions api/formatters/bufr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from . import covjson

from bufr_tools import covjson2bufr


def convert_to_bufr(raw_data: str):
cov_json = covjson.convert_to_covjson(raw_data)
print(type(cov_json))
bufr_content = covjson2bufr.covjson2bufr(cov_json)

if not bufr_content and not cov_json:
raise ValueError("No content")
return bufr_content
17 changes: 13 additions & 4 deletions api/formatters/covjson.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,10 @@ def convert_to_covjson(observations):
referencing = [
ReferenceSystemConnectionObject(
coordinates=["x", "y"],
system=ReferenceSystem(type="GeographicCRS", id="http://www.opengis.net/def/crs/OGC/1.3/CRS84"),
system=ReferenceSystem(
type="GeographicCRS",
id="http://www.opengis.net/def/crs/OGC/1.3/CRS84",
),
),
ReferenceSystemConnectionObject(
coordinates=["t"],
Expand Down Expand Up @@ -106,7 +109,9 @@ def convert_to_covjson(observations):
parameters[parameter_id] = make_parameter(data.ts_mdata)

ranges[parameter_id] = NdArrayFloat(
values=values_no_nan, axisNames=["t", "x", "y"], shape=[len(values_no_nan), 1, 1]
values=values_no_nan,
axisNames=["t", "x", "y"],
shape=[len(values_no_nan), 1, 1],
)

custom_fields = {"metocean:wigosId": data.ts_mdata.platform}
Expand All @@ -115,10 +120,14 @@ def convert_to_covjson(observations):
if len(coverages) == 0:
raise HTTPException(status_code=404, detail="Requested data not found.")
elif len(coverages) == 1:
return coverages[0]
return coverages[0].model_dump_json(exclude_none=True)
else:
parameter_union = reduce(operator.ior, (c.parameters.root for c in coverages), {})
return CoverageCollection(coverages=coverages, parameters=dict(sorted(parameter_union.items())))
return CoverageCollection(
coverages=coverages, parameters=dict(sorted(parameter_union.items()))
).model_dump_json(
exclude_none=True
) # (mode="json")


def _collect_data(ts_mdata, obs_mdata):
Expand Down
1 change: 1 addition & 0 deletions api/requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ jinja2~=3.1
isodate~=0.7.2
prometheus-fastapi-instrumentator~=7.0.0
rdflib~=7.1.4
rodeo-bufr-tools~=0.4.8
Loading
Loading