Skip to content

Commit 9986f54

Browse files
committed
Workaround for ECMWF x1, y1, x2, y2 unsigned bug.
1 parent fa42186 commit 9986f54

File tree

2 files changed

+83
-5
lines changed

2 files changed

+83
-5
lines changed

lib/iris/fileformats/grib/_load_convert.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,21 @@ def fixup_float32_from_int32(value):
233233
return float(value_as_float32)
234234

235235

236+
def fixup_int32_from_uint32(value):
237+
"""
238+
Workaround for use when reading a signed, 4-byte integer which the
239+
ECMWF GRIB API has erroneously treated as an unsigned, 4-byte
240+
integer.
241+
242+
NB. This workaround is safe to use with values which are already
243+
treated as signed, 4-byte integers.
244+
245+
"""
246+
if value >= 0x80000000:
247+
value = 0x80000000 - value
248+
return value
249+
250+
236251
###############################################################################
237252
#
238253
# Identification Section 1
@@ -694,17 +709,24 @@ def grid_definition_template_12(section, metadata):
694709
cs = icoord_systems.TransverseMercator(lat, lon, easting, northing,
695710
scale, geog_cs)
696711

712+
# Deal with bug in ECMWF GRIB API (present at 1.12.1) where these
713+
# values are treated as unsigned, 4-byte integers.
714+
x1 = fixup_int32_from_uint32(section['x1'])
715+
y1 = fixup_int32_from_uint32(section['y1'])
716+
x2 = fixup_int32_from_uint32(section['x2'])
717+
y2 = fixup_int32_from_uint32(section['y2'])
718+
697719
# Rather unhelpfully this grid definition template seems to be
698720
# overspecified, and thus open to inconsistency.
699-
last_x = section['x1'] + (section['Ni'] - 1) * section['Di']
700-
last_y = section['y1'] + (section['Nj'] - 1) * section['Dj']
701-
if (last_x != section['x2'] or last_y != section['y2']):
721+
last_x = x1 + (section['Ni'] - 1) * section['Di']
722+
last_y = y1 + (section['Nj'] - 1) * section['Dj']
723+
if (last_x != x2 or last_y != y2):
702724
raise TranslationError('Inconsistent grid definition')
703725

704-
x1 = section['x1'] * CM_TO_M
726+
x1 = x1 * CM_TO_M
705727
dx = section['Di'] * CM_TO_M
706728
x_points = x1 + np.arange(section['Ni']) * dx
707-
y1 = section['y1'] * CM_TO_M
729+
y1 = y1 * CM_TO_M
708730
dy = section['Dj'] * CM_TO_M
709731
y_points = y1 + np.arange(section['Nj']) * dy
710732

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# (C) British Crown Copyright 2014, Met Office
2+
#
3+
# This file is part of Iris.
4+
#
5+
# Iris is free software: you can redistribute it and/or modify it under
6+
# the terms of the GNU Lesser General Public License as published by the
7+
# Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# Iris is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU Lesser General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU Lesser General Public License
16+
# along with Iris. If not, see <http://www.gnu.org/licenses/>.
17+
"""
18+
Unit tests for `iris.fileformats.grib._load_convert.fixup_int32_from_uint32`.
19+
20+
"""
21+
22+
from __future__ import (absolute_import, division, print_function)
23+
24+
# Import iris.tests first so that some things can be initialised before
25+
# importing anything else.
26+
import iris.tests as tests
27+
28+
from iris.fileformats.grib._load_convert import fixup_int32_from_uint32
29+
30+
31+
class Test(tests.IrisTest):
32+
def test_negative(self):
33+
result = fixup_int32_from_uint32(0x80000005)
34+
self.assertEqual(result, -5)
35+
36+
def test_negative_zero(self):
37+
result = fixup_int32_from_uint32(0x80000000)
38+
self.assertEqual(result, 0)
39+
40+
def test_zero(self):
41+
result = fixup_int32_from_uint32(0)
42+
self.assertEqual(result, 0)
43+
44+
def test_positive(self):
45+
result = fixup_int32_from_uint32(200000)
46+
self.assertEqual(result, 200000)
47+
48+
def test_already_negative(self):
49+
# If we *already* have a negative value the fixup routine should
50+
# leave it alone.
51+
result = fixup_int32_from_uint32(-7)
52+
self.assertEqual(result, -7)
53+
54+
55+
if __name__ == '__main__':
56+
tests.main()

0 commit comments

Comments
 (0)