[Git][debian-gis-team/pyresample][upstream] New upstream version 1.33.0
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sat Mar 22 14:11:45 GMT 2025
Antonio Valentino pushed to branch upstream at Debian GIS Project / pyresample
Commits:
3b297551 by Antonio Valentino at 2025-03-22T12:00:47+00:00
New upstream version 1.33.0
- - - - -
10 changed files:
- .github/workflows/deploy.yaml
- .pre-commit-config.yaml
- CHANGELOG.md
- pyresample/ewa/_ll2cr.pyx
- pyresample/ewa/ewa.py
- pyresample/geometry.py
- pyresample/test/test_ewa_ll2cr.py
- pyresample/test/test_geometry/test_area.py
- pyresample/version.py
- versioneer.py
Changes:
=====================================
.github/workflows/deploy.yaml
=====================================
@@ -93,7 +93,7 @@ jobs:
path: dist
- name: Publish package to PyPI
if: github.event.action != 'published'
- uses: pypa/gh-action-pypi-publish at v1.12.3
+ uses: pypa/gh-action-pypi-publish at v1.12.4
with:
user: __token__
password: ${{ secrets.test_pypi_password }}
@@ -116,7 +116,7 @@ jobs:
path: dist
- name: Publish package to PyPI
if: github.event.action == 'published'
- uses: pypa/gh-action-pypi-publish at v1.12.3
+ uses: pypa/gh-action-pypi-publish at v1.12.4
with:
user: __token__
password: ${{ secrets.pypi_password }}
=====================================
.pre-commit-config.yaml
=====================================
@@ -2,7 +2,7 @@ exclude: '^$'
fail_fast: false
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: 'v0.8.6'
+ rev: 'v0.9.9'
hooks:
- id: ruff
- repo: https://github.com/pre-commit/pre-commit-hooks
@@ -13,12 +13,12 @@ repos:
- id: check-yaml
args: [--unsafe]
- repo: https://github.com/PyCQA/bandit
- rev: '1.8.0' # Update me!
+ rev: '1.8.3' # Update me!
hooks:
- id: bandit
args: [--ini, .bandit]
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: 'v1.14.1' # Use the sha / tag you want to point at
+ rev: 'v1.15.0' # Use the sha / tag you want to point at
hooks:
- id: mypy
additional_dependencies:
@@ -32,7 +32,7 @@ repos:
- pytest
args: [ --warn-unused-configs ]
- repo: https://github.com/pycqa/isort
- rev: 5.13.2
+ rev: 6.0.1
hooks:
- id: isort
language_version: python3
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,23 @@
+## Version 1.33.0 (2025/03/18)
+
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 651](https://github.com/pytroll/pyresample/pull/651) - Fix dynamic area freezing not preserving epsg code
+
+#### Features added
+
+* [PR 648](https://github.com/pytroll/pyresample/pull/648) - Update EWA pyproj Proj usage to Transformer
+
+#### Backward incompatible changes
+
+* [PR 648](https://github.com/pytroll/pyresample/pull/648) - Update EWA pyproj Proj usage to Transformer
+
+In this release 3 pull requests were closed.
+
+
## Version 1.32.0 (2025/01/29)
### Issues Closed
=====================================
pyresample/ewa/_ll2cr.pyx
=====================================
@@ -23,11 +23,14 @@
__docformat__ = "restructuredtext en"
import numpy
-from pyproj import Proj
+from pyproj import Transformer
+from pyproj.enums import TransformDirection
cimport cython
cimport numpy
+from pyresample.utils.proj4 import get_geodetic_crs_with_no_datum_shift
+
numpy.import_array()
# column and rows can only be doubles for now until the PROJ.4 is linked directly so float->double casting can be done
@@ -40,18 +43,18 @@ cdef extern from "numpy/npy_math.h":
bint npy_isnan(double x)
-def projection_circumference(p):
+def projection_circumference(t):
"""Return the projection circumference if the projection is cylindrical, None otherwise.
Projections that are not cylindrical and centered on the globes axis
can not easily have data cross the antimeridian of the projection.
"""
- lon0, lat0 = p(0, 0, inverse=True)
+ lon0, lat0 = t.transform(0, 0, direction=TransformDirection.INVERSE)
lon1 = lon0 + 180.0
lat1 = lat0 + 5.0
- x0, y0 = p(lon0, lat0) # should result in zero or near zero
- x1, y1 = p(lon1, lat0)
- x2, y2 = p(lon1, lat1)
+ x0, y0 = t.transform(lon0, lat0) # should result in zero or near zero
+ x1, y1 = t.transform(lon1, lat0)
+ x2, y2 = t.transform(lon1, lat1)
if y0 != y1 or x1 != x2:
return 0.0
return abs(x1 - x0) * 2
@@ -61,7 +64,7 @@ def projection_circumference(p):
@cython.wraparound(False)
@cython.cdivision(True)
def ll2cr_dynamic(numpy.ndarray[cr_dtype, ndim=2] lon_arr, numpy.ndarray[cr_dtype, ndim=2] lat_arr,
- cr_dtype fill_in, str proj4_definition,
+ cr_dtype fill_in, object src_crs, object dst_crs,
double cell_width, double cell_height,
width=None, height=None,
origin_x=None, origin_y=None):
@@ -98,13 +101,13 @@ def ll2cr_dynamic(numpy.ndarray[cr_dtype, ndim=2] lon_arr, numpy.ndarray[cr_dtyp
of limitations in pyproj.
"""
# pure python stuff for now
- p = Proj(proj4_definition)
+ t = Transformer.from_crs(src_crs, dst_crs, always_xy=True)
# Pyproj currently makes a copy so we don't have to do anything special here
- cdef tuple projected_tuple = p(lon_arr, lat_arr)
+ cdef tuple projected_tuple = t.transform(lon_arr, lat_arr)
cdef cr_dtype[:, ::1] rows_out = projected_tuple[1]
cdef cr_dtype[:, ::1] cols_out = projected_tuple[0]
- cdef double proj_circum = projection_circumference(p)
+ cdef double proj_circum = projection_circumference(t)
cdef unsigned int w
cdef unsigned int h
cdef double ox
@@ -203,7 +206,7 @@ def ll2cr_dynamic(numpy.ndarray[cr_dtype, ndim=2] lon_arr, numpy.ndarray[cr_dtyp
@cython.wraparound(False)
@cython.cdivision(True)
def ll2cr_static(numpy.ndarray[cr_dtype, ndim=2] lon_arr, numpy.ndarray[cr_dtype, ndim=2] lat_arr,
- cr_dtype fill_in, str proj4_definition,
+ cr_dtype fill_in, object src_crs, object dst_crs,
double cell_width, double cell_height,
unsigned int width, unsigned int height,
double origin_x, double origin_y):
@@ -229,11 +232,11 @@ def ll2cr_static(numpy.ndarray[cr_dtype, ndim=2] lon_arr, numpy.ndarray[cr_dtype
of limitations in pyproj.
"""
# TODO: Rewrite so it is no GIL
- # pure python stuff for now
- p = Proj(proj4_definition)
+ # pure python stuff for now
+ t = Transformer.from_crs(src_crs, dst_crs, always_xy=True)
# Pyproj currently makes a copy so we don't have to do anything special here
- cdef tuple projected_tuple = p(lon_arr, lat_arr)
+ cdef tuple projected_tuple = t.transform(lon_arr, lat_arr)
cdef cr_dtype[:, ::1] rows_out = projected_tuple[1]
cdef cr_dtype[:, ::1] cols_out = projected_tuple[0]
cdef cr_dtype[:, ::1] lons_view = lon_arr
=====================================
pyresample/ewa/ewa.py
=====================================
@@ -76,7 +76,7 @@ def ll2cr(swath_def, area_def, fill=np.nan, copy=True):
ox = area_def.area_extent[0] + cw / 2.
oy = area_def.area_extent[3] + ch / 2.
swath_points_in_grid = _ll2cr.ll2cr_static(lons, lats, fill,
- p, cw, ch, w, h, ox, oy)
+ swath_def.crs, area_def.crs, cw, ch, w, h, ox, oy)
return swath_points_in_grid, lons, lats
=====================================
pyresample/geometry.py
=====================================
@@ -22,6 +22,7 @@ import hashlib
import math
import warnings
from collections import OrderedDict
+from contextlib import suppress
from functools import partial, wraps
from logging import getLogger
from pathlib import Path
@@ -66,6 +67,7 @@ except ModuleNotFoundError:
from pyproj import CRS
from pyproj.enums import TransformDirection
+from pyproj.exceptions import CRSError
logger = getLogger(__name__)
@@ -1284,6 +1286,8 @@ class DynamicAreaDefinition(object):
area_extent = self.area_extent
if not area_extent or not width or not height:
projection, corners = self._compute_bound_centers(proj_dict, lonslats, antimeridian_mode=antimeridian_mode)
+ with suppress(CRSError):
+ projection = CRS(CRS(projection).to_epsg())
area_extent, width, height = self.compute_domain(corners, resolution, shape, projection)
return AreaDefinition(self.area_id, self.description, '',
projection, width, height,
=====================================
pyresample/test/test_ewa_ll2cr.py
=====================================
@@ -21,6 +21,7 @@ import logging
import unittest
import numpy as np
+from pyproj import CRS
from pyresample.test.utils import create_test_latitude, create_test_longitude
@@ -70,14 +71,15 @@ class TestLL2CRStatic(unittest.TestCase):
lat_arr = create_test_latitude(18.0, 40.0, (50, 100), dtype=np.float64)
grid_info = static_lcc.copy()
fill_in = np.nan
- proj_str = grid_info["proj4_definition"]
+ src_crs = CRS.from_epsg(4326)
+ dst_crs = CRS.from_proj4(grid_info["proj4_definition"])
cw = grid_info["cell_width"]
ch = grid_info["cell_height"]
ox = grid_info["origin_x"]
oy = grid_info["origin_y"]
w = grid_info["width"]
h = grid_info["height"]
- points_in_grid = _ll2cr.ll2cr_static(lon_arr, lat_arr, fill_in, proj_str,
+ points_in_grid = _ll2cr.ll2cr_static(lon_arr, lat_arr, fill_in, src_crs, dst_crs,
cw, ch, w, h, ox, oy)
self.assertEqual(points_in_grid, lon_arr.size, "all these test points should fall in this grid")
@@ -92,14 +94,15 @@ class TestLL2CRStatic(unittest.TestCase):
grid_info = static_geo_whole_earth.copy()
fill_in = np.nan
- proj_str = grid_info['proj4_definition']
+ src_crs = CRS.from_epsg(4326)
+ dst_crs = CRS.from_proj4(grid_info["proj4_definition"])
cw = grid_info['cell_width']
ch = grid_info['cell_height']
ox = grid_info['origin_x']
oy = grid_info['origin_y']
w = grid_info['width']
h = grid_info['height']
- points_in_grid = _ll2cr.ll2cr_static(lon_arr, lat_arr, fill_in, proj_str,
+ points_in_grid = _ll2cr.ll2cr_static(lon_arr, lat_arr, fill_in, src_crs, dst_crs,
cw, ch, w, h, ox, oy)
self.assertEqual(points_in_grid, lon_arr.size,
'all these test points should fall in this grid')
@@ -110,14 +113,15 @@ class TestLL2CRStatic(unittest.TestCase):
lat_arr = create_test_latitude(18.0, 40.0, (50, 100), dtype=np.float64)
grid_info = static_lcc.copy()
fill_in = np.nan
- proj_str = grid_info["proj4_definition"]
+ src_crs = CRS.from_epsg(4326)
+ dst_crs = CRS.from_proj4(grid_info["proj4_definition"])
cw = grid_info["cell_width"]
ch = grid_info["cell_height"]
ox = grid_info["origin_x"]
oy = grid_info["origin_y"]
w = grid_info["width"]
h = grid_info["height"]
- points_in_grid = _ll2cr.ll2cr_static(lon_arr, lat_arr, fill_in, proj_str,
+ points_in_grid = _ll2cr.ll2cr_static(lon_arr, lat_arr, fill_in, src_crs, dst_crs,
cw, ch, w, h, ox, oy)
self.assertEqual(points_in_grid, 0, "none of these test points should fall in this grid")
@@ -131,14 +135,16 @@ class TestLL2CRDynamic(unittest.TestCase):
lat_arr = create_test_latitude(15.0, 30.0, (50, 100), dtype=np.float64)
grid_info = dynamic_wgs84.copy()
fill_in = np.nan
- proj_str = grid_info["proj4_definition"]
+ src_crs = CRS.from_epsg(4326)
+ dst_crs = CRS.from_proj4(grid_info["proj4_definition"])
cw = grid_info["cell_width"]
ch = grid_info["cell_height"]
ox = grid_info["origin_x"]
oy = grid_info["origin_y"]
w = grid_info["width"]
h = grid_info["height"]
- points_in_grid, lon_res, lat_res, ox, oy, w, h = _ll2cr.ll2cr_dynamic(lon_arr, lat_arr, fill_in, proj_str,
+ points_in_grid, lon_res, lat_res, ox, oy, w, h = _ll2cr.ll2cr_dynamic(lon_arr, lat_arr, fill_in,
+ src_crs, dst_crs,
cw, ch, w, h, ox, oy)
self.assertEqual(points_in_grid, lon_arr.size, "all points should be contained in a dynamic grid")
self.assertIs(lon_arr, lon_res)
@@ -152,14 +158,16 @@ class TestLL2CRDynamic(unittest.TestCase):
lat_arr = create_test_latitude(15.0, 30.0, (50, 100), twist_factor=-0.1, dtype=np.float64)
grid_info = dynamic_wgs84.copy()
fill_in = np.nan
- proj_str = grid_info["proj4_definition"]
+ src_crs = CRS.from_epsg(4326)
+ dst_crs = CRS.from_proj4(grid_info["proj4_definition"])
cw = grid_info["cell_width"]
ch = grid_info["cell_height"]
ox = grid_info["origin_x"]
oy = grid_info["origin_y"]
w = grid_info["width"]
h = grid_info["height"]
- points_in_grid, lon_res, lat_res, ox, oy, w, h = _ll2cr.ll2cr_dynamic(lon_arr, lat_arr, fill_in, proj_str,
+ points_in_grid, lon_res, lat_res, ox, oy, w, h = _ll2cr.ll2cr_dynamic(lon_arr, lat_arr, fill_in,
+ src_crs, dst_crs,
cw, ch, w, h, ox, oy)
self.assertEqual(points_in_grid, lon_arr.size, "all points should be contained in a dynamic grid")
self.assertIs(lon_arr, lon_res)
@@ -173,14 +181,16 @@ class TestLL2CRDynamic(unittest.TestCase):
lat_arr = create_test_latitude(15.0, 30.0, (50, 100), twist_factor=-0.1, dtype=np.float64)
grid_info = dynamic_wgs84.copy()
fill_in = np.nan
- proj_str = grid_info["proj4_definition"]
+ src_crs = CRS.from_epsg(4326)
+ dst_crs = CRS.from_proj4(grid_info["proj4_definition"])
cw = grid_info["cell_width"]
ch = grid_info["cell_height"]
ox = grid_info["origin_x"]
oy = grid_info["origin_y"]
w = grid_info["width"]
h = grid_info["height"]
- points_in_grid, lon_res, lat_res, ox, oy, w, h = _ll2cr.ll2cr_dynamic(lon_arr, lat_arr, fill_in, proj_str,
+ points_in_grid, lon_res, lat_res, ox, oy, w, h = _ll2cr.ll2cr_dynamic(lon_arr, lat_arr, fill_in,
+ src_crs, dst_crs,
cw, ch, w, h, ox, oy)
self.assertEqual(points_in_grid, lon_arr.size, "all points should be contained in a dynamic grid")
self.assertIs(lon_arr, lon_res)
=====================================
pyresample/test/test_geometry/test_area.py
=====================================
@@ -1763,3 +1763,12 @@ def test_dynamic_area_can_use_bounding_box_attribute():
swath_def_to_freeze_on = SwathDefinition(None, None, attrs=dict(bounding_box=[[0, 20, 20, 0], [55, 55, 45, 45]]))
res_area = area_def.freeze(swath_def_to_freeze_on)
assert res_area.area_extent == (3533500, 2484500, 5108500, 3588500)
+ assert res_area.crs == CRS("epsg:3035")
+
+
+def test_dynamic_area_preserves_epsg_code():
+ """Test that area freezing preserves epsg code."""
+ area_def = DynamicAreaDefinition("test_area", "", "epsg:3035", resolution=500)
+ swath_def_to_freeze_on = SwathDefinition(None, None, attrs=dict(bounding_box=[[0, 20, 20, 0], [55, 55, 45, 45]]))
+ res_area = area_def.freeze(swath_def_to_freeze_on)
+ assert res_area.crs == CRS("epsg:3035")
=====================================
pyresample/version.py
=====================================
@@ -26,9 +26,9 @@ def get_keywords() -> Dict[str, str]:
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
- git_refnames = " (tag: v1.32.0)"
- git_full = "dde652d430e57df1b338a46e9c279ac9e6c15071"
- git_date = "2025-01-29 21:16:05 -0600"
+ git_refnames = " (HEAD -> main, tag: v1.33.0)"
+ git_full = "71ceaba53599a288de01e64e119e186aef822f49"
+ git_date = "2025-03-18 13:32:18 +0100"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
=====================================
versioneer.py
=====================================
@@ -1898,7 +1898,7 @@ def get_cmdclass(cmdclass: Optional[Dict[str, Any]] = None):
class cmd_version(Command):
description = "report generated version string"
- user_options: List[Tuple[str, str, str]] = []
+ user_options: List[Tuple[str, str, str]] = [] # type: ignore
boolean_options: List[str] = []
def initialize_options(self) -> None:
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyresample/-/commit/3b297551bd2441b2b5dbe68303e233449cd4164b
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pyresample/-/commit/3b297551bd2441b2b5dbe68303e233449cd4164b
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20250322/04205201/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list