[Git][debian-gis-team/xcube-resampling][master] 4 commits: New upstream version 0.3.4

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Mon Jun 1 19:09:34 BST 2026



Antonio Valentino pushed to branch master at Debian GIS Project / xcube-resampling


Commits:
5cb073a6 by Antonio Valentino at 2026-06-01T17:59:07+00:00
New upstream version 0.3.4
- - - - -
bf66e6bf by Antonio Valentino at 2026-06-01T17:59:40+00:00
Update upstream source from tag 'upstream/0.3.4'

Update to upstream version '0.3.4'
with Debian dir ace9ac88328d0ec254ccf265c94ae27faaf6b2f5
- - - - -
1a520400 by Antonio Valentino at 2026-06-01T18:00:14+00:00
New upstream release

- - - - -
f9670801 by Antonio Valentino at 2026-06-01T18:05:54+00:00
Set distribution to unstable

- - - - -


11 changed files:

- CHANGES.md
- debian/changelog
- + docs/examples/resample_in_space.ipynb
- docs/examples/resample_in_space_large_example_reproject_dataset.ipynb
- examples/resample_in_space.ipynb
- examples/resample_in_space_large_example_reproject_dataset.ipynb
- mkdocs.yml
- tests/test_utils.py
- xcube_resampling/affine.py
- xcube_resampling/utils.py
- xcube_resampling/version.py


Changes:

=====================================
CHANGES.md
=====================================
@@ -1,3 +1,13 @@
+## Changes in 0.3.4
+
+- Added `utils.transform_resolution` to convert spatial resolution between coordinate 
+  reference systems (CRS).
+
+## Changes in 0.3.3
+
+- Added function `utils.resolution_degrees_to_meters` to convert spatial resolution
+  from degrees to meters at a given geographic latitude.
+
 ## Changes in 0.3.2
 
 - Change inconsistent license classifier in `pyproject.toml` 


=====================================
debian/changelog
=====================================
@@ -1,9 +1,12 @@
-xcube-resampling (0.3.2-2) UNRELEASED; urgency=medium
+xcube-resampling (0.3.4-1) unstable; urgency=medium
 
-  * Team upload.
+  [ Bas Couwenberg ]
   * Bump Standards-Version to 4.7.4, no changes.
 
- -- Bas Couwenberg <sebastic at debian.org>  Sat, 04 Apr 2026 10:25:10 +0200
+  [ Antonio Valentino ]
+  * New upstream release.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it>  Mon, 01 Jun 2026 18:05:37 +0000
 
 xcube-resampling (0.3.2-1) unstable; urgency=medium
 


=====================================
docs/examples/resample_in_space.ipynb
=====================================
The diff for this file was not included because it is too large.

=====================================
docs/examples/resample_in_space_large_example_reproject_dataset.ipynb
=====================================
@@ -4,9 +4,7 @@
    "cell_type": "markdown",
    "id": "62866d66-22db-4f13-b585-3310df862833",
    "metadata": {},
-   "source": [
-    "# Reproject the ESA-CCI land cover map for Europe"
-   ]
+   "source": "# Reprojection of ESA-CCI land cover map for Europe"
   },
   {
    "cell_type": "markdown",


=====================================
examples/resample_in_space.ipynb
=====================================
@@ -4,7 +4,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## Demonstration of function `resample_in_space()`\n",
+    "# Demonstration of function resample_in_space()\n",
     "\n",
     "This notebook demonstrates the functionality of the `resample_in_space` function. We show how it can be used for three different purposes:\n",
     "\n",


=====================================
examples/resample_in_space_large_example_reproject_dataset.ipynb
=====================================
@@ -4,9 +4,7 @@
    "cell_type": "markdown",
    "id": "62866d66-22db-4f13-b585-3310df862833",
    "metadata": {},
-   "source": [
-    "# Reproject the ESA-CCI land cover map for Europe"
-   ]
+   "source": "# Reprojection of ESA-CCI land cover map for Europe"
   },
   {
    "cell_type": "markdown",


=====================================
mkdocs.yml
=====================================
@@ -12,6 +12,7 @@ nav:
       - GridMapping instance: examples/grid_mapping.ipynb
       - Affine Transformation: examples/affine.ipynb
       - Rectification of a Sentinel-3 tile: examples/rectify_sentinel3.ipynb
+      - Demonstration of the function resample_in_space: examples/resample_in_space.ipynb
       - Reprojection of large Land Cover Map: examples/resample_in_space_large_example_reproject_dataset.ipynb
   - Python API: api.md
   - About: about.md


=====================================
tests/test_utils.py
=====================================
@@ -28,7 +28,9 @@ from xcube_resampling.utils import (
     get_spatial_coords,
     get_utm_crs,
     reproject_bbox,
+    resolution_degrees_to_meters,
     resolution_meters_to_degrees,
+    transform_resolution,
 )
 
 from .sampledata import create_2x4x4_dataset_with_irregular_coords
@@ -417,9 +419,34 @@ class TestUtils(unittest.TestCase):
         target = (-170, 0, -160, 10)
         self.assertAlmostEqual(0.0, bbox_overlap(source, target))
 
+    def test_transform_resolution(self):
+        # EPSG:4326 → UTM (meters)
+        res = transform_resolution((3, 60), 1.0, "EPSG:4326", "EPSG:32631")
+        self.assertAlmostEqual(55777.9, res[0], places=1)
+        self.assertAlmostEqual(111376.2, res[1], places=1)
+
+        # UTM (meters) → EPSG:4326
+        res = transform_resolution(
+            (500_000, 6_651_411), 111376, "EPSG:32631", "EPSG:4326"
+        )
+        self.assertAlmostEqual(2.0, res[0], places=2)
+        self.assertAlmostEqual(1.0, res[1], places=2)
+
+        # UTM → UTM (same CRS, should remain ~unchanged in magnitude)
+        res = transform_resolution(
+            (500000, 0), (1000, 1000), "EPSG:32631", "EPSG:32631"
+        )
+        self.assertAlmostEqual(1000, res[0])
+        self.assertAlmostEqual(1000, res[1])
+
+        # US survey foot to meter
+        res = transform_resolution((0, 0), (1, 1), "EPSG:3561", "EPSG:32605")
+        self.assertAlmostEqual(0.30525, res[0], places=4)
+        self.assertAlmostEqual(0.30525, res[1], places=4)
+
     def test_resolution_meters_to_degrees(self):
         # 111320 m ≈ 1 degree at equator
-        lat_deg, lon_deg = resolution_meters_to_degrees(111320, 0)
+        lon_deg, lat_deg = resolution_meters_to_degrees(111320, 0)
         self.assertAlmostEqual(1.0, lat_deg, places=6)
         self.assertAlmostEqual(1.0, lon_deg, places=6)
 
@@ -433,6 +460,22 @@ class TestUtils(unittest.TestCase):
         self.assertAlmostEqual(1.0, lat_deg, places=6)
         self.assertAlmostEqual(1.0 / 0.5, lon_deg, places=6)  # 2 degrees
 
+    def test_resolution_degrees_to_meters(self):
+        # 111320 m ≈ 1 degree at equator
+        lon_deg, lat_deg = resolution_degrees_to_meters(1, 0)
+        self.assertAlmostEqual(111320, lat_deg, places=6)
+        self.assertAlmostEqual(111320, lon_deg, places=6)
+
+        # 222640 m ≈ 2 degrees latitude
+        lon_deg, lat_deg = resolution_degrees_to_meters((1, 2), 0)
+        self.assertAlmostEqual(222640, lat_deg, places=6)
+        self.assertAlmostEqual(111320, lon_deg, places=6)
+
+        # At 60 degrees latitude, longitude degrees shrink by cos(60°) = 0.5
+        lon_deg, lat_deg = resolution_degrees_to_meters(1, 60)
+        self.assertAlmostEqual(111320, lat_deg, places=6)
+        self.assertAlmostEqual(111320 * 0.5, lon_deg, places=6)
+
 
 class TestClipDatasetByBBox(unittest.TestCase):
 


=====================================
xcube_resampling/affine.py
=====================================
@@ -22,7 +22,6 @@
 import math
 from collections.abc import Iterable, Sequence
 
-import dask
 import dask.array as da
 import numpy as np
 import xarray as xr


=====================================
xcube_resampling/utils.py
=====================================
@@ -19,6 +19,7 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 
+import warnings
 from collections.abc import Callable, Hashable, Iterable, Mapping, Sequence
 from dataclasses import dataclass
 
@@ -313,6 +314,45 @@ def _split_bbox_antimeridian(bbox: Sequence[FloatInt]) -> Sequence[Sequence[Floa
     return [bbox]
 
 
+def transform_resolution(
+    ref_point: tuple[FloatInt, FloatInt],
+    resolution: FloatInt | tuple[FloatInt, FloatInt],
+    src_crs: str | pyproj.CRS,
+    dst_crs: str | pyproj.CRS,
+) -> tuple[FloatInt, FloatInt]:
+    """Estimate local spatial resolution in the destination CRS using
+    finite differences.
+
+    Args:
+        ref_point: Reference point (easting, northing) in the source CRS.
+        resolution: Spatial resolution in the source CRS. Can be a single number
+            (applied equally to both axes) or a tuple `(x_res, y_res)`.
+        src_crs: Source coordinate reference system.
+        dst_crs: Destination coordinate reference system.
+
+    Returns:
+        tuple: Estimated local resolution in the destination CRS as `(x_res, y_res)`.
+    """
+    transformer = pyproj.Transformer.from_crs(src_crs, dst_crs, always_xy=True)
+    if not isinstance(resolution, tuple):
+        resolution = (resolution, resolution)
+
+    # reference point
+    x0, y0 = transformer.transform(*ref_point)
+
+    # step in x direction
+    x1, y1 = transformer.transform(ref_point[0] + resolution[0], ref_point[1])
+
+    # step in y direction
+    x2, y2 = transformer.transform(ref_point[0], ref_point[1] + resolution[1])
+
+    # Euclidean distances
+    res_x = np.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2)
+    res_y = np.sqrt((x2 - x0) ** 2 + (y2 - y0) ** 2)
+
+    return res_x, res_y
+
+
 def resolution_meters_to_degrees(
     resolution: FloatInt | tuple[FloatInt, FloatInt], latitude: FloatInt
 ) -> tuple[FloatInt, FloatInt]:
@@ -341,6 +381,33 @@ def resolution_meters_to_degrees(
     )
 
 
+def resolution_degrees_to_meters(
+    resolution: FloatInt | tuple[FloatInt, FloatInt], latitude: FloatInt
+) -> tuple[FloatInt, FloatInt]:
+    """Convert spatial resolution from degrees to meters at a given geographic latitude.
+
+    Args:
+        resolution: Spatial resolution in degrees. Can be a single number
+            (applied equally to both axes) or a tuple ``(lon_res, lat_res)``.
+        latitude: Latitude in degrees at which to compute the longitude scaling.
+
+    Returns:
+        A tuple `(x_res, y_res)` giving the approximate spatial
+        resolution in degrees for the latitude and longitude directions.
+
+    Notes:
+        - 1 degree of latitude ≈ 111,320 meters (constant approximation).
+        - 1 degree of longitude ≈ 111,320 * cos(latitude) meters.
+
+    """
+    if not isinstance(resolution, tuple):
+        resolution = (resolution, resolution)
+    return (
+        resolution[0] * (111320 * np.cos(np.deg2rad(latitude))),
+        resolution[1] * 111320,
+    )
+
+
 def normalize_grid_mapping(ds: xr.Dataset, gm: GridMapping) -> xr.Dataset:
     """
     Normalize the grid mapping of a dataset to use a standard "spatial_ref" coordinate.


=====================================
xcube_resampling/version.py
=====================================
@@ -19,4 +19,4 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 # DEALINGS IN THE SOFTWARE.
 
-__version__ = "0.3.2"
+__version__ = "0.3.4"



View it on GitLab: https://salsa.debian.org/debian-gis-team/xcube-resampling/-/compare/320d348685bb5e6166a075fdcd7349523a2f9dea...f9670801ba99caa36baaa005b9aaed9b568a6c66

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/xcube-resampling/-/compare/320d348685bb5e6166a075fdcd7349523a2f9dea...f9670801ba99caa36baaa005b9aaed9b568a6c66
You're receiving this email because of your account on salsa.debian.org. Manage all notifications: https://salsa.debian.org/-/profile/notifications | Help: https://salsa.debian.org/help


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20260601/df87fb12/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list