Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add checks and resize #636

Merged
merged 3 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@

# 6.1.0 (TBD)

* add `width`, `height` and `count` properties in `MosaicMethodBase`
* make sure we mosaic ImageData/PointData with same number of bands
* resize `ImageData.array` to the first asset's width/height in `mosaic_reader`

# 6.0.3 (2023-09-13)

* return a 1x1 image when bbox is smaller than a single pixel (author @JackDunnNZ, https://github.com/cogeotiff/rio-tiler/pull/637)
Expand Down
3 changes: 3 additions & 0 deletions rio_tiler/mosaic/methods/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ class MosaicMethodBase(abc.ABC):
mosaic: Optional[numpy.ma.MaskedArray] = field(default=None, init=False)
exit_when_filled: bool = field(default=False, init=False)
cutline_mask: Optional[numpy.ndarray] = field(default=None, init=False)
width: int = field(init=False)
height: int = field(init=False)
count: int = field(init=False)

@property
def is_done(self) -> bool:
Expand Down
59 changes: 49 additions & 10 deletions rio_tiler/mosaic/reader.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""rio_tiler.mosaic: create tile from multiple assets."""

import warnings
from inspect import isclass
from typing import Any, Callable, List, Optional, Sequence, Tuple, Type, Union, cast

import numpy
from rasterio.crs import CRS

from rio_tiler.constants import MAX_THREADS
Expand All @@ -17,10 +19,10 @@
from rio_tiler.mosaic.methods.defaults import FirstMethod
from rio_tiler.tasks import create_tasks, filter_tasks
from rio_tiler.types import BBox
from rio_tiler.utils import _chunks
from rio_tiler.utils import _chunks, resize_array


def mosaic_reader(
def mosaic_reader( # noqa: C901
mosaic_assets: Sequence,
reader: Callable[..., ImageData],
*args: Any,
Expand Down Expand Up @@ -88,14 +90,42 @@ def mosaic_reader(
tasks,
allowed_exceptions=allowed_exceptions,
):
crs = img.crs
bounds = img.bounds
band_names = img.band_names
# On the first Image we set the properties
if len(assets_used) == 0:
crs = img.crs
bounds = img.bounds
band_names = img.band_names
pixel_selection.cutline_mask = img.cutline_mask
pixel_selection.width = img.width
pixel_selection.height = img.height
pixel_selection.count = img.count

assert (
img.count == pixel_selection.count
), "Assets HAVE TO have the same number of bands"
if any(
[
img.width != pixel_selection.width,
img.height != pixel_selection.height,
]
):
warnings.warn(
"Cannot concatenate images with different size. Will resize using fist asset width/heigh",
UserWarning,
)
h = pixel_selection.height
w = pixel_selection.width
pixel_selection.feed(
numpy.ma.MaskedArray(
resize_array(img.array.data, h, w),
mask=resize_array(img.array.mask * 1, h, w).astype("bool"),
)
)

pixel_selection.cutline_mask = img.cutline_mask
else:
pixel_selection.feed(img.array)

assets_used.append(asset)
pixel_selection.feed(img.array)

if pixel_selection.is_done and pixel_selection.data is not None:
return (
Expand Down Expand Up @@ -184,9 +214,18 @@ def mosaic_point_reader(
tasks,
allowed_exceptions=allowed_exceptions,
):
crs = pt.crs
coordinates = pt.coordinates
band_names = pt.band_names
# On the first Image we set the properties
if len(assets_used) == 0:
crs = pt.crs
coordinates = pt.coordinates
band_names = pt.band_names
pixel_selection.width = 1
pixel_selection.height = 1
pixel_selection.count = pt.count

assert (
pt.count == pixel_selection.count
), "Assets HAVE TO have the same number of bands"

assets_used.append(asset)
pixel_selection.feed(pt.array)
Expand Down
Binary file added tests/fixtures/mosaic_cog3.tif
Binary file not shown.
26 changes: 26 additions & 0 deletions tests/test_mosaic.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@

asset1 = os.path.join(os.path.dirname(__file__), "fixtures", "mosaic_cog1.tif")
asset2 = os.path.join(os.path.dirname(__file__), "fixtures", "mosaic_cog2.tif")
asset3 = os.path.join(os.path.dirname(__file__), "fixtures", "mosaic_cog3.tif")
assets = [asset1, asset2]
assets_order = [asset2, asset1]
assets_mixed = [asset1, asset3]

stac_asset = os.path.join(os.path.dirname(__file__), "fixtures", "stac.json")

Expand Down Expand Up @@ -707,3 +709,27 @@ def test_mosaic_feature():
assert list(numpy.unique(dat.array.mask)) == [False, True]
assert dat.assets == [asset1, asset2]
assert dat.crs == crs


def test_mosaic_feature_size_diff():
"""Test mosaic feature with ."""
feature = {
"type": "Feature",
"properties": {},
"geometry": {
"coordinates": [
[
[-74.08023862322486, 45.69787317293694],
[-74.08023862322486, 45.2992447541925],
[-73.43873302640402, 45.2992447541925],
[-73.43873302640402, 45.69787317293694],
[-74.08023862322486, 45.69787317293694],
]
],
"type": "Polygon",
},
}

with pytest.warns(UserWarning):
dat, _ = mosaic.mosaic_reader(assets_mixed, _read_feature, shape=feature)
assert dat.data.shape == (3, 120, 193)