grib2io.utils.rotated_grid

Tools for working with Rotated Lat/Lon Grids.

  1"""Tools for working with Rotated Lat/Lon Grids."""
  2
  3import numpy as np
  4from numpy.typing import NDArray
  5
  6RAD2DEG = 57.29577951308232087684
  7DEG2RAD = 0.01745329251994329576
  8
  9
 10def rotate(
 11    latin: NDArray[np.float32],
 12    lonin: NDArray[np.float32],
 13    aor: NDArray[np.float32],
 14    splat: NDArray[np.float32],
 15    splon: NDArray[np.float32],
 16) -> tuple[NDArray[np.float32], NDArray[np.float32]]:
 17    """
 18    Perform grid rotation.
 19
 20    This function is adapted from ECMWF's ecCodes library void function,
 21    rotate().
 22
 23    https://github.com/ecmwf/eccodes/blob/develop/src/grib_geography.cc
 24
 25    Parameters
 26    ----------
 27    latin
 28        Latitudes in units of degrees.
 29    lonin
 30        Longitudes in units of degrees.
 31    aor
 32        Angle of rotation as defined in GRIB2 GDTN 4.1.
 33    splat
 34        Latitude of South Pole as defined in GRIB2 GDTN 4.1.
 35    splon
 36        Longitude of South Pole as defined in GRIB2 GDTN 4.1.
 37
 38    Returns
 39    -------
 40    lats
 41        `numpy.ndarrays` with `dtype=numpy.float32` of grid latitudes in units
 42        of degrees.
 43    lons
 44        `numpy.ndarrays` with `dtype=numpy.float32` of grid longitudes in units
 45        of degrees.
 46    """
 47    zsycen = np.sin(DEG2RAD * (splat + 90.0))
 48    zcycen = np.cos(DEG2RAD * (splat + 90.0))
 49    zxmxc = DEG2RAD * (lonin - splon)
 50    zsxmxc = np.sin(zxmxc)
 51    zcxmxc = np.cos(zxmxc)
 52    zsyreg = np.sin(DEG2RAD * latin)
 53    zcyreg = np.cos(DEG2RAD * latin)
 54    zsyrot = zcycen * zsyreg - zsycen * zcyreg * zcxmxc
 55
 56    zsyrot = np.where(zsyrot > 1.0, 1.0, zsyrot)
 57    zsyrot = np.where(zsyrot < -1.0, -1.0, zsyrot)
 58
 59    pyrot = np.arcsin(zsyrot) * RAD2DEG
 60
 61    zcyrot = np.cos(pyrot * DEG2RAD)
 62    zcxrot = (zcycen * zcyreg * zcxmxc + zsycen * zsyreg) / zcyrot
 63    zcxrot = np.where(zcxrot > 1.0, 1.0, zcxrot)
 64    zcxrot = np.where(zcxrot < -1.0, -1.0, zcxrot)
 65    zsxrot = zcyreg * zsxmxc / zcyrot
 66
 67    pxrot = np.arccos(zcxrot) * RAD2DEG
 68
 69    pxrot = np.where(zsxrot < 0.0, -pxrot, pxrot)
 70
 71    return pyrot, pxrot
 72
 73
 74def unrotate(
 75    latin: NDArray[np.float32],
 76    lonin: NDArray[np.float32],
 77    aor: NDArray[np.float32],
 78    splat: NDArray[np.float32],
 79    splon: NDArray[np.float32],
 80) -> tuple[NDArray[np.float32], NDArray[np.float32]]:
 81    """
 82    Perform grid un-rotation.
 83
 84    This function is adapted from ECMWF's ecCodes library void function,
 85    unrotate().
 86
 87    https://github.com/ecmwf/eccodes/blob/develop/src/grib_geography.cc
 88
 89    Parameters
 90    ----------
 91    latin
 92        Latitudes in units of degrees.
 93    lonin
 94        Longitudes in units of degrees.
 95    aor
 96        Angle of rotation as defined in GRIB2 GDTN 4.1.
 97    splat
 98        Latitude of South Pole as defined in GRIB2 GDTN 4.1.
 99    splon
100        Longitude of South Pole as defined in GRIB2 GDTN 4.1.
101
102    Returns
103    -------
104    lats
105        `numpy.ndarrays` with `dtype=numpy.float32` of grid latitudes in units
106        of degrees.
107    lons
108        `numpy.ndarrays` with `dtype=numpy.float32` of grid longitudes in units
109        of degrees.
110    """
111    lon_x = lonin
112    lat_y = latin
113
114    latr = lat_y * DEG2RAD
115    lonr = lon_x * DEG2RAD
116
117    xd = np.cos(lonr) * np.cos(latr)
118    yd = np.sin(lonr) * np.cos(latr)
119    zd = np.sin(latr)
120
121    t = -(90.0 + splat)
122    o = -splon
123
124    sin_t = np.sin(DEG2RAD * t)
125    cos_t = np.cos(DEG2RAD * t)
126    sin_o = np.sin(DEG2RAD * o)
127    cos_o = np.cos(DEG2RAD * o)
128
129    x = cos_t * cos_o * xd + sin_o * yd + sin_t * cos_o * zd
130    y = -cos_t * sin_o * xd + cos_o * yd - sin_t * sin_o * zd
131    z = -sin_t * xd + cos_t * zd
132
133    ret_lat = 0
134    ret_lon = 0
135
136    # Then convert back to 'normal' (lat,lon)
137    # Uses arcsin, to convert back to degrees, put in range -1 to 1 in case of slight rounding error
138    # avoid error on calculating e.g. asin(1.00000001)
139    z = np.where(z > 1.0, 1.0, z)
140    z = np.where(z < -1.0, -1.0, z)
141
142    ret_lat = np.arcsin(z) * RAD2DEG
143    ret_lon = np.arctan2(y, x) * RAD2DEG
144
145    # Still get a very small rounding error, round to 6 decimal places
146    ret_lat = np.round(ret_lat * 1000000.0) / 1000000.0
147    ret_lon = np.round(ret_lon * 1000000.0) / 1000000.0
148
149    ret_lon -= aor
150
151    return ret_lat, ret_lon
RAD2DEG = 57.29577951308232
DEG2RAD = 0.017453292519943295
def rotate( latin: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], lonin: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], aor: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], splat: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], splon: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]]) -> tuple[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]]]:
11def rotate(
12    latin: NDArray[np.float32],
13    lonin: NDArray[np.float32],
14    aor: NDArray[np.float32],
15    splat: NDArray[np.float32],
16    splon: NDArray[np.float32],
17) -> tuple[NDArray[np.float32], NDArray[np.float32]]:
18    """
19    Perform grid rotation.
20
21    This function is adapted from ECMWF's ecCodes library void function,
22    rotate().
23
24    https://github.com/ecmwf/eccodes/blob/develop/src/grib_geography.cc
25
26    Parameters
27    ----------
28    latin
29        Latitudes in units of degrees.
30    lonin
31        Longitudes in units of degrees.
32    aor
33        Angle of rotation as defined in GRIB2 GDTN 4.1.
34    splat
35        Latitude of South Pole as defined in GRIB2 GDTN 4.1.
36    splon
37        Longitude of South Pole as defined in GRIB2 GDTN 4.1.
38
39    Returns
40    -------
41    lats
42        `numpy.ndarrays` with `dtype=numpy.float32` of grid latitudes in units
43        of degrees.
44    lons
45        `numpy.ndarrays` with `dtype=numpy.float32` of grid longitudes in units
46        of degrees.
47    """
48    zsycen = np.sin(DEG2RAD * (splat + 90.0))
49    zcycen = np.cos(DEG2RAD * (splat + 90.0))
50    zxmxc = DEG2RAD * (lonin - splon)
51    zsxmxc = np.sin(zxmxc)
52    zcxmxc = np.cos(zxmxc)
53    zsyreg = np.sin(DEG2RAD * latin)
54    zcyreg = np.cos(DEG2RAD * latin)
55    zsyrot = zcycen * zsyreg - zsycen * zcyreg * zcxmxc
56
57    zsyrot = np.where(zsyrot > 1.0, 1.0, zsyrot)
58    zsyrot = np.where(zsyrot < -1.0, -1.0, zsyrot)
59
60    pyrot = np.arcsin(zsyrot) * RAD2DEG
61
62    zcyrot = np.cos(pyrot * DEG2RAD)
63    zcxrot = (zcycen * zcyreg * zcxmxc + zsycen * zsyreg) / zcyrot
64    zcxrot = np.where(zcxrot > 1.0, 1.0, zcxrot)
65    zcxrot = np.where(zcxrot < -1.0, -1.0, zcxrot)
66    zsxrot = zcyreg * zsxmxc / zcyrot
67
68    pxrot = np.arccos(zcxrot) * RAD2DEG
69
70    pxrot = np.where(zsxrot < 0.0, -pxrot, pxrot)
71
72    return pyrot, pxrot

Perform grid rotation.

This function is adapted from ECMWF's ecCodes library void function, rotate().

https://github.com/ecmwf/eccodes/blob/develop/src/grib_geography.cc

Parameters
  • latin: Latitudes in units of degrees.
  • lonin: Longitudes in units of degrees.
  • aor: Angle of rotation as defined in GRIB2 GDTN 4.1.
  • splat: Latitude of South Pole as defined in GRIB2 GDTN 4.1.
  • splon: Longitude of South Pole as defined in GRIB2 GDTN 4.1.
Returns
  • lats: numpy.ndarrays with dtype=numpy.float32 of grid latitudes in units of degrees.
  • lons: numpy.ndarrays with dtype=numpy.float32 of grid longitudes in units of degrees.
def unrotate( latin: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], lonin: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], aor: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], splat: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], splon: numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]]) -> tuple[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]], numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float32]]]:
 75def unrotate(
 76    latin: NDArray[np.float32],
 77    lonin: NDArray[np.float32],
 78    aor: NDArray[np.float32],
 79    splat: NDArray[np.float32],
 80    splon: NDArray[np.float32],
 81) -> tuple[NDArray[np.float32], NDArray[np.float32]]:
 82    """
 83    Perform grid un-rotation.
 84
 85    This function is adapted from ECMWF's ecCodes library void function,
 86    unrotate().
 87
 88    https://github.com/ecmwf/eccodes/blob/develop/src/grib_geography.cc
 89
 90    Parameters
 91    ----------
 92    latin
 93        Latitudes in units of degrees.
 94    lonin
 95        Longitudes in units of degrees.
 96    aor
 97        Angle of rotation as defined in GRIB2 GDTN 4.1.
 98    splat
 99        Latitude of South Pole as defined in GRIB2 GDTN 4.1.
100    splon
101        Longitude of South Pole as defined in GRIB2 GDTN 4.1.
102
103    Returns
104    -------
105    lats
106        `numpy.ndarrays` with `dtype=numpy.float32` of grid latitudes in units
107        of degrees.
108    lons
109        `numpy.ndarrays` with `dtype=numpy.float32` of grid longitudes in units
110        of degrees.
111    """
112    lon_x = lonin
113    lat_y = latin
114
115    latr = lat_y * DEG2RAD
116    lonr = lon_x * DEG2RAD
117
118    xd = np.cos(lonr) * np.cos(latr)
119    yd = np.sin(lonr) * np.cos(latr)
120    zd = np.sin(latr)
121
122    t = -(90.0 + splat)
123    o = -splon
124
125    sin_t = np.sin(DEG2RAD * t)
126    cos_t = np.cos(DEG2RAD * t)
127    sin_o = np.sin(DEG2RAD * o)
128    cos_o = np.cos(DEG2RAD * o)
129
130    x = cos_t * cos_o * xd + sin_o * yd + sin_t * cos_o * zd
131    y = -cos_t * sin_o * xd + cos_o * yd - sin_t * sin_o * zd
132    z = -sin_t * xd + cos_t * zd
133
134    ret_lat = 0
135    ret_lon = 0
136
137    # Then convert back to 'normal' (lat,lon)
138    # Uses arcsin, to convert back to degrees, put in range -1 to 1 in case of slight rounding error
139    # avoid error on calculating e.g. asin(1.00000001)
140    z = np.where(z > 1.0, 1.0, z)
141    z = np.where(z < -1.0, -1.0, z)
142
143    ret_lat = np.arcsin(z) * RAD2DEG
144    ret_lon = np.arctan2(y, x) * RAD2DEG
145
146    # Still get a very small rounding error, round to 6 decimal places
147    ret_lat = np.round(ret_lat * 1000000.0) / 1000000.0
148    ret_lon = np.round(ret_lon * 1000000.0) / 1000000.0
149
150    ret_lon -= aor
151
152    return ret_lat, ret_lon

Perform grid un-rotation.

This function is adapted from ECMWF's ecCodes library void function, unrotate().

https://github.com/ecmwf/eccodes/blob/develop/src/grib_geography.cc

Parameters
  • latin: Latitudes in units of degrees.
  • lonin: Longitudes in units of degrees.
  • aor: Angle of rotation as defined in GRIB2 GDTN 4.1.
  • splat: Latitude of South Pole as defined in GRIB2 GDTN 4.1.
  • splon: Longitude of South Pole as defined in GRIB2 GDTN 4.1.
Returns
  • lats: numpy.ndarrays with dtype=numpy.float32 of grid latitudes in units of degrees.
  • lons: numpy.ndarrays with dtype=numpy.float32 of grid longitudes in units of degrees.