[Git][debian-gis-team/trollimage][upstream] New upstream version 1.20.0
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Mon Jan 16 06:49:47 GMT 2023
Antonio Valentino pushed to branch upstream at Debian GIS Project / trollimage
Commits:
d747012f by Antonio Valentino at 2023-01-14T08:33:13+00:00
New upstream version 1.20.0
- - - - -
13 changed files:
- + .github/dependabot.yml
- .github/workflows/ci.yaml
- .github/workflows/deploy-sdist.yaml
- + .readthedocs.yml
- CHANGELOG.md
- doc/conf.py
- rtd_requirements.txt
- setup.cfg
- trollimage/colormap.py
- trollimage/image.py
- trollimage/tests/test_colormap.py
- trollimage/tests/test_image.py
- trollimage/version.py
Changes:
=====================================
.github/dependabot.yml
=====================================
@@ -0,0 +1,11 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "github-actions" # See documentation for possible values
+ directory: "/" # Location of package manifests
+ schedule:
+ interval: "weekly"
=====================================
.github/workflows/ci.yaml
=====================================
@@ -25,7 +25,7 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout at v2
+ uses: actions/checkout at v3
- name: Setup Conda Environment
uses: conda-incubator/setup-miniconda at v2
@@ -67,7 +67,7 @@ jobs:
pytest --cov=trollimage trollimage/tests --cov-report=xml
- name: Upload unittest coverage to Codecov
- uses: codecov/codecov-action at v1
+ uses: codecov/codecov-action at v3
with:
flags: unittests
file: ./coverage.xml
=====================================
.github/workflows/deploy-sdist.yaml
=====================================
@@ -11,7 +11,7 @@ jobs:
steps:
- name: Checkout source
- uses: actions/checkout at v2
+ uses: actions/checkout at v3
- name: Create sdist
shell: bash -l {0}
@@ -19,7 +19,7 @@ jobs:
- name: Publish package to PyPI
if: github.event.action == 'published'
- uses: pypa/gh-action-pypi-publish at v1.4.1
+ uses: pypa/gh-action-pypi-publish at v1.6.4
with:
user: __token__
password: ${{ secrets.pypi_password }}
\ No newline at end of file
=====================================
.readthedocs.yml
=====================================
@@ -0,0 +1,13 @@
+version: 2
+
+build:
+ os: "ubuntu-20.04"
+ tools:
+ python: "3.10"
+sphinx:
+ configuration: doc/conf.py
+python:
+ install:
+ - requirements: rtd_requirements.txt
+ - method: pip
+ path: .
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,23 @@
+## Version 1.20.0 (2023/01/11)
+
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 118](https://github.com/pytroll/trollimage/pull/118) - Fix image colorization ([26](https://github.com/pytroll/pydecorate/issues/26))
+
+#### Features added
+
+* [PR 117](https://github.com/pytroll/trollimage/pull/117) - Refactor colormap creation ([2308](https://github.com/pytroll/satpy/issues/2308))
+
+#### Documentation changes
+
+* [PR 110](https://github.com/pytroll/trollimage/pull/110) - Add readthedocs config file to force newer dependencies
+
+In this release 3 pull requests were closed.
+
+
## Version 1.19.0 (2022/10/21)
### Issues Closed
=====================================
doc/conf.py
=====================================
@@ -121,7 +121,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
+html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
=====================================
rtd_requirements.txt
=====================================
@@ -3,3 +3,6 @@ rasterio
xarray
dask[array]
docutils<0.18
+sphinx>=5,<6
+sphinx-rtd-theme>=1.1.0,<2.0
+
=====================================
setup.cfg
=====================================
@@ -7,6 +7,8 @@ universal=1
[flake8]
max-line-length = 120
+exclude =
+ doc/conf.py
[versioneer]
VCS = git
=====================================
trollimage/colormap.py
=====================================
@@ -22,11 +22,14 @@
"""A simple colormap module."""
import contextlib
+import copy
import os
from io import StringIO
from typing import Optional
import warnings
from numbers import Number
+import pathlib
+import sys
import numpy as np
from trollimage.colorspaces import rgb2hcl, hcl2rgb
@@ -399,15 +402,14 @@ class Colormap(object):
@classmethod
def from_file(
cls,
- filename_or_string: str,
+ filename: str,
colormap_mode: Optional[str] = None,
color_scale: Number = 255,
):
"""Create Colormap from a comma-separated or binary file of colormap data.
Args:
- filename_or_string: Filename of a binary or CSV file or a
- string version of the comma-separate data.
+ filename: Filename of a binary or CSV file
colormap_mode: Force the scheme of the colormap data (ex. RGBA).
See information below on other possible values and how they
are interpreted. By default this is determined based on the
@@ -425,8 +427,7 @@ class Colormap(object):
:func:`numpy.load`. All other extensions are
read as a comma-separated file. For ``.npz`` files the data must be stored
as a positional list where the first element represents the colormap to
- use. See :func:`numpy.savez` for more information. The filename should
- be an absolute path for consistency.
+ use. See :func:`numpy.savez` for more information.
The colormap is interpreted as 1 of 4 different "colormap modes":
``RGB``, ``RGBA``, ``VRGB``, or ``VRGBA``. The
@@ -445,6 +446,13 @@ class Colormap(object):
See the "Color Scale" section below for more information on the value
range of provided numbers.
+ To read from a string containing CSV, use :meth:`~Colormap.from_csv`.
+
+ To get a named colormap, use :meth:`~Colormap.from_name` or load the
+ colormap directly as a module attribute.
+
+ To get a colormap from an ndarray, use :meth:`~Colormap.from_ndarray`.
+
**Color Scale**
By default colors are expected to be in a 0-255 range. This
@@ -452,16 +460,250 @@ class Colormap(object):
A common alternative to 255 is ``1`` to specify floating
point numbers between 0 and 1. The resulting Colormap uses the normalized
color values (0-1).
+ """
+ if _is_actually_a_csv_string(filename):
+ warnings.warn(
+ "Passing a data string to Colormap.from_file is deprecated. "
+ "Please use Colormap.from_string.",
+ category=DeprecationWarning)
+ return cls.from_string(filename, colormap_mode, color_scale)
+ values, colors = _get_values_colors_from_file(filename, colormap_mode, color_scale)
+ return cls(values=values, colors=colors)
+
+ @classmethod
+ def from_string(cls, string, *args, **kwargs):
+ """Create colormap from string.
+
+ Create a colormap from a string that contains comma seperated values
+ (CSV).
+
+ To read from an external file that contains CSV, use :meth:`from_csv`.
+
+ Args:
+ string (str): String containing CSV. Must have no less than three
+ and no more than five columns and describe entirely numeric
+ data.
+ colormap_mode (str or None): Optional. Can be None, "RGB", "RGBA", "VRGB", or
+ "VRGBA". If None (default), this is inferred from the dimensions of
+ the data contained in the CSV. Modes starting with V have in
+ the first column the values to which the color relates.
+ color_scale (number): The value that represents white in the
+ numbers describing the colors. Defaults to 255, could also be 1
+ or something else.
+ """
+ openfile = StringIO(string)
+ cmap_data = _read_colormap_data_from_file(openfile)
+ return cls.from_ndarray(cmap_data, *args, **kwargs)
+
+ @classmethod
+ def from_np(cls, path, *args, **kwargs):
+ """Create Colormap from a numpy-file.
+
+ Create a colormap from a numpy data file ``.npy`` or ``.npz``.
+
+ The data should contain at least three and at most five columns.
+ Args:
+ path (str or Pathlib.Path): Path to file containing numpy data.
+ colormap_mode (str or None): Optional. Can be None, "RGB", "RGBA", "VRGB", or
+ "VRGBA". If None (default), this is inferred from the dimensions of
+ the data contained in the CSV. Modes starting with V have in
+ the first column the values to which the color relates.
+ color_scale (number): The value that represents white in the
+ numbers describing the colors. Defaults to 255, could also be 1
+ or something else.
+ """
+ cmap_data = _read_colormap_data_from_np(path)
+ return cls.from_ndarray(cmap_data, *args, **kwargs)
+
+ @classmethod
+ def from_csv(cls, path, colormap_mode=None, color_scale=255):
+ """Create Colormap from CSV file.
+
+ Create a Colormap from a file that contains comma seperated values
+ (CSV).
+
+ To read from a string that contains CSV, use :meth:`from_string`.
+
+ Args:
+ string (str or pathlib.Path): Path to file containing CSV.
+ The CSV must have at least three and at most five columns and
+ describe entirely numeric data.
+ colormap_mode (str or None): Optional. Can be None, "RGB", "RGBA", "VRGB", or
+ "VRGBA". If None (default), this is inferred from the dimensions of
+ the data contained in the CSV. Modes starting with V have in
+ the first column the values to which the color relates.
+ color_scale (number): The value that represents white in the
+ numbers describing the colors. Defaults to 255, could also be 1
+ or something else.
+ """
+ cmap_data = np.loadtxt(path, delimiter=",")
+ return cls.from_ndarray(cmap_data, colormap_mode, color_scale)
+
+ @classmethod
+ def from_ndarray(cls, cmap_data, colormap_mode=None, color_scale=255):
+ """Create Colormap from ndarray.
+
+ Create a colormap from a numpy data array.
+
+ The data should contain at least three and at most five columns.
+
+ For historical reasons, this method exists alongside
+ :meth:`from_sequence_of_colors` and :meth:`from_array_with_metadata` despite similar
+ functionality.
+
+ Args:
+ cmap_data (ndarray): Array describing the colours.
+ Must have at least three and at most five columns and
+ have a numeric dtype.
+ colormap_mode (str or None): Optional. Can be None, "RGB", "RGBA", "VRGB", or
+ "VRGBA". If None (default), this is inferred from the dimensions of
+ the data contained in the CSV. Modes starting with V have in
+ the first column the values to which the color relates.
+ color_scale (number): The value that represents white in the
+ numbers describing the colors. Defaults to 255, could also be 1
+ or something else.
"""
- if not os.path.isfile(filename_or_string):
- filename_or_string = StringIO(filename_or_string)
- values, colors = _get_values_colors_from_file(filename_or_string, colormap_mode, color_scale)
+ values, colors = _get_values_colors_from_ndarray(cmap_data, colormap_mode, color_scale)
return cls(values=values, colors=colors)
+ @classmethod
+ def from_name(cls, name):
+ """Return named colormap.
+
+ Return a colormap by name. Supported colormaps are the ones defined in
+ the module namespace.
+
+ Args:
+ name (str): Name of colormap.
+ """
+ cmap = getattr(sys.modules[__name__], name)
+ return copy.copy(cmap)
+
+ @classmethod
+ def from_sequence_of_colors(cls, colors, values=None, color_scale=255):
+ """Create Colormap from sequence of colors.
+
+ Create a colormap from a sequence of colors, such as a list of colors.
+ If values is not given, assume values between 0 and 1, linearly spaced
+ according to the total number of colors.
+
+ For historical reasons, this method exists alongside
+ :meth:`from_ndarray` and :meth:`from_array_with_metadata` despite similar
+ functionality.
+
+ Args:
+ colors (Sequence): List of colors, where each element must itself
+ be a sequence of 3 or 4 numbers (RGB or RGBA).
+ values (array, optional): Values associated with the colors. If
+ not given, assume linear between 0 and 1.
+ color_scale (number): The value that represents white in the
+ numbers describing the colors. Defaults to 255, could also be 1
+ or something else.
+ """
+ # this method was moved from satpy. where it was in
+ # satpy.enhancements.create_colormap
+ # then it was refactored/rewritten
+ color_array = np.array(colors)
+ if values is None:
+ values = np.linspace(0, 1, len(colors))
+ else:
+ values = np.asarray(values)
+ color_array = np.concatenate((values[:, np.newaxis], color_array), axis=1)
+ return cls.from_ndarray(
+ color_array,
+ "VRGB" if color_array.shape[1] == 4 else "VRGBA",
+ color_scale=color_scale)
+
+ @classmethod
+ def from_array_with_metadata(
+ cls, palette, dtype, color_scale=255,
+ valid_range=None, scale_factor=1, add_offset=0,
+ remove_last=True):
+ """Create Colormap from an array with metadata.
+
+ Create a colormap from an array with associated metadata, either in
+ attributes or passed on to the function.
+
+ For historical reasons, this method exists alongside
+ :meth:`from_ndarray` and :meth:`from_sequence_of_colors` despite similar
+ functionality.
+
+ If ``palette`` is an xarray dataarray with the attribute
+ ``palette_meanings``, those meanings are interpreted as values
+ associated with the colormap.
+
+ If no values can be interpreted from the metadata, values will be
+ linearly interpolated between 0 and 255 (if ``dtype`` is ``np.uint8``)
+ or according to ``valid_range``.
+
+ Args:
+
+ palette (ndarray or xarray.DataArray)
+ Array describing colors, possibly with metadata. If it has a
+ ``palette_meanings`` attribute, this will be used for color
+ interpretation.
+ dtype
+ dtype for the colormap
+ color_scale (number): The value that represents white in the
+ numbers describing the colors. Defaults to 255, could also be 1
+ or something else.
+ valid_range
+ valid range for colors, if colormap is not of dtype uint8
+ scale_factor
+ scale factor to apply to the colormap
+ add_offset
+ add offset to apply to the colormap
+ remove_last
+ Remove the last value if the array has no metadata associated.
+ Defaults to true for historical reasons.
+
+ """
+ # this method was moved from satpy, where it was in
+ # satpy.composites.ColormapCompositor.build_colormap
+ #
+ # then it was refactored/rewritten for trollimage
+ squeezed_palette = np.asanyarray(palette).squeeze()
+ set_range = True
+ if hasattr(palette, 'attrs') and 'palette_meanings' in palette.attrs:
+ set_range = False
+ values = np.asarray(palette.attrs['palette_meanings'])
+ else:
+ # remove the last value because monkeys don't like water sprays
+ # on a more serious note, I don't know why we are removing the last
+ # value here, but this behaviour was copied from ancient satpy code
+ values = np.arange(squeezed_palette.shape[0]-remove_last)
+ if remove_last:
+ squeezed_palette = squeezed_palette[:-remove_last, :]
+
+ color_array = np.concatenate((values[:, np.newaxis], squeezed_palette), axis=1)
+ colormap = cls.from_ndarray(
+ color_array,
+ "VRGB" if color_array.shape[1] == 4 else "VRGBA",
+ color_scale=color_scale)
+ if valid_range is not None:
+ if set_range:
+ colormap.set_range(
+ *(np.array(valid_range) * scale_factor
+ + add_offset))
+ else:
+ if dtype != np.dtype("uint8"):
+ raise AttributeError("Data need to have either a valid_range or be of type uint8" +
+ " in order to be displayable with an attached color-palette!")
+ return colormap
+
+
+def _is_actually_a_csv_string(string):
+ """Try to guess whether this string contains CSV."""
+ return string.count("\n") > 0 and string.count(",") > 0
+
def _get_values_colors_from_file(filename, colormap_mode, color_scale):
data = _read_colormap_data_from_file(filename)
+ return _get_values_colors_from_ndarray(data, colormap_mode, color_scale)
+
+
+def _get_values_colors_from_ndarray(data, colormap_mode, color_scale):
cols = data.shape[1]
default_modes = {
3: 'RGB',
@@ -492,16 +734,21 @@ def _read_colormap_data_from_file(filename_or_file_obj):
if isinstance(filename_or_file_obj, str):
ext = os.path.splitext(filename_or_file_obj)[1]
if ext in (".npy", ".npz"):
- file_content = np.load(filename_or_file_obj)
- if ext == ".npz":
- # .npz is a collection
- # assume position list-like and get the first element
- file_content = file_content["arr_0"]
- return file_content
+ return _read_colormap_data_from_np(filename_or_file_obj)
# CSV file or file-like object of CSV string data
return np.loadtxt(filename_or_file_obj, delimiter=",")
+def _read_colormap_data_from_np(path):
+ path = pathlib.Path(path)
+ file_content = np.load(path)
+ if path.suffix == ".npz":
+ # .npz is a collection
+ # assume position list-like and get the first element
+ file_content = file_content["arr_0"]
+ return file_content
+
+
# matlab jet "#00007F", "blue", "#007FFF", "cyan", "#7FFF7F", "yellow",
# "#FF7F00", "red", "#7F0000"
=====================================
trollimage/image.py
=====================================
@@ -117,14 +117,14 @@ class Image(object):
self._secondary_mode = "RGB"
- if(channels is not None and
- not isinstance(channels, (tuple, set, list,
- np.ndarray, np.ma.core.MaskedArray))):
+ if (channels is not None and
+ not isinstance(channels, (tuple, set, list,
+ np.ndarray, np.ma.core.MaskedArray))):
raise TypeError("Image channels should a tuple, set, list, numpy "
"array, or masked array.")
- if(isinstance(channels, (tuple, list)) and
- len(channels) != len(re.findall("[A-Z]", mode))):
+ if (isinstance(channels, (tuple, list)) and
+ len(channels) != len(re.findall("[A-Z]", mode))):
errmsg = ("Number of channels (" +
"{n}) does not match mode {mode}.".format(
n=len(channels), mode=mode))
@@ -136,20 +136,20 @@ class Image(object):
if mode not in self.modes:
raise ValueError("Unknown mode.")
- if(color_range is not None and
- not _is_pair(color_range) and
- not _is_list_of_pairs(color_range)):
+ if (color_range is not None and
+ not _is_pair(color_range) and
+ not _is_list_of_pairs(color_range)):
raise ValueError("Color_range should be a pair"
" or a list/tuple/set of pairs.")
- if(color_range is not None and
- _is_list_of_pairs(color_range) and
- (channels is None or
+ if (color_range is not None and
+ _is_list_of_pairs(color_range) and
+ (channels is None or
len(color_range) != len(channels))):
raise ValueError("Color_range length does not match number of "
"channels.")
- if(color_range is not None and
- (((mode == "L" or mode == "P") and not _is_pair(color_range)) and
+ if (color_range is not None and
+ (((mode == "L" or mode == "P") and not _is_pair(color_range)) and
(len(color_range) != len(re.findall("[A-Z]", mode))))):
raise ValueError("Color_range does not match mode")
@@ -256,8 +256,8 @@ class Image(object):
def is_empty(self):
"""Check for an empty image."""
- if(((self.channels == []) and (not self.shape == (0, 0))) or
- ((not self.channels == []) and (self.shape == (0, 0)))):
+ if (((self.channels == []) and (not self.shape == (0, 0))) or
+ ((not self.channels == []) and (self.shape == (0, 0)))):
raise RuntimeError("Channels-shape mismatch.")
return self.channels == [] and self.shape == (0, 0)
@@ -425,9 +425,7 @@ class Image(object):
def putalpha(self, alpha):
"""Add an *alpha* channel to the current image, or replaces it with *alpha* if it already exists."""
alpha = np.ma.array(alpha)
- if(not (alpha.shape[0] == 0 and
- self.shape[0] == 0) and
- alpha.shape != self.shape):
+ if (not (alpha.shape[0] == 0 and self.shape[0] == 0) and alpha.shape != self.shape):
raise ValueError("Alpha channel shape should match image shape")
if not self.mode.endswith("A"):
@@ -736,8 +734,7 @@ class Image(object):
else:
factor[1] = self.width * 1.0 / shape[1]
- if(int(factor[0]) != factor[0] or
- int(factor[1]) != factor[1]):
+ if (int(factor[0]) != factor[0] or int(factor[1]) != factor[1]):
raise ValueError("Resize not of integer factor!")
factor[0] = int(factor[0])
@@ -822,8 +819,7 @@ class Image(object):
are several channels in the image. The behaviour of :func:`gamma` is
undefined outside the normal [0,1] range of the channels.
"""
- if(isinstance(gamma, (list, tuple, set)) and
- len(gamma) != len(self.channels)):
+ if (isinstance(gamma, (list, tuple, set)) and len(gamma) != len(self.channels)):
raise ValueError("Number of channels and gamma components differ.")
if isinstance(gamma, (tuple, list)):
gamma_list = list(gamma)
@@ -876,8 +872,7 @@ class Image(object):
if self.mode.endswith("A"):
ch_len -= 1
- if((isinstance(stretch, tuple) or
- isinstance(stretch, list))):
+ if ((isinstance(stretch, tuple) or isinstance(stretch, list))):
if len(stretch) == 2:
for i in range(ch_len):
self.stretch_linear(i, cutoffs=stretch, **kwargs)
@@ -911,8 +906,7 @@ class Image(object):
Note: 'Inverting' means that black becomes white, and vice-versa, not that the values are negated!
"""
- if(isinstance(invert, (tuple, list)) and
- len(self.channels) != len(invert)):
+ if (isinstance(invert, (tuple, list)) and len(self.channels) != len(invert)):
raise ValueError(
"Number of channels and invert components differ.")
@@ -929,8 +923,7 @@ class Image(object):
"""Stretch the current image's colors by performing histogram equalization on channel *ch_nb*."""
logger.info("Perform a histogram equalized contrast stretch.")
- if(self.channels[ch_nb].size ==
- np.ma.count_masked(self.channels[ch_nb])):
+ if (self.channels[ch_nb].size == np.ma.count_masked(self.channels[ch_nb])):
logger.warning("Nothing to stretch !")
return
@@ -978,9 +971,8 @@ class Image(object):
"""
logger.debug("Perform a linear contrast stretch.")
- if((self.channels[ch_nb].size ==
- np.ma.count_masked(self.channels[ch_nb])) or
- self.channels[ch_nb].min() == self.channels[ch_nb].max()):
+ if ((self.channels[ch_nb].size == np.ma.count_masked(self.channels[ch_nb])) or
+ self.channels[ch_nb].min() == self.channels[ch_nb].max()):
logger.warning("Nothing to stretch !")
return
@@ -1019,9 +1011,8 @@ class Image(object):
if isinstance(max_stretch, (list, tuple)):
max_stretch = max_stretch[ch_nb]
- if((not self.channels[ch_nb].mask.all()) and
- abs(max_stretch - min_stretch) > 0):
- stretched = self.channels[ch_nb].data.astype(np.float)
+ if ((not self.channels[ch_nb].mask.all()) and abs(max_stretch - min_stretch) > 0):
+ stretched = self.channels[ch_nb].data.astype(float)
stretched -= min_stretch
stretched /= max_stretch - min_stretch
self.channels[ch_nb] = np.ma.array(stretched,
@@ -1060,7 +1051,7 @@ class Image(object):
alpha = self.channels[1]
else:
alpha = None
- self.channels = colormap.colorize(self.channels[0])
+ self.channels = list(colormap.colorize(self.channels[0]))
if alpha is not None:
self.channels.append(alpha)
self.mode = "RGBA"
=====================================
trollimage/tests/test_colormap.py
=====================================
@@ -27,6 +27,7 @@ import unittest
from trollimage import colormap
import numpy as np
from tempfile import NamedTemporaryFile
+import xarray
import pytest
@@ -628,6 +629,96 @@ class TestFromFileCreation:
with pytest.raises(ValueError):
colormap.Colormap.from_file(cmap_filename)
+ def test_cmap_from_np(self, tmp_path):
+ """Test creating a colormap from a numpy file."""
+ cmap_data = _generate_cmap_test_data(None, "RGB")
+ fnp = tmp_path / "test.npy"
+ np.save(fnp, cmap_data)
+ cmap = colormap.Colormap.from_np(fnp, color_scale=1)
+ np.testing.assert_allclose(cmap.values, [0, 0.33333333, 0.6666667, 1])
+ np.testing.assert_array_equal(cmap.colors, cmap_data)
+
+ def test_cmap_from_csv(self, tmp_path, color_scale=1):
+ """Test creating a colormap from a CSV file."""
+ cmap_data = _generate_cmap_test_data(None, "RGB")
+ fnp = tmp_path / "test.csv"
+ np.savetxt(fnp, cmap_data, delimiter=",")
+ cmap = colormap.Colormap.from_csv(fnp, color_scale=1)
+ np.testing.assert_allclose(cmap.values, [0, 0.33333333, 0.66666667, 1])
+ np.testing.assert_array_equal(cmap.colors, cmap_data)
+
+
+def test_cmap_from_string():
+ """Test creating a colormap from a string."""
+ s = "0,0,0,0\n1,1,1,1\n2,2,2,2"
+ cmap = colormap.Colormap.from_string(s, color_scale=1)
+ np.testing.assert_array_equal(cmap.values, [0, 1, 2])
+ np.testing.assert_array_equal(cmap.colors, [[0, 0, 0], [1, 1, 1], [2, 2, 2]])
+
+
+def test_cmap_from_ndarray():
+ """Test creating a colormap from a numpy array."""
+ cmap_data = _generate_cmap_test_data(None, "RGB")
+ cmap = colormap.Colormap.from_ndarray(cmap_data, color_scale=1)
+ np.testing.assert_allclose(cmap.values, [0, 0.33333333, 0.66666667, 1])
+ np.testing.assert_array_equal(cmap.colors, cmap_data)
+
+
+def test_cmap_from_name():
+ """Test creating a colormap from a string representing a name."""
+ cmap = colormap.Colormap.from_name("puor")
+ np.testing.assert_array_equal(cmap.values, colormap.puor.values)
+ np.testing.assert_array_equal(cmap.colors, colormap.puor.colors)
+
+
+def test_cmap_from_sequence_of_colors():
+ """Test creating a colormap from a sequence of colors."""
+ colors = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
+ cmap = colormap.Colormap.from_sequence_of_colors(colors, color_scale=2)
+ np.testing.assert_allclose(cmap.values, [0, 0.33333333, 0.66666667, 1])
+ np.testing.assert_array_equal(cmap.colors*2, colors)
+
+ vals = [0, 5, 10, 15]
+ cmap = colormap.Colormap.from_sequence_of_colors(colors, values=vals, color_scale=2)
+ np.testing.assert_allclose(cmap.values, [0, 5, 10, 15])
+
+
+def test_build_colormap_with_int_data_and_without_meanings():
+ """Test colormap building."""
+ palette = np.array([[0, 0, 0], [127, 127, 127], [255, 255, 255]])
+ cmap = colormap.Colormap.from_array_with_metadata(palette, np.uint8)
+ np.testing.assert_array_equal(cmap.values, [0, 1])
+
+ with pytest.raises(AttributeError):
+ colormap.Colormap.from_array_with_metadata(palette/100, np.float32)
+
+ cmap = colormap.Colormap.from_array_with_metadata(
+ palette,
+ np.float32,
+ valid_range=[0, 100],
+ scale_factor=2,
+ remove_last=True)
+
+ np.testing.assert_array_equal(cmap.values, [0, 200])
+
+ cmap = colormap.Colormap.from_array_with_metadata(
+ palette,
+ np.float32,
+ valid_range=[0, 100],
+ scale_factor=2,
+ remove_last=False)
+
+ np.testing.assert_array_equal(cmap.values, [0, 100, 200])
+
+
+def test_build_colormap_with_int_data_and_with_meanings():
+ """Test colormap building."""
+ palette = xarray.DataArray(np.array([[0, 0, 0], [127, 127, 127], [255, 255, 255]]),
+ dims=['value', 'band'])
+ palette.attrs['palette_meanings'] = [2, 3, 4]
+ cmap = colormap.Colormap.from_array_with_metadata(palette, np.uint8)
+ np.testing.assert_array_equal(cmap.values, [2, 3, 4])
+
def _assert_monotonic_values(cmap, increasing=True):
delta = np.diff(cmap.values)
=====================================
trollimage/tests/test_image.py
=====================================
@@ -1385,7 +1385,7 @@ class TestXRImage:
img.save(tmp.name, tiled=True, overviews=[], driver='COG')
with rio.open(tmp.name) as f:
# The COG driver should add a tag indicating layout
- assert(f.tags(ns='IMAGE_STRUCTURE')['LAYOUT'] == 'COG')
+ assert (f.tags(ns='IMAGE_STRUCTURE')['LAYOUT'] == 'COG')
assert len(f.overviews(1)) == 2
@pytest.mark.skipif(sys.platform.startswith('win'), reason="'NamedTemporaryFile' not supported on Windows")
@@ -2004,6 +2004,22 @@ class TestXRImage:
pil_img.convert.assert_called_with('RGB')
+class TestImageColorize:
+ """Test the colorize method of the Image class."""
+
+ def test_colorize_la_rgb(self):
+ """Test colorizing an LA image with an RGB colormap."""
+ arr = np.arange(75).reshape(5, 15) / 74.
+ alpha = arr > 40.
+ img = image.Image(channels=[arr.copy(), alpha], mode="LA")
+ img.colorize(brbg)
+
+ expected = list(TestXRImageColorize._expected)
+ for i in range(3):
+ np.testing.assert_allclose(img.channels[i], expected[i])
+ np.testing.assert_allclose(img.channels[3], alpha)
+
+
class TestXRImageColorize:
"""Test the colorize method of the XRImage class."""
=====================================
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.19.0)"
- git_full = "93e547a45a1c3a13768da4a5169a3f5bf5ebc75a"
- git_date = "2022-10-21 09:50:00 -0500"
+ git_refnames = " (tag: v1.20.0)"
+ git_full = "d87d61cdc9f29998fb26af354fa7e6a897760103"
+ git_date = "2023-01-11 12:33:44 +0100"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/-/commit/d747012fb184c6086d3a7581bd1544ba96202490
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/trollimage/-/commit/d747012fb184c6086d3a7581bd1544ba96202490
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/20230116/225a0b9d/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list