Skip to content

Commit cb08cd1

Browse files
committed
First partial working w rotated message.
1 parent ce3dcdf commit cb08cd1

File tree

4 files changed

+320
-88
lines changed

4 files changed

+320
-88
lines changed

src/iris_grib/_grib1_convert.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Copyright iris-grib contributors
2+
#
3+
# This file is part of iris-grib and is released under the BSD license.
4+
# See LICENSE in the root of the repository for full licensing details.
5+
"""
6+
Module to support loading of GRIB1 data.
7+
8+
Converts a GRIB1 message into cube metadata.
9+
10+
This is a re-implementation of '._grib1_legacy.grib1_load_rules', but now using a
11+
:class:`iris_grib.message.GribMessage` in place of a
12+
:class:`iris_grib._grib1_legacy.grib_wrapper.GribWrapper`.
13+
"""
14+
15+
import numpy as np
16+
17+
from iris.coords import DimCoord
18+
from iris import coord_systems
19+
from iris.exceptions import TranslationError
20+
from iris.fileformats.rules import (
21+
ConversionMetadata,
22+
)
23+
24+
from iris_grib.message import GribMessage
25+
from iris_grib import grib_phenom_translation as gptx
26+
27+
_CENTRE_NAME_NUMBERS_BACKREFS = {
28+
"egrr": 74,
29+
"ecmwf": 98,
30+
}
31+
32+
33+
def phenomenon(field: GribMessage, metadata: ConversionMetadata):
34+
section1 = field.sections[1]
35+
36+
tables_version = section1["table2Version"]
37+
centre_name = section1["centre"]
38+
# It seems that, for GRIB1, we have no access to the original number here.
39+
centre_number = _CENTRE_NAME_NUMBERS_BACKREFS.get(centre_name, 0)
40+
param_number = section1["indicatorOfParameter"]
41+
42+
grib_code = gptx.GRIBCode(
43+
edition=1,
44+
table_version=tables_version,
45+
centre_number=centre_number,
46+
number=param_number,
47+
)
48+
metadata["attributes"]["GRIB_PARAM"] = grib_code
49+
50+
cf_data = gptx.grib1_phenom_to_cf_info(
51+
table2_version=tables_version,
52+
centre_number=centre_number,
53+
param_number=param_number,
54+
)
55+
standard_name, long_name, units = None, None, None
56+
if cf_data is not None:
57+
standard_name = cf_data.standard_name
58+
long_name = cf_data.standard_name or cf_data.long_name
59+
units = cf_data.units
60+
elif tables_version < 128:
61+
# built-in decodes...
62+
match param_number:
63+
case 11:
64+
standard_name = "air_temperature"
65+
units = "kelvin"
66+
67+
case 33:
68+
standard_name = "x_wind"
69+
units = "m s-1"
70+
71+
case 34:
72+
standard_name = "y_wind"
73+
units = "m s-1"
74+
75+
if tables_version >= 128 or (tables_version == 1 and param_number >= 128):
76+
long_name = f"UNKNOWN LOCAL PARAM {param_number}.{tables_version}"
77+
units = "???"
78+
79+
metadata["standard_name"] = standard_name
80+
metadata["long_name"] = long_name
81+
metadata["units"] = units
82+
83+
84+
def grid(field: GribMessage, metadata: ConversionMetadata):
85+
grid_type = field.sections[1]["gridDefinition"]
86+
section2 = field.sections[2]
87+
grid_type = section2["dataRepresentationType"]
88+
dim_coords_and_dims = metadata["dim_coords_and_dims"]
89+
match grid_type:
90+
case 10:
91+
# rotated latitude-longitude
92+
93+
# just pin this aspect for now
94+
scanning_code = section2["scanningMode"]
95+
scanning_code &= 7 # only 3 bits significant??
96+
# TODO: don't understand why we can get value=64
97+
# only 3 bits : bits 3-7 seem to be unspecified (upto 2015?)
98+
99+
if scanning_code != 0:
100+
raise TranslationError(
101+
f"bad scanning code: scanningMode={scanning_code}"
102+
)
103+
104+
res_comp_flags = section2["resolutionAndComponentFlags"]
105+
res_comp_flags &= 1 | 2 | 16
106+
# TODO: AGAIN don't understand why we can get value=64
107+
# only 4 bits : bits 3-4 and 6-7 unspecified (upto 2015?)
108+
109+
if res_comp_flags != 0:
110+
raise TranslationError(
111+
f"bad grid scanning: resolutionAndComponentFlags={scanning_code}"
112+
)
113+
114+
spole_lon = 0.001 * section2["longitudeOfSouthernPole"]
115+
spole_lat = 0.001 * section2["latitudeOfSouthernPole"]
116+
rot_ang = section2["angleOfRotationInDegrees"]
117+
geoid = coord_systems.GeogCS(semi_major_axis=6367470)
118+
coord_system = coord_systems.RotatedGeogCS(
119+
-spole_lat,
120+
(spole_lon + 180.0) % 360.0,
121+
rot_ang,
122+
geoid,
123+
)
124+
125+
nlats = section2["Nj"]
126+
y0 = 0.001 * section2["latitudeOfFirstGridPoint"]
127+
dy = 0.001 * section2["jDirectionIncrement"]
128+
y_points = np.arange(nlats, dtype=np.float64) * dy + y0
129+
dim_coords_and_dims.append(
130+
(
131+
DimCoord(
132+
y_points,
133+
"grid_latitude",
134+
units="degrees",
135+
coord_system=coord_system,
136+
),
137+
0,
138+
)
139+
)
140+
141+
nlons = section2["Ni"]
142+
x0 = 0.001 * section2["longitudeOfFirstGridPoint"]
143+
dx = 0.001 * section2["iDirectionIncrement"]
144+
x_points = np.arange(nlons, dtype=np.float64) * dx + x0
145+
dim_coords_and_dims.append(
146+
(
147+
DimCoord(
148+
x_points,
149+
"grid_longitude",
150+
units="degrees",
151+
coord_system=coord_system,
152+
),
153+
1,
154+
)
155+
)
156+
157+
case _:
158+
msg = (
159+
"Unsupported grid type in grib message: "
160+
f"dataRepresentationType={grid_type}"
161+
)
162+
raise TranslationError(msg)
163+
164+
165+
def grib1_convert(field: GribMessage, metadata: ConversionMetadata):
166+
assert hasattr(field, "sections")
167+
assert field.sections[0]["editionNumber"] == 1
168+
169+
# Section 1 == product definitions
170+
phenomenon(field, metadata)
171+
grid(field, metadata)

src/iris_grib/_load_convert.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,11 @@ def convert(field):
3434
"""
3535
from ._grib1_legacy.grib1_load_rules import grib1_convert as old_grib1_convert # noqa: PLC0415
3636
from ._grib2_convert import grib2_convert # noqa: PLC0415
37+
from ._grib1_convert import grib1_convert as new_grib1_convert # noqa: PLC0415
3738

3839
if hasattr(field, "sections"):
3940
editionNumber = field.sections[0]["editionNumber"]
4041

41-
if editionNumber != 2:
42-
emsg = "GRIB edition {} is not supported by {!r}."
43-
raise TranslationError(emsg.format(editionNumber, type(field).__name__))
44-
4542
# Initialise the cube metadata.
4643
metadata = OrderedDict()
4744
metadata["factories"] = []
@@ -55,7 +52,13 @@ def convert(field):
5552
metadata["aux_coords_and_dims"] = []
5653

5754
# Convert GRIB2 message to cube metadata.
58-
grib2_convert(field, metadata)
55+
if editionNumber == 2:
56+
grib2_convert(field, metadata)
57+
elif editionNumber == 1:
58+
new_grib1_convert(field, metadata)
59+
else:
60+
emsg = "GRIB edition {} is not supported by {!r}."
61+
raise TranslationError(emsg.format(editionNumber, type(field).__name__))
5962

6063
result = ConversionMetadata._make(metadata.values())
6164
else:

0 commit comments

Comments
 (0)