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

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[typing.Any, numpy.dtype[numpy.float32]], lonin: numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]], aor: numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]], splat: numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]], splon: numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]) -> tuple[numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]], numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]]:
 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

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.