[Git][debian-gis-team/trollimage][upstream] New upstream version 1.16.0

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Sat Oct 16 17:39:34 BST 2021



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


Commits:
64580fed by Antonio Valentino at 2021-10-16T16:13:11+00:00
New upstream version 1.16.0
- - - - -


10 changed files:

- − .github/ISSUE_TEMPLATE.md
- + .github/ISSUE_TEMPLATE/bug_report.md
- + .github/ISSUE_TEMPLATE/feature_request.md
- CHANGELOG.md
- trollimage/colormap.py
- trollimage/image.py
- trollimage/tests/test_colormap.py
- trollimage/tests/test_image.py
- trollimage/version.py
- trollimage/xrimage.py


Changes:

=====================================
.github/ISSUE_TEMPLATE.md deleted
=====================================
@@ -1,18 +0,0 @@
-#### Code Sample, a minimal, complete, and verifiable piece of code
-
-```python
-# Your code here
-
-```
-#### Problem description
-
-[this should also explain **why** the current behaviour is a problem and why the 
-expected output is a better solution.]
-
-#### Expected Output
-
-#### Actual Result, Traceback if applicable
-
-#### Versions of Python, package at hand and relevant dependencies
-
-Thank you for reporting an issue !


=====================================
.github/ISSUE_TEMPLATE/bug_report.md
=====================================
@@ -0,0 +1,34 @@
+---
+name: Bug report
+about: Create a report to help us improve
+labels: bug
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+If you're not sure how, please consider this
+[article on minimal bug reports](https://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports).
+
+**To Reproduce**
+
+```python
+# Your code here
+
+```
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Actual results**
+Text output of actual results or error messages including full tracebacks if applicable.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Environment Info:**
+ - OS: [e.g. OSX, Windows, Linux]
+ - Satpy Version: [e.g. 0.9.0]
+
+**Additional context**
+Add any other context about the problem here.


=====================================
.github/ISSUE_TEMPLATE/feature_request.md
=====================================
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+labels: enhancement
+
+---
+
+## Feature Request
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe any changes to existing user workflow**
+Are there any backwards compatibility concerns? Changes to the build process? Additional dependencies?
+
+**Additional context**
+Have you considered any alternative solutions or is there anything else that would help describe your request.


=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,28 @@
+## Version 1.16.0 (2021/10/12)
+
+### Issues Closed
+
+* [Issue 87](https://github.com/pytroll/trollimage/issues/87) - ReportError "ValueError: Merged colormap 'values' are not monotonically increasing." ([PR 91](https://github.com/pytroll/trollimage/pull/91) by [@djhoese](https://github.com/djhoese))
+* [Issue 84](https://github.com/pytroll/trollimage/issues/84) - allow customization of GDALMetaData tags for scale and offset ([PR 85](https://github.com/pytroll/trollimage/pull/85) by [@gerritholl](https://github.com/gerritholl))
+
+In this release 2 issues were closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 91](https://github.com/pytroll/trollimage/pull/91) - Fix colormaps not allowing merge points of the same value ([87](https://github.com/pytroll/trollimage/issues/87))
+* [PR 83](https://github.com/pytroll/trollimage/pull/83) - Fix palettize dtype for dask arrays
+
+#### Features added
+
+* [PR 88](https://github.com/pytroll/trollimage/pull/88) - List supported "simple image" formats in docstrings and error message ([86](https://github.com/pytroll/trollimage/issues/86), [1345](https://github.com/pytroll/satpy/issues/1345))
+* [PR 85](https://github.com/pytroll/trollimage/pull/85) - Allow customising scale and offset labels ([84](https://github.com/pytroll/trollimage/issues/84))
+* [PR 82](https://github.com/pytroll/trollimage/pull/82) - Add 'inplace' keyword argument to Colormap.reverse and Colormap.set_range
+
+In this release 5 pull requests were closed.
+
+
 ## Version 1.15.1 (2021/07/20)
 
 ### Pull Requests Merged


=====================================
trollimage/colormap.py
=====================================
@@ -134,7 +134,7 @@ def palettize(arr, colors, values):
 
 def _palettize_dask(darr, colors, values):
     """Apply a palette to a dask array."""
-    return darr.map_blocks(_palettize, values, dtype=colors.dtype)
+    return darr.map_blocks(_palettize, values, dtype=int)
 
 
 def _palettize(arr, values):
@@ -256,9 +256,9 @@ class Colormap(object):
         """Append colormap together."""
         old, other = self._normalize_color_arrays(self, other)
         values = np.concatenate((old.values, other.values))
-        if not (np.diff(values) > 0).all():
+        if not (np.diff(values) >= 0).all():
             raise ValueError("Merged colormap 'values' are not monotonically "
-                             "increasing.")
+                             "increasing or equal.")
         colors = np.concatenate((old.colors, other.colors))
         return Colormap(
             values=values,
@@ -273,17 +273,56 @@ class Colormap(object):
             return cmap1, cmap2
         return cmap1.to_rgba(), cmap2.to_rgba()
 
-    def reverse(self):
-        """Reverse the current colormap in place."""
-        self.colors = np.flipud(self.colors)
+    def reverse(self, inplace=True):
+        """Reverse the current colormap in place.
 
-    def set_range(self, min_val, max_val):
-        """Set the range of the colormap to [*min_val*, *max_val*]."""
+        Args:
+            inplace (bool): If True (default), modify the colors of this
+                Colormap inplace. If False, return a new instance.
+
+        """
+        colors = np.flipud(self.colors)
+        if not inplace:
+            return Colormap(
+                values=self.values.copy(),
+                colors=colors
+            )
+        self.colors = colors
+        return self
+
+    def set_range(self, min_val, max_val, inplace=True):
+        """Set the range of the colormap to [*min_val*, *max_val*].
+
+        If min is greater than max then the Colormap's colors are reversed
+        before values are updated to the new range. This is done because
+        Colormap ``values`` must always be monotonically increasing.
+
+        Args:
+            min_val (float): New minimum value for the control points in
+                this colormap.
+            max_val (float): New maximum value for the control points in
+                this colormap.
+            inplace (bool): If True (default), modify the values inplace..
+                If False, return a new Colormap instance.
+
+        """
         if min_val > max_val:
+            cmap = self.reverse(inplace=inplace)
             max_val, min_val = min_val, max_val
-        self.values = (((self.values * 1.0 - self.values.min()) /
-                        (self.values.max() - self.values.min()))
-                       * (max_val - min_val) + min_val)
+        else:
+            cmap = self
+
+        values = (((cmap.values * 1.0 - cmap.values.min()) /
+                   (cmap.values.max() - cmap.values.min()))
+                  * (max_val - min_val) + min_val)
+        if not inplace:
+            return Colormap(
+                values=values,
+                colors=cmap.colors.copy()
+            )
+
+        cmap.values = values
+        return cmap
 
     def to_rio(self):
         """Convert the colormap to a rasterio colormap."""


=====================================
trollimage/image.py
=====================================
@@ -42,6 +42,22 @@ except ImportError:
 
 logger = logging.getLogger(__name__)
 
+PIL_IMAGE_FORMATS = Pil.registered_extensions()
+
+
+def _pprint_pil_formats():
+    res = ''
+    row = []
+    for i in PIL_IMAGE_FORMATS:
+        if len(row) > 12:
+            res = res + ", ".join(row) + ",\n"
+            row = []
+        row.append(i)
+    return res + ", ".join(row)
+
+
+PIL_IMAGE_FORMATS_STR = _pprint_pil_formats()
+
 
 def ensure_dir(filename):
     """Checks if the dir of f exists, otherwise create it.
@@ -58,30 +74,17 @@ class UnknownImageFormat(Exception):
 
 
 def check_image_format(fformat):
-    """Check that *fformat* is valid
+    """Check that *fformat* is valid.
+
+    Valid formats are listed in https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
     """
-    cases = {"jpg": "jpeg",
-             "jpeg": "jpeg",
-             "tif": "tiff",
-             "tiff": "tif",
-             "pgm": "ppm",
-             "pbm": "ppm",
-             "ppm": "ppm",
-             "bmp": "bmp",
-             "dib": "bmp",
-             "gif": "gif",
-             "im": "im",
-             "pcx": "pcx",
-             "png": "png",
-             "xbm": "xbm",
-             "xpm": "xpm",
-             'jp2': 'jp2',
-             }
     fformat = fformat.lower()
     try:
-        fformat = cases[fformat]
+        fformat = PIL_IMAGE_FORMATS["." + fformat]
     except KeyError:
-        raise UnknownImageFormat("Unknown image format '%s'." % fformat)
+        raise UnknownImageFormat(
+            "Unknown image format '%s'.  Supported formats for 'simple_image' writer are:\n%s" %
+            (fformat, PIL_IMAGE_FORMATS_STR))
     return fformat
 
 
@@ -178,7 +181,7 @@ class Image(object):
                         color_min = color_range[i][0]
                         color_max = color_range[i][1]
                         # Add data to image object as a channel
-                        #self._add_channel(chn, color_min, color_max)
+                        # self._add_channel(chn, color_min, color_max)
                     else:
                         color_min = 0.0
                         color_max = 1.0
@@ -366,9 +369,12 @@ class Image(object):
 
     def pil_save(self, filename, compression=6, fformat=None,
                  thumbnail_name=None, thumbnail_size=None):
-        """Save the image to the given *filename* using PIL. For now, the
-        compression level [0-9] is ignored, due to PIL's lack of support. See
-        also :meth:`save`.
+        """Save the image to the given *filename* using PIL.
+
+        For now, the compression level [0-9] is ignored, due to PIL's lack of support.
+        See also :meth:`save`.
+
+        Supported image formats are listed in https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
         """
         # PIL does not support compression option.
         del compression
@@ -384,12 +390,12 @@ class Image(object):
 
         params = {}
 
-        if fformat == 'png':
+        if fformat == 'PNG':
             # Take care of GeoImage.tags (if any).
             params['pnginfo'] = self._pngmeta()
 
         # JPEG images does not support transparency
-        if fformat == 'jpeg' and not self.fill_value:
+        if fformat == 'JPEG' and not self.fill_value:
             self.fill_value = [0, 0, 0, 0]
             logger.debug("No fill_value provided, setting it to 0.")
 


=====================================
trollimage/tests/test_colormap.py
=====================================
@@ -125,6 +125,7 @@ class TestColormapClass(unittest.TestCase):
         self.assertTrue(np.allclose(channels.compute(), [[0, 1, 2, 3],
                                                          [0, 1, 2, 3],
                                                          [0, 1, 2, 3]]))
+        assert channels.dtype == int
 
     def test_set_range(self):
         """Test set_range."""
@@ -357,3 +358,93 @@ class TestColormap:
         )
         new_cmap = cmap1 + cmap2
         assert new_cmap.values.shape[0] == colors1.shape[0] + colors2.shape[0]
+
+    def test_merge_equal_values(self):
+        """Test that merged colormaps can have equal values at the merge point."""
+        cmap1 = colormap.Colormap(
+            colors=np.arange(5 * 3).reshape((5, 3)),
+            values=np.linspace(0, 1, 5),
+        )
+        cmap2 = colormap.Colormap(
+            colors=np.arange(5 * 3).reshape((5, 3)),
+            values=np.linspace(1, 2, 5),
+        )
+        assert cmap1.values[-1] == cmap2.values[0]
+        # this should succeed
+        _ = cmap1 + cmap2
+
+    @pytest.mark.parametrize('inplace', [False, True])
+    def test_reverse(self, inplace):
+        """Test colormap reverse."""
+        values = np.linspace(0.2, 0.5, 10)
+        colors = np.repeat(np.linspace(0.2, 0.8, 10)[:, np.newaxis], 3, 1)
+        orig_values = values.copy()
+        orig_colors = colors.copy()
+
+        cmap = colormap.Colormap(values=values, colors=colors)
+        new_cmap = cmap.reverse(inplace)
+        self._assert_inplace_worked(cmap, new_cmap, inplace)
+        self._compare_reversed_colors(cmap, new_cmap, inplace, orig_colors)
+        self._assert_unchanged_values(cmap, new_cmap, inplace, orig_values)
+
+    @pytest.mark.parametrize(
+        'new_range',
+        [
+            (0.0, 1.0),
+            (1.0, 0.0),
+            (210.0, 300.0),
+            (300.0, 210.0),
+        ])
+    @pytest.mark.parametrize('inplace', [False, True])
+    def test_set_range(self, new_range, inplace):
+        """Test 'set_range' method."""
+        values = np.linspace(0.2, 0.5, 10)
+        colors = np.repeat(np.linspace(0.2, 0.8, 10)[:, np.newaxis], 3, 1)
+        orig_values = values.copy()
+        orig_colors = colors.copy()
+
+        cmap = colormap.Colormap(values=values, colors=colors)
+        new_cmap = cmap.set_range(*new_range, inplace)
+        self._assert_inplace_worked(cmap, new_cmap, inplace)
+        self._assert_monotonic_values(cmap)
+        self._assert_monotonic_values(new_cmap)
+        self._assert_values_changed(cmap, new_cmap, inplace, orig_values)
+        if new_range[0] > new_range[1]:
+            self._compare_reversed_colors(cmap, new_cmap, inplace, orig_colors)
+
+    @staticmethod
+    def _assert_monotonic_values(cmap):
+        np.testing.assert_allclose(np.diff(cmap.values) > 0, True)
+
+    @staticmethod
+    def _assert_unchanged_values(cmap, new_cmap, inplace, orig_values):
+        if inplace:
+            np.testing.assert_allclose(cmap.values, orig_values)
+        else:
+            np.testing.assert_allclose(cmap.values, orig_values)
+            np.testing.assert_allclose(new_cmap.values, orig_values)
+
+    @staticmethod
+    def _compare_reversed_colors(cmap, new_cmap, inplace, orig_colors):
+        if inplace:
+            assert cmap is new_cmap
+            np.testing.assert_allclose(cmap.colors, orig_colors[::-1])
+        else:
+            assert cmap is not new_cmap
+            np.testing.assert_allclose(cmap.colors, orig_colors)
+            np.testing.assert_allclose(new_cmap.colors, orig_colors[::-1])
+
+    @staticmethod
+    def _assert_values_changed(cmap, new_cmap, inplace, orig_values):
+        assert not np.allclose(new_cmap.values, orig_values)
+        if not inplace:
+            np.testing.assert_allclose(cmap.values, orig_values)
+        else:
+            assert not np.allclose(cmap.values, orig_values)
+
+    @staticmethod
+    def _assert_inplace_worked(cmap, new_cmap, inplace):
+        if not inplace:
+            assert new_cmap is not cmap
+        else:
+            assert new_cmap is cmap


=====================================
trollimage/tests/test_image.py
=====================================
@@ -2123,11 +2123,14 @@ class TestXRImageSaveScaleOffset(unittest.TestCase):
         expected_tags = {'scale': 24.0 / 255, 'offset': 0}
 
         self.img.stretch()
-        self._save_and_check_tags(expected_tags)
+        with pytest.warns(DeprecationWarning):
+            self._save_and_check_tags(
+                expected_tags,
+                include_scale_offset_tags=True)
 
-    def _save_and_check_tags(self, expected_tags):
+    def _save_and_check_tags(self, expected_tags, **kwargs):
         with NamedTemporaryFile(suffix='.tif') as tmp:
-            self.img.save(tmp.name, include_scale_offset_tags=True)
+            self.img.save(tmp.name, **kwargs)
 
             import rasterio as rio
             with rio.open(tmp.name) as f:
@@ -2141,7 +2144,18 @@ class TestXRImageSaveScaleOffset(unittest.TestCase):
         expected_tags = {'scale': 23.0 / 255, 'offset': 1}
 
         self.img.crude_stretch([1], [24])
-        self._save_and_check_tags(expected_tags)
+        self._save_and_check_tags(
+                expected_tags,
+                scale_offset_tags=("scale", "offset"))
+
+    @pytest.mark.skipif(sys.platform.startswith('win'), reason="'NamedTemporaryFile' not supported on Windows")
+    def test_save_scale_offset_custom_labels(self):
+        """Test saving GeoTIFF with different scale/offset tag labels."""
+        expected_tags = {"gradient": 24.0 / 255, "axis_intercept": 0}
+        self.img.stretch()
+        self._save_and_check_tags(
+                expected_tags,
+                scale_offset_tags=("gradient", "axis_intercept"))
 
 
 def _get_tags_after_writing_to_geotiff(data):


=====================================
trollimage/version.py
=====================================
@@ -23,9 +23,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.15.1)"
-    git_full = "c160f34f1c773a927c9e944f727003cbd185dee5"
-    git_date = "2021-07-20 09:06:09 -0500"
+    git_refnames = " (HEAD -> main, tag: v1.16.0)"
+    git_full = "844ca2a2a005fe3016bda9dca3db294b0b71f0c1"
+    git_date = "2021-10-12 19:58:23 -0500"
     keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
     return keywords
 


=====================================
trollimage/xrimage.py
=====================================
@@ -428,6 +428,7 @@ class XRImage(object):
                  keep_palette=False, cmap=None, overviews=None,
                  overviews_minsize=256, overviews_resampling=None,
                  include_scale_offset_tags=False,
+                 scale_offset_tags=None,
                  **format_kwargs):
         """Save the image using rasterio.
 
@@ -464,9 +465,10 @@ class XRImage(object):
                 provided. Common values include `nearest` (default),
                 `bilinear`, `average`, and many others. See the rasterio
                 documentation for more information.
-            include_scale_offset_tags (bool): Whether or not (default) to
-                include a ``scale`` and an ``offset`` tag in the data that would
-                help retrieving original data values from pixel values.
+            scale_offset_tags (Tuple[str, str] or None)
+                If set to a ``(str, str)`` tuple, scale and offset will be
+                stored in GDALMetaData tags.  Those can then be used to
+                retrieve the original data values from pixel values.
 
         Returns:
             The delayed or computed result of the saving.
@@ -479,6 +481,12 @@ class XRImage(object):
                    'tiff': 'GTiff',
                    'jp2': 'JP2OpenJPEG'}
         driver = drivers.get(fformat, fformat)
+        if include_scale_offset_tags:
+            warnings.warn(
+                "include_scale_offset_tags is deprecated, please use "
+                "scale_offset_tags to indicate tag labels",
+                DeprecationWarning)
+            scale_offset_tags = scale_offset_tags or ("scale", "offset")
 
         if tags is None:
             tags = {}
@@ -537,9 +545,10 @@ class XRImage(object):
         elif driver == 'JPEG' and 'A' in mode:
             raise ValueError('JPEG does not support alpha')
 
-        if include_scale_offset_tags:
+        if scale_offset_tags:
+            scale_label, offset_label = scale_offset_tags
             scale, offset = self.get_scaling_from_history(data.attrs.get('enhancement_history', []))
-            tags['scale'], tags['offset'] = invert_scale_offset(scale, offset)
+            tags[scale_label], tags[offset_label] = invert_scale_offset(scale, offset)
 
         # FIXME add metadata
         r_file = RIOFile(filename, 'w', driver=driver,



View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/-/commit/64580fed927a7932759b2da824fee49321f868c8

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/-/commit/64580fed927a7932759b2da824fee49321f868c8
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/20211016/86101c0e/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list