Skip to content

Commit

Permalink
merge from main
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentsarago committed Dec 19, 2024
2 parents e78e30c + 83e8fc4 commit 257eeaa
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 28 deletions.
8 changes: 8 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@
>> ("b1", "array")
```

# 7.2.2 (2024-11-18)

* Catch and expand error message when GDAL cannot encode data using specified image driver (https://github.com/cogeotiff/rio-tiler/pull/767)

# 7.2.1 (2024-11-14)

* add official support for floating point values in ColorMap
* cast data to `uint8` datatype when applying linear colormap

# 7.2.0 (2024-11-05)

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ ignore = [
max-complexity = 14

[tool.bumpversion]
current_version = "7.2.0"
current_version = "7.2.2"
search = "{current_version}"
replace = "{new_version}"
regex = false
Expand Down
2 changes: 1 addition & 1 deletion rio_tiler/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""rio-tiler."""

__version__ = "7.2.0"
__version__ = "7.2.2"

from . import ( # noqa
colormap,
Expand Down
30 changes: 21 additions & 9 deletions rio_tiler/colormap.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import pathlib
import re
import warnings
from typing import Dict, List, Sequence, Tuple, Union

import attr
Expand All @@ -18,6 +19,7 @@
from rio_tiler.types import (
ColorMapType,
DataMaskType,
DiscreteColorMapType,
GDALColorMapType,
IntervalColorMapType,
)
Expand Down Expand Up @@ -116,28 +118,38 @@ def apply_cmap(data: numpy.ndarray, colormap: ColorMapType) -> DataMaskType:
# rio_tiler.colormap.make_lut, because we don't want to create a `lookup table`
# with more than 256 entries (256 x 4) array. In this case we use `apply_discrete_cmap`
# which can work with arbitrary colormap dict.
if len(colormap) != 256 or max(colormap) >= 256 or min(colormap) < 0:
if (
len(colormap) != 256
or max(colormap) >= 256
or min(colormap) < 0
or any(isinstance(k, float) for k in colormap)
):
return apply_discrete_cmap(data, colormap)

lookup_table = make_lut(colormap)
# For now we assume ColorMap are in uint8
if data.dtype != numpy.uint8:
warnings.warn(
f"Input array is of type {data.dtype} and `will be converted to Int in order to apply the ColorMap.",
UserWarning,
)
data = data.astype(numpy.uint8)

lookup_table = make_lut(colormap) # type: ignore
data = lookup_table[data[0], :]

data = numpy.transpose(data, [2, 0, 1])

# If the colormap has values between 0-255
# we cast the output array to Uint8.
if data.min() >= 0 and data.max() <= 255:
data = data.astype("uint8")

return data[:-1], data[-1]


def apply_discrete_cmap(data: numpy.ndarray, colormap: GDALColorMapType) -> DataMaskType:
def apply_discrete_cmap(
data: numpy.ndarray, colormap: Union[GDALColorMapType, DiscreteColorMapType]
) -> DataMaskType:
"""Apply discrete colormap.
Args:
data (numpy.ndarray): 1D image array to translate to RGB.
colormap (GDALColorMapType): Discrete ColorMap dictionary.
colormap (GDALColorMapType or DiscreteColorMapType): Discrete ColorMap dictionary.
Returns:
tuple: Data (numpy.ndarray) and Alpha band (numpy.ndarray).
Expand Down
4 changes: 4 additions & 0 deletions rio_tiler/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@
# ColorMap Dict: {1: (0, 0, 0, 255), ...}
GDALColorMapType = Dict[int, ColorTuple]

# Discrete Colormap, like GDALColorMapType but accept Float: {0.1: (0, 0, 0, 255), ...}
DiscreteColorMapType = Dict[NumType, ColorTuple]

# Intervals ColorMap: [((0, 1), (0, 0, 0, 0)), ...]
IntervalColorMapType = Sequence[Tuple[IntervalTuple, ColorTuple]]

ColorMapType = Union[
GDALColorMapType,
DiscreteColorMapType,
IntervalColorMapType,
]

Expand Down
38 changes: 22 additions & 16 deletions rio_tiler/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from rio_tiler.colormap import apply_cmap
from rio_tiler.constants import WEB_MERCATOR_CRS, WGS84_CRS
from rio_tiler.errors import RioTilerError
from rio_tiler.errors import InvalidFormat, RioTilerError
from rio_tiler.types import BBox, ColorMapType, IntervalTuple, RIOResampling


Expand Down Expand Up @@ -588,24 +588,30 @@ def render(
}
output_profile.update(creation_options)

with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=NotGeoreferencedWarning,
module="rasterio",
)
with MemoryFile() as memfile:
with memfile.open(**output_profile) as dst:
dst.write(data, indexes=list(range(1, count + 1)))
try:
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
category=NotGeoreferencedWarning,
module="rasterio",
)
with MemoryFile() as memfile:
with memfile.open(**output_profile) as dst:
dst.write(data, indexes=list(range(1, count + 1)))

# Use Mask as an alpha band
if mask is not None:
if ColorInterp.alpha not in dst.colorinterp:
dst.colorinterp = *dst.colorinterp[:-1], ColorInterp.alpha

# Use Mask as an alpha band
if mask is not None:
if ColorInterp.alpha not in dst.colorinterp:
dst.colorinterp = *dst.colorinterp[:-1], ColorInterp.alpha
dst.write(mask.astype(data.dtype), indexes=count + 1)

dst.write(mask.astype(data.dtype), indexes=count + 1)
return memfile.read()

return memfile.read()
except Exception as e:
raise InvalidFormat(
f"Could not encode array of shape ({count},{height},{width}) and of datatype `{data.dtype}` using {img_format} driver"
) from e


def mapzen_elevation_rgb(data: numpy.ndarray) -> numpy.ndarray:
Expand Down
Binary file added tests/fixtures/cog_int16.tif
Binary file not shown.
19 changes: 19 additions & 0 deletions tests/test_cmap.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,8 @@ def test_parse_color_bad():

def test_discrete_float():
"""test for titiler issue 738."""

# make sure we apply discrete colormap when we have less than 256 cmap entries
cm = {
0: (0, 255, 255, 255),
1: (83, 151, 145, 255),
Expand All @@ -325,3 +327,20 @@ def test_discrete_float():
dd, mm = colormap.apply_discrete_cmap(data.copy(), cm)
assert d.dtype == numpy.uint8
assert m.dtype == numpy.uint8
numpy.testing.assert_array_equal(d, dd)
numpy.testing.assert_array_equal(m, mm)

# make we allow float keys in discrete colormap
cm = {
0.5: (0, 255, 255, 255),
1.5: (83, 151, 145, 255),
2.5: (87, 194, 23, 255),
}

data = numpy.random.choice([0.5, 2.5], 256 * 256).reshape(1, 256, 256)
d, m = colormap.apply_cmap(data.copy(), cm)
dd, mm = colormap.apply_discrete_cmap(data.copy(), cm)
assert d.dtype == numpy.uint8
assert m.dtype == numpy.uint8
numpy.testing.assert_array_equal(d, dd)
numpy.testing.assert_array_equal(m, mm)
25 changes: 25 additions & 0 deletions tests/test_io_rasterio.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from rasterio.vrt import WarpedVRT
from rasterio.warp import transform_bounds

from rio_tiler.colormap import cmap
from rio_tiler.constants import WEB_MERCATOR_TMS, WGS84_CRS
from rio_tiler.errors import (
ExpressionMixingWarning,
Expand Down Expand Up @@ -1120,3 +1121,27 @@ def test_inverted_latitude():
with pytest.warns(UserWarning):
with Reader(COG_INVERTED) as src:
_ = src.tile(0, 0, 0)


def test_int16_colormap():
"""Should raise a warning about invalid data type for applying colormap.
ref: https://github.com/developmentseed/titiler/issues/1023
"""
data = os.path.join(PREFIX, "cog_int16.tif")
color_map = cmap.get("viridis")

with Reader(data) as src:
info = src.info()
assert info.dtype == "int16"
assert info.nodata_type == "Nodata"
assert info.nodata_value == -32768

img = src.preview()
assert img.mask.any()

with pytest.warns(UserWarning):
im = img.apply_colormap(color_map)

# make sure we keep the nodata part masked
assert not im.mask.all()
12 changes: 11 additions & 1 deletion tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
from rasterio.errors import NotGeoreferencedWarning
from rasterio.io import MemoryFile

from rio_tiler.errors import InvalidDatatypeWarning, InvalidPointDataError
from rio_tiler.errors import (
InvalidDatatypeWarning,
InvalidFormat,
InvalidPointDataError,
)
from rio_tiler.models import ImageData, PointData


Expand Down Expand Up @@ -456,3 +460,9 @@ def test_imagedata_coverage():

coverage = im.get_coverage_array(poly, cover_scale=1000)
assert numpy.round(numpy.unique(coverage), decimals=3).tolist() == [0, 0.125, 0.25]


def test_image_encoding_error():
"""Test ImageData error when using bad data array shape."""
with pytest.raises(InvalidFormat):
ImageData(numpy.zeros((5, 256, 256), dtype="uint8")).render(img_format="PNG")

0 comments on commit 257eeaa

Please sign in to comment.