[Git][debian-gis-team/trollimage][master] 6 commits: New upstream version 1.26.0

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Thu Oct 24 06:53:11 BST 2024



Antonio Valentino pushed to branch master at Debian GIS Project / trollimage


Commits:
3ec297b2 by Antonio Valentino at 2024-10-24T05:44:27+00:00
New upstream version 1.26.0
- - - - -
9269faa3 by Antonio Valentino at 2024-10-24T05:44:35+00:00
Update upstream source from tag 'upstream/1.26.0'

Update to upstream version '1.26.0'
with Debian dir 1a6d9355aba02e24f96163b4053802496e55b9bc
- - - - -
8d347494 by Antonio Valentino at 2024-10-24T05:45:10+00:00
New upstream release

- - - - -
6e41122e by Antonio Valentino at 2024-10-24T05:46:40+00:00
Update dates in d/copyright

- - - - -
2c67e452 by Antonio Valentino at 2024-10-24T05:47:52+00:00
Refresh all patches

- - - - -
88a3f00e by Antonio Valentino at 2024-10-24T05:48:11+00:00
Set distribution to unstable

- - - - -


10 changed files:

- .github/workflows/ci.yaml
- .github/workflows/deploy.yaml
- CHANGELOG.md
- continuous_integration/environment.yaml
- debian/changelog
- debian/copyright
- debian/patches/0001-No-display.patch
- trollimage/tests/test_image.py
- trollimage/version.py
- trollimage/xrimage.py


Changes:

=====================================
.github/workflows/ci.yaml
=====================================
@@ -30,9 +30,7 @@ jobs:
       - name: Setup Conda Environment
         uses: conda-incubator/setup-miniconda at v3
         with:
-          miniforge-variant: Mambaforge
           miniforge-version: latest
-          use-mamba: true
           python-version: ${{ matrix.python-version }}
           activate-environment: test-environment
           channels: conda-forge
@@ -65,6 +63,7 @@ jobs:
       - name: Install trollimage
         shell: bash -l {0}
         run: |
+          conda list
           pip install --no-deps -e .
           # pip forces non-wheel builds if we provide --cython-coverage as a --build-option
           # and that's way too slow


=====================================
.github/workflows/deploy.yaml
=====================================
@@ -59,7 +59,7 @@ jobs:
           platforms: all
 
       - name: Build wheels
-        uses: pypa/cibuildwheel at v2.19.2
+        uses: pypa/cibuildwheel at v2.21.1
         env:
           CIBW_SKIP: "cp36-* cp37-* cp38-* pp* *-manylinux_i686 *-musllinux_i686 *-musllinux_aarch64 *-win32"
           CIBW_ARCHS: "${{ matrix.cibw_archs }}"
@@ -101,14 +101,14 @@ jobs:
           path: dist
       - name: Publish package to Test PyPI
         if: github.event.action != 'published' && github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
-        uses: pypa/gh-action-pypi-publish at v1.9.0
+        uses: pypa/gh-action-pypi-publish at v1.10.2
         with:
           user: __token__
           password: ${{ secrets.test_pypi_password }}
           repository_url: https://test.pypi.org/legacy/
       - name: Publish package to PyPI
         if: github.event.action == 'published'
-        uses: pypa/gh-action-pypi-publish at v1.9.0
+        uses: pypa/gh-action-pypi-publish at v1.10.2
         with:
           user: __token__
           password: ${{ secrets.pypi_password }}


=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,28 @@
+###############################################################################
+## Version 1.26.0 (2024/10/21)
+
+### Issues Closed
+
+* [Issue 180](https://github.com/pytroll/trollimage/issues/180) - fill value not respected when saving palette images while keeping palette ([PR 181](https://github.com/pytroll/trollimage/pull/181) by [@gerritholl](https://github.com/gerritholl))
+* [Issue 178](https://github.com/pytroll/trollimage/issues/178) - XRImage.palettize results in image with fill value inconsistent with dtype ([PR 179](https://github.com/pytroll/trollimage/pull/179) by [@gerritholl](https://github.com/gerritholl))
+
+In this release 2 issues were closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 186](https://github.com/pytroll/trollimage/pull/186) - Fix dtype promotion in channel inversion
+* [PR 179](https://github.com/pytroll/trollimage/pull/179) - Adapt fill value after palettizing. ([178](https://github.com/pytroll/trollimage/issues/178))
+
+#### Features added
+
+* [PR 181](https://github.com/pytroll/trollimage/pull/181) - Respect fill value when saving palette images while keeping the palette. ([180](https://github.com/pytroll/trollimage/issues/180))
+
+In this release 3 pull requests were closed.
+
+###############################################################################
+
 ## Version 1.25.0 (2024/08/15)
 
 ### Issues Closed


=====================================
continuous_integration/environment.yaml
=====================================
@@ -13,6 +13,7 @@ dependencies:
   - coverage
   - codecov
   - rasterio
+  - libgdal-jp2openjpeg
   - libtiff
   - pyproj
   - pyresample


=====================================
debian/changelog
=====================================
@@ -1,3 +1,12 @@
+trollimage (1.26.0-1) unstable; urgency=medium
+
+  * New upstream release.
+  * Update dates in d/copyright.
+  * debian/patches:
+    - Refresh all patches.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it>  Thu, 24 Oct 2024 05:47:55 +0000
+
 trollimage (1.25.0-1) unstable; urgency=medium
 
   [ Bas Couwenberg ]


=====================================
debian/copyright
=====================================
@@ -5,7 +5,7 @@ Source: https://github.com/pytroll/trollimage
 
 Files: *
 Copyright: 2009-2018, Martin Raspaud
-           2009-2023, Trollimage Developers
+           2009-2024, Trollimage Developers
 License: GPL-3+
 
 Files: trollimage/_colorspaces.pyx


=====================================
debian/patches/0001-No-display.patch
=====================================
@@ -8,10 +8,10 @@ Skip tests that require display.
  1 file changed, 1 insertion(+)
 
 diff --git a/trollimage/tests/test_image.py b/trollimage/tests/test_image.py
-index 0aaf6a9..370d773 100644
+index 93544bd..be87aeb 100644
 --- a/trollimage/tests/test_image.py
 +++ b/trollimage/tests/test_image.py
-@@ -2191,6 +2191,7 @@ class TestXRImage:
+@@ -2213,6 +2213,7 @@ class TestXRImage:
          """Test putalpha."""
          pass
  


=====================================
trollimage/tests/test_image.py
=====================================
@@ -1,6 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# Copyright (c) 2009-2021 trollimage developers
+# Copyright (c) 2009-2024 trollimage developers
 #
 # This file is part of trollimage.
 #
@@ -1384,6 +1382,25 @@ class TestXRImage:
                 img.save(tmp.name, keep_palette=True, cmap=t_cmap,
                          dtype='uint16')
 
+    def test_save_geotiff_with_cmap_and_fill_value(self, tmp_path):
+        """Test saving GeoTIFF with colormap and fill value."""
+        import rasterio
+        test_file = tmp_path / "test.tif"
+        fv = np.uint8(42)
+        arr = np.ones((1, 5, 5), dtype="uint8")
+        arr[0, 2, 2] = 255
+        data = xr.DataArray(
+                arr,
+                dims=["bands", 'y', 'x'],
+                attrs={"_FillValue": 255},
+                coords={"bands": ["P"]})
+        img = xrimage.XRImage(data)
+        img.save(test_file, keep_palette=True, cmap=brbg,
+                 fill_value=fv)
+        with rasterio.open(test_file) as f:
+            cont = f.read()
+            assert cont[0, 2, 2] == fv
+
     @pytest.mark.skipif(sys.platform.startswith('win'),
                         reason="'NamedTemporaryFile' not supported on Windows")
     def test_save_geotiff_closed_file(self):
@@ -1595,8 +1612,8 @@ class TestXRImage:
         np.testing.assert_allclose(img.data.values, arr.astype(np.float32) / max_stretch, rtol=1e-6)
 
     @pytest.mark.parametrize("dtype", (np.float32, np.float64, float))
-    def test_invert(self, dtype):
-        """Check inversion of the image."""
+    def test_invert_single_parameter(self, dtype):
+        """Check inversion of the image for single inversion parameter."""
         arr = np.arange(75, dtype=dtype).reshape(5, 5, 3) / 75.
         data = xr.DataArray(arr.copy(), dims=['y', 'x', 'bands'],
                             coords={'bands': ['R', 'G', 'B']})
@@ -1608,7 +1625,11 @@ class TestXRImage:
         assert img.data.dtype == dtype
         assert np.allclose(img.data.values, 1 - arr)
 
-        data = xr.DataArray(arr.copy(), dims=['y', 'x', 'bands'],
+    @pytest.mark.parametrize("dtype", (np.float32, np.float64, float))
+    def test_invert_parameter_for_each_channel(self, dtype):
+        """Check inversion of the image for single inversion parameter."""
+        arr = np.arange(75, dtype=dtype).reshape(5, 5, 3) / 75.
+        data = xr.DataArray(arr, dims=['y', 'x', 'bands'],
                             coords={'bands': ['R', 'G', 'B']})
         img = xrimage.XRImage(data)
 
@@ -1618,6 +1639,7 @@ class TestXRImage:
         scale = xr.DataArray(np.array([-1, 1, -1]), dims=['bands'],
                              coords={'bands': ['R', 'G', 'B']})
         np.testing.assert_allclose(img.data.values, (data * scale + offset).values)
+        assert img.data.dtype == dtype
 
     @pytest.mark.parametrize("dtype", (np.float32, np.float64, float))
     def test_linear_stretch(self, dtype):
@@ -2600,6 +2622,25 @@ class TestXRImagePalettize:
                 np.testing.assert_allclose(new_brbg.values, loaded_brbg.values)
                 np.testing.assert_allclose(new_brbg.colors, loaded_brbg.colors)
 
+    def test_palettize_fill_value(self):
+        """Test that fill values are adapted."""
+        arr = np.arange(25, dtype="float32").reshape(5, 5)/25
+        arr[2, 2] = np.nan
+        data = xr.DataArray(arr.copy(), dims=['y', 'x'], attrs={"_FillValue": np.nan})
+        img = xrimage.XRImage(data)
+        img.palettize(brbg)
+        assert img.data[0, 2, 2] == img.data.attrs["_FillValue"]
+
+    def test_palettize_bad_fill_value(self):
+        """Test that palettize warns with a strange fill value."""
+        arr = np.arange(25, dtype="uint8").reshape(5, 5)
+        data = xr.DataArray(arr.copy(), dims=['y', 'x'], attrs={"_FillValue": 10})
+        img = xrimage.XRImage(data)
+        with pytest.warns(UserWarning,
+                          match="Palettizing uint8 data with the _FillValue attribute set to 10, "
+                                "but palettize is not generally fill value aware"):
+            img.palettize(brbg)
+
 
 class TestXRImageSaveScaleOffset:
     """Test case for saving an image with scale and offset tags."""


=====================================
trollimage/version.py
=====================================
@@ -26,9 +26,9 @@ def get_keywords():
     # 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 = " (HEAD -> main, tag: v1.25.0)"
-    git_full = "f3eae46c604e8964fadf2c1056d74d6c5cb10a3f"
-    git_date = "2024-08-15 15:12:05 +0200"
+    git_refnames = " (HEAD -> main, tag: v1.26.0)"
+    git_full = "b6c005940150b96705a5bd39112644032bb7b5fe"
+    git_date = "2024-10-21 08:46:19 +0300"
     keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
     return keywords
 


=====================================
trollimage/xrimage.py
=====================================
@@ -1,13 +1,6 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+# Copyright (c) 2017-2024 trollimage developers
 #
-# Copyright (c) 2017-2018
-#
-# Author(s):
-#
-#   Martin Raspaud <martin.raspaud at smhi.se>
-#   Adam Dybbroe <adam.dybbroe at smhi.se>
-#   Esben S. Nielsen <esn at dmi.dk>
+# This file is part of trollimage.
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -830,20 +823,25 @@ class XRImage:
             return data.attrs.get('_FillValue')
         return None
 
-    def _scale_and_replace_fill_value(self, data, input_fill_value, fill_value, dtype):
+    def _scale_and_replace_fill_value(self, data, input_fill_value, fill_value,
+                                      dtype, scale, replace_fill_value):
         # scale float data to the proper dtype
         # this method doesn't cast yet so that we can keep track of NULL values
-        data = self._scale_to_dtype(data, dtype, fill_value)
-        data = self._replace_fill_value(data, input_fill_value, fill_value, dtype)
+        if scale:
+            data = self._scale_to_dtype(data, dtype, fill_value)
+        if replace_fill_value:
+            data = self._replace_fill_value(data, input_fill_value, fill_value, dtype)
         return data
 
-    def _scale_alpha_or_fill_data(self, data, fill_value, dtype):
+    def _scale_alpha_or_fill_data(self, data, fill_value, dtype, scale, alpha, replace_fill_value):
         input_fill_value = self._get_input_fill_value(data)
-        needs_alpha = fill_value is None and not self.mode.endswith('A')
+        needs_alpha = alpha and fill_value is None and not self.mode.endswith('A')
         if needs_alpha:
             # We don't have a fill value or an alpha, let's add an alpha
             return self._add_alpha_and_scale(data, input_fill_value, dtype)
-        return self._scale_and_replace_fill_value(data, input_fill_value, fill_value, dtype)
+        return self._scale_and_replace_fill_value(data, input_fill_value,
+                                                  fill_value, dtype, scale,
+                                                  replace_fill_value)
 
     def finalize(self, fill_value=None, dtype=np.uint8, keep_palette=False):
         """Finalize the image to be written to an output file.
@@ -908,8 +906,11 @@ class XRImage:
             pass
         with xr.set_options(keep_attrs=True):
             attrs = final_data.attrs
-            if not keep_palette:
-                final_data = self._scale_alpha_or_fill_data(final_data, fill_value, dtype)
+            final_data = self._scale_alpha_or_fill_data(
+                    final_data, fill_value, dtype,
+                    scale=not keep_palette,
+                    alpha=not keep_palette,
+                    replace_fill_value=True)
             final_data = final_data.astype(dtype)
             final_data.attrs = attrs
 
@@ -1132,7 +1133,6 @@ class XRImage:
         if val is None:
             non_band_dims = tuple(x for x in self.data.dims if x != 'bands')
             val = getattr(self.data, kind)(dim=non_band_dims)
-
         if isinstance(val, (list, tuple)):
             val = self.xrify_tuples(val)
 
@@ -1302,7 +1302,7 @@ class XRImage:
         logger.debug("Applying invert with parameters %s", str(invert))
         if isinstance(invert, (tuple, list)):
             invert = self.xrify_tuples(invert)
-            offset = invert.astype(int)
+            offset = invert.astype(self.data.dtype)
             scale = (-1) ** offset
         elif invert:
             offset = 1
@@ -1428,6 +1428,9 @@ class XRImage:
         To (directly) get an image in mode "RGB" or "RGBA", use
         :meth:`~XRImage.colorize`.
 
+        Invalid data (NaN) are sorted into the final bin and the ``_FillValue``
+        attributed for the resulting image is set to the corresponding value.
+
         Args:
             colormap (:class:`~trollimage.colormap.Colormap`):
                 Colormap to be applied to the image.
@@ -1481,8 +1484,10 @@ class XRImage:
             mode = "PA"
             new_data = da.concatenate([new_data, self.data.sel(bands=['A'])], axis=0)
 
+        old_dtype = self.data.dtype
         self.data.data = new_data
         self.data.coords['bands'] = list(mode)
+        self._set_new_fill_value_after_palettize(colormap, old_dtype)
         # See docstring notes above for how scale/offset should be used
         scale_factor, offset = self._get_colormap_scale_offset(colormap)
         self.data.attrs.setdefault('enhancement_history', []).append({
@@ -1499,6 +1504,28 @@ class XRImage:
         offset = -cmap_min * scale_factor
         return scale_factor, offset
 
+    def _set_new_fill_value_after_palettize(self, colormap, old_dtype):
+        """Set new fill value after palettizing."""
+        # OK: float without fill value or fill value nan
+        # OK: int without fill value or fill value to max value
+        if ((np.issubdtype(old_dtype, np.inexact) and
+             np.isnan(self.data.attrs.get("_FillValue", np.nan))) or
+            (np.issubdtype(old_dtype, np.integer) and
+             self.data.attrs.get("_FillValue", np.iinfo(old_dtype).max) == np.iinfo(old_dtype).max)):
+            self.data.attrs["_FillValue"] = colormap.values.shape[0]-1
+        # not OK: float or int with different fill value
+        elif "_FillValue" in self.data.attrs:
+            warnings.warn(
+                f"Palettizing {old_dtype.name:s} data with the _FillValue attribute set to "
+                f"{self.data.attrs['_FillValue']!s}, "
+                "but palettize is not generally fill value aware (masked data "
+                "will be correctly palettized only for float with NaN or for "
+                "ints with fill value set to dtype max.",
+                UserWarning,
+                stacklevel=3)
+        # else: non-numeric data, probably doesn't work at all and will fail
+        # elsewhere anyway
+
     def blend(self, src):
         r"""Alpha blend *src* on top of the current image.
 



View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/-/compare/4754f8643a83d7243befe5f7f38d84f7d0ea62dd...88a3f00ebe3090ef6ecffec1ca63c8abc2df5cba

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/-/compare/4754f8643a83d7243befe5f7f38d84f7d0ea62dd...88a3f00ebe3090ef6ecffec1ca63c8abc2df5cba
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/20241024/4ab40a30/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list