Skip to content

Commit

Permalink
ENH: Added PoleRotationNetCDFCFConversion
Browse files Browse the repository at this point in the history
  • Loading branch information
snowman2 committed Oct 9, 2021
1 parent 75f1edc commit 440ad03
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 13 deletions.
9 changes: 9 additions & 0 deletions docs/api/crs/coordinate_operation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,15 @@ RotatedLatitudeLongitudeConversion
:special-members: __new__


PoleRotationNetCDFCFConversion
----------------------------------

.. autoclass:: pyproj.crs.coordinate_operation.PoleRotationNetCDFCFConversion
:members:
:show-inheritance:
:special-members: __new__


ToWGS84Transformation
---------------------

Expand Down
2 changes: 2 additions & 0 deletions docs/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ Latest
- DEP: Minimum PROJ version 8.0 (issue #940)
- BUG: Prepend "Derived" to CRS type name if CRS is derived (issue #932)
- BUG: Improved handling of inf values in :meth:`pyproj.transformer.Transformer.transform_bounds` (pull #961)
- BUG: CRS CF conversions mismatch of PROJ parameters in rotated pole (issue #948)
- ENH: Add support for transforming bounds at the poles in :meth:`pyproj.transformer.Transformer.transform_bounds` (pull #962)
- ENH: Added :attr:`pyproj.transformer.Transformer.source_crs` & :attr:`pyproj.transformer.Transformer.target_crs` (pull #976)
- ENH: Added :class:`pyproj.crs.coordinate_operation.PoleRotationNetCDFCFConversion` (issue #948)

3.2.1
------
Expand Down
1 change: 1 addition & 0 deletions pyproj/_transformer.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ from pyproj.exceptions import ProjError

# version number string for PROJ
proj_version_str = f"{PROJ_VERSION_MAJOR}.{PROJ_VERSION_MINOR}.{PROJ_VERSION_PATCH}"
PROJ_VERSION = (PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR, PROJ_VERSION_PATCH)
_AUTH_CODE_RE = re.compile(r"(?P<authority>\w+)\:(?P<code>\w+)")


Expand Down
15 changes: 7 additions & 8 deletions pyproj/crs/_cf1x8.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
OrthographicConversion,
PolarStereographicAConversion,
PolarStereographicBConversion,
RotatedLatitudeLongitudeConversion,
PoleRotationNetCDFCFConversion,
SinusoidalConversion,
StereographicConversion,
TransverseMercatorConversion,
Expand Down Expand Up @@ -333,11 +333,10 @@ def _rotated_latitude_longitude(cf_params):
"""
http://cfconventions.org/cf-conventions/cf-conventions.html#_rotated_pole
"""
return RotatedLatitudeLongitudeConversion(
o_lat_p=cf_params["grid_north_pole_latitude"],
o_lon_p=cf_params["grid_north_pole_longitude"],
# https://github.com/pyproj4/pyproj/issues/927
lon_0=cf_params.get("north_pole_grid_longitude", 0.0) + 180,
return PoleRotationNetCDFCFConversion(
grid_north_pole_latitude=cf_params["grid_north_pole_latitude"],
grid_north_pole_longitude=cf_params["grid_north_pole_longitude"],
north_pole_grid_longitude=cf_params.get("north_pole_grid_longitude", 0.0),
)


Expand Down Expand Up @@ -626,9 +625,9 @@ def _rotated_latitude_longitude__to_cf(conversion):
return {
"grid_mapping_name": "rotated_latitude_longitude",
"grid_north_pole_latitude": params["o_lat_p"],
"grid_north_pole_longitude": params["o_lon_p"],
# https://github.com/pyproj4/pyproj/issues/927
"north_pole_grid_longitude": params["lon_0"] - 180,
"grid_north_pole_longitude": params["lon_0"] - 180,
"north_pole_grid_longitude": params["o_lon_p"],
}


Expand Down
65 changes: 64 additions & 1 deletion pyproj/crs/coordinate_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from typing import Any

from pyproj._crs import CoordinateOperation
from pyproj._transformer import PROJ_VERSION
from pyproj.exceptions import CRSError


Expand Down Expand Up @@ -1444,7 +1445,7 @@ def __new__(cls, o_lat_p: float, o_lon_p: float, lon_0: float = 0.0):
Longitude of the North pole of the unrotated source CRS,
expressed in the rotated geographic CRS.
lon_0: float, default=0.0
Longitude of projection center (lon_0).
Longitude of projection center.
"""
rot_latlon_json = {
Expand All @@ -1461,6 +1462,68 @@ def __new__(cls, o_lat_p: float, o_lon_p: float, lon_0: float = 0.0):
return cls.from_json_dict(rot_latlon_json)


class PoleRotationNetCDFCFConversion(CoordinateOperation):
"""
.. versionadded:: 3.3.0
Class for constructing the Pole rotation (netCDF CF convention) conversion.
http://cfconventions.org/cf-conventions/cf-conventions.html#_rotated_pole
:ref:`PROJ docs <ob_tran>`
"""

def __new__(
cls,
grid_north_pole_latitude: float,
grid_north_pole_longitude: float,
north_pole_grid_longitude: float = 0.0,
):
"""
Parameters
----------
grid_north_pole_latitude: float
Latitude of the North pole of the unrotated source CRS,
expressed in the rotated geographic CRS (o_lat_p)
grid_north_pole_longitude: float
Longitude of projection center (lon_0 - 180).
north_pole_grid_longitude: float, default=0.0
Longitude of the North pole of the unrotated source CRS,
expressed in the rotated geographic CRS (o_lon_p).
"""
if PROJ_VERSION < (8, 2, 0):
# https://github.com/pyproj4/pyproj/issues/927
return RotatedLatitudeLongitudeConversion(
o_lat_p=grid_north_pole_latitude,
o_lon_p=north_pole_grid_longitude,
lon_0=grid_north_pole_longitude + 180,
)
rot_latlon_json = {
"$schema": "https://proj.org/schemas/v0.4/projjson.schema.json",
"type": "Conversion",
"name": "Pole rotation (netCDF CF convention)",
"method": {"name": "Pole rotation (netCDF CF convention)"},
"parameters": [
{
"name": "Grid north pole latitude (netCDF CF convention)",
"value": grid_north_pole_latitude,
"unit": "degree",
},
{
"name": "Grid north pole longitude (netCDF CF convention)",
"value": grid_north_pole_longitude,
"unit": "degree",
},
{
"name": "North pole grid longitude (netCDF CF convention)",
"value": north_pole_grid_longitude,
"unit": "degree",
},
],
}
return cls.from_json_dict(rot_latlon_json)


class EquidistantCylindricalConversion(CoordinateOperation):
"""
.. versionadded:: 2.5.0
Expand Down
8 changes: 4 additions & 4 deletions test/crs/test_crs_cf.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,8 @@ def test_cf_rotated_latlon():
"proj": "ob_tran",
"o_proj": "longlat",
"o_lat_p": 32.5,
"o_lon_p": 170.0,
"lon_0": 180,
"o_lon_p": 0,
"lon_0": 350,
"datum": "WGS84",
"no_defs": None,
"type": "crs",
Expand All @@ -400,8 +400,8 @@ def test_cf_rotated_latlon__grid():
dict(
grid_mapping_name="rotated_latitude_longitude",
grid_north_pole_latitude=32.5,
grid_north_pole_longitude=170.0,
north_pole_grid_longitude=1.0,
grid_north_pole_longitude=1.0,
north_pole_grid_longitude=170.0,
)
)
with pytest.warns(UserWarning):
Expand Down
48 changes: 48 additions & 0 deletions test/crs/test_crs_coordinate_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
PlateCarreeConversion,
PolarStereographicAConversion,
PolarStereographicBConversion,
PoleRotationNetCDFCFConversion,
RotatedLatitudeLongitudeConversion,
SinusoidalConversion,
StereographicConversion,
Expand All @@ -30,6 +31,7 @@
VerticalPerspectiveConversion,
)
from pyproj.exceptions import CRSError
from test.conftest import PROJ_GTE_82


def _to_dict(operation):
Expand Down Expand Up @@ -640,6 +642,52 @@ def test_rotated_latitude_longitude_operation():
assert _to_dict(aeaop) == {"o_lat_p": 1.0, "o_lon_p": 2.0, "lon_0": 3.0}


def test_pole_rotation_netcdf_cf_convention__defaults():
poleop = PoleRotationNetCDFCFConversion(
grid_north_pole_latitude=1, grid_north_pole_longitude=2
)
if PROJ_GTE_82:
assert poleop.name == "Pole rotation (netCDF CF convention)"
assert poleop.method_name == "Pole rotation (netCDF CF convention)"
assert _to_dict(poleop) == {
"Grid north pole latitude (netCDF CF convention)": 1.0,
"Grid north pole longitude (netCDF CF convention)": 2.0,
"North pole grid longitude (netCDF CF convention)": 0.0,
}
else:
assert poleop.name == "unknown"
assert poleop.method_name == "PROJ ob_tran o_proj=longlat"
assert _to_dict(poleop) == {
"o_lat_p": 1.0,
"o_lon_p": 0.0,
"lon_0": 182.0,
}


def test_pole_rotation_netcdf_cf_convention():
poleop = PoleRotationNetCDFCFConversion(
grid_north_pole_latitude=1,
grid_north_pole_longitude=2,
north_pole_grid_longitude=10,
)
if PROJ_GTE_82:
assert poleop.name == "Pole rotation (netCDF CF convention)"
assert poleop.method_name == "Pole rotation (netCDF CF convention)"
assert _to_dict(poleop) == {
"Grid north pole latitude (netCDF CF convention)": 1.0,
"Grid north pole longitude (netCDF CF convention)": 2.0,
"North pole grid longitude (netCDF CF convention)": 10.0,
}
else:
assert poleop.name == "unknown"
assert poleop.method_name == "PROJ ob_tran o_proj=longlat"
assert _to_dict(poleop) == {
"o_lat_p": 1.0,
"o_lon_p": 10.0,
"lon_0": 182.0,
}


def test_lambert_cylindrical_equal_area_scale_operation__defaults():
lceaop = LambertCylindricalEqualAreaScaleConversion()
assert lceaop.name == "unknown"
Expand Down

0 comments on commit 440ad03

Please sign in to comment.