[Git][debian-gis-team/trollimage][upstream] New upstream version 1.26.0
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Thu Oct 24 06:52:54 BST 2024
Antonio Valentino pushed to branch upstream at Debian GIS Project / trollimage
Commits:
3ec297b2 by Antonio Valentino at 2024-10-24T05:44:27+00:00
New upstream version 1.26.0
- - - - -
7 changed files:
- .github/workflows/ci.yaml
- .github/workflows/deploy.yaml
- CHANGELOG.md
- continuous_integration/environment.yaml
- 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
=====================================
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/-/commit/3ec297b2efb8db9afde2bc15deec48218fc5f632
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/-/commit/3ec297b2efb8db9afde2bc15deec48218fc5f632
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/40e29870/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list