From ebe2b7dc3f272d001bc0eb23ad691e5e7a3ec110 Mon Sep 17 00:00:00 2001 From: Floze <88098863+floze-the-genius@users.noreply.github.com> Date: Sat, 18 Jul 2026 18:24:06 +0400 Subject: [PATCH 1/2] Add explicit spherical projection axis --- pyresample/area_config.py | 15 ++++++++++++ pyresample/test/test_area_config.py | 35 +++++++++++++++++++++++++++ pyresample/test/test_files/areas.cfg | 6 ++--- pyresample/test/test_files/areas.yaml | 13 ++++++++++ 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/pyresample/area_config.py b/pyresample/area_config.py index 2ae10d18a..4d458390d 100644 --- a/pyresample/area_config.py +++ b/pyresample/area_config.py @@ -501,6 +501,7 @@ def create_area_def(area_id, projection, width=None, height=None, area_extent=No # (hold on to EPSG code as much as possible) if isinstance(projection, dict) and 'EPSG' in projection: projection = "EPSG:{}".format(projection['EPSG']) + projection = _add_missing_spherical_axis(projection) try: crs = _get_proj_data(projection) @@ -602,6 +603,20 @@ def _get_proj_data(projection: Any) -> CRS: return CRS.from_user_input(projection) +def _add_missing_spherical_axis(projection: Any) -> Any: + """Explicitly define a sphere when only its semi-major axis is provided.""" + if not isinstance(projection, dict) or "a" not in projection: + return projection + + ellipsoid_parameters = {"b", "R", "ellps", "datum", "rf", "f", "es", "e"} + if ellipsoid_parameters.intersection(projection): + return projection + + projection = projection.copy() + projection["b"] = projection["a"] + return projection + + def _get_proj_units(crs): if crs.is_geographic: unit_name = 'degrees' diff --git a/pyresample/test/test_area_config.py b/pyresample/test/test_area_config.py index 9583dcff0..e6d4deeac 100644 --- a/pyresample/test/test_area_config.py +++ b/pyresample/test/test_area_config.py @@ -312,3 +312,38 @@ def test_unused_params_warn(): units: m""" with pytest.warns(UserWarning, match=r"Unused/unexpected area definition parameter.*revolution"): load_area_from_string(yaml_str) + + +def test_projection_with_only_semi_major_axis_gets_explicit_spherical_axis(): + """Test that an incomplete spherical projection gets an explicit semi-minor axis.""" + from pyresample.area_config import _add_missing_spherical_axis + + projection = {"proj": "laea", "a": 6371228.0} + + normalized_projection = _add_missing_spherical_axis(projection) + + assert normalized_projection == {"proj": "laea", "a": 6371228.0, "b": 6371228.0} + assert projection == {"proj": "laea", "a": 6371228.0} + + +def test_create_area_def_passes_explicit_spherical_axis_to_geometry(): + """Test that area creation passes the completed projection to geometry construction.""" + from unittest import mock + + from pyresample.area_config import create_area_def + + projection = {"proj": "laea", "a": 6371228.0} + with mock.patch("pyresample.area_config._make_area") as make_area: + create_area_def("test", projection) + + assert make_area.call_args.args[3] == {"proj": "laea", "a": 6371228.0, "b": 6371228.0} + + +@pytest.mark.parametrize("ellipsoid_parameter", ["b", "R", "ellps", "datum", "rf", "f", "es", "e"]) +def test_projection_with_complete_ellipsoid_is_unchanged(ellipsoid_parameter): + """Test that existing ellipsoid definitions are preserved.""" + from pyresample.area_config import _add_missing_spherical_axis + + projection = {"proj": "laea", "a": 6371228.0, ellipsoid_parameter: "value"} + + assert _add_missing_spherical_axis(projection) is projection diff --git a/pyresample/test/test_files/areas.cfg b/pyresample/test/test_files/areas.cfg index cff1a98ac..3a40587fc 100644 --- a/pyresample/test/test_files/areas.cfg +++ b/pyresample/test/test_files/areas.cfg @@ -1,7 +1,7 @@ REGION: ease_sh { NAME: Antarctic EASE grid PCS_ID: ease_sh - PCS_DEF: proj=laea, lat_0=-90, lon_0=0, a=6371228.0, units=m + PCS_DEF: proj=laea, lat_0=-90, lon_0=0, a=6371228.0, b=6371228.0, units=m XSIZE: 425 YSIZE: 425 AREA_EXTENT: (-5326849.0625,-5326849.0625,5326849.0625,5326849.0625) @@ -10,7 +10,7 @@ REGION: ease_sh { REGION: ease_nh { NAME: Arctic EASE grid PCS_ID: ease_nh - PCS_DEF: proj=laea, lat_0=90, lon_0=0, a=6371228.0, units=m + PCS_DEF: proj=laea, lat_0=90, lon_0=0, a=6371228.0, b=6371228.0, units=m XSIZE: 425 YSIZE: 425 AREA_EXTENT: (-5326849.0625,-5326849.0625,5326849.0625,5326849.0625) @@ -19,7 +19,7 @@ REGION: ease_nh { #REGION: commented { # NAME: Arctic EASE grid # PCS_ID: ease_nh -# PCS_DEF: proj=laea, lat_0=90, lon_0=0, a=6371228.0, units=m +# PCS_DEF: proj=laea, lat_0=90, lon_0=0, a=6371228.0, b=6371228.0, units=m # XSIZE: 425 # YSIZE: 425 # AREA_EXTENT: (-5326849.0625,-5326849.0625,5326849.0625,5326849.0625) diff --git a/pyresample/test/test_files/areas.yaml b/pyresample/test/test_files/areas.yaml index d40de1c51..b3ddf3a6b 100644 --- a/pyresample/test/test_files/areas.yaml +++ b/pyresample/test/test_files/areas.yaml @@ -6,6 +6,7 @@ boundary: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: [425, 425] area_extent: [-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625] @@ -18,6 +19,7 @@ boundary_2: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: height: 425 @@ -33,6 +35,7 @@ corner: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: [425, 425] upper_left_extent: [-5326849.0625, 5326849.0625] @@ -47,6 +50,7 @@ corner_2: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: [425, 425] upper_left_extent: @@ -64,6 +68,7 @@ circle: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m center: [0, 0] resolution: [25067.525, 25067.525] @@ -77,6 +82,7 @@ circle_2: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m center: x: 0 @@ -97,6 +103,7 @@ area_of_interest: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: [425, 425] center: [0, 0] @@ -110,6 +117,7 @@ area_of_interest_2: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: [425, 425] center: @@ -126,6 +134,7 @@ test_meters: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: [425, 850] upper_left_extent: @@ -148,6 +157,7 @@ test_degrees: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m area_extent: lower_left_xy: [-135.0, -17.516001139327766] @@ -177,6 +187,7 @@ ease_sh: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: height: 425 @@ -193,6 +204,7 @@ ease_nh: lat_0: -90 lon_0: 0 a: 6371228.0 + b: 6371228.0 units: m shape: height: 425 @@ -271,6 +283,7 @@ ortho: lon_0: 40. lat_0: -40. a: 6370997.0 + b: 6370997.0 shape: height: 480 width: 640 From d70af9fe08d8f545a90e6898b33711240068b5c5 Mon Sep 17 00:00:00 2001 From: Floze <88098863+floze-the-genius@users.noreply.github.com> Date: Sat, 18 Jul 2026 18:27:50 +0400 Subject: [PATCH 2/2] Pin NumPy for Python 3.11 mypy checks --- .pre-commit-config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b5318fb63..4a1286dde 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,7 @@ repos: rev: 'v2.1.0' # Use the sha / tag you want to point at hooks: - id: mypy + language_version: python3.12 additional_dependencies: - types-docutils - types-PyYAML @@ -28,7 +29,7 @@ repos: - type_extensions - types-setuptools # Typed libraries - - numpy + - numpy<2.3 - pytest args: [ --warn-unused-configs ] - repo: https://github.com/pycqa/isort