cogeotiff / rio-tiler

User friendly Rasterio plugin to read raster datasets.
https://cogeotiff.github.io/rio-tiler/
BSD 3-Clause "New" or "Revised" License
511 stars 106 forks source link

.tile() is not masking region outside of image when nodata used #675

Closed banesullivan closed 8 months ago

banesullivan commented 8 months ago

I'm not sure if this is a usage issue on my part or a bug, but I'm noticing that the .tile() method sometimes doesn't mask the output image when the tile extends beyond the extent of the source dataset.

All looks good for tile x=82, y=198, z=9 when not specifying nodata:

from rio_tiler.io import Reader

class PNG(bytes):
    def _repr_png_(self):
        return self

reader = Reader('https://github.com/giswqs/data/raw/main/raster/landsat7.tif')
img = reader.tile(82, 198, 9, nodata=0)
PNG(img.render(img_format="png"))

303823332-8cef848e-0636-40fe-9379-5d5649a98531

But then when I specify nodata it seems the returned image is no longer masked outside the bounds:

img = reader.tile(82, 198, 9, nodata=-1)
PNG(img.render(img_format="png"))

303823335-c0f1fb82-cd6e-4614-8687-054ac1b1d095


Originally posted in https://github.com/banesullivan/localtileserver/issues/197

vincentsarago commented 8 months ago

@banesullivan interesting this is related to #669 where we removed WarpedVRT for nodata handling.

We know use simple rasterio.read method

https://github.com/cogeotiff/rio-tiler/blob/874f1410e04c3e46bd50f6a272340787ef39fb55/rio_tiler/reader.py#L229-L241

There might be a bug in rasterio when using boundless=True and fill_value=nodata

https://github.com/rasterio/rasterio/blob/main/rasterio/_io.pyx#L680-L735

vincentsarago commented 8 months ago
import rasterio

with rasterio.open("tests/fixtures/cog.tif") as src_dst:
    arr = src_dst.read(
        masked=True, 
        fill_value=-1, 
        window=((-100,200), (-100,200)), 
        boundless=True,
)

print(arr.data)

>> [[[0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  [0 0 0 ... 0 0 0]
  ...
  [0 0 0 ... 1 1 1]
  [0 0 0 ... 1 1 1]
  [0 0 0 ... 1 1 1]]]

I would expect the outside to be -1 instead of 0

Interestingly the mask from rasterio is good

print(arr.mask)

>> [[[ True  True  True ...  True  True  True]
  [ True  True  True ...  True  True  True]
  [ True  True  True ...  True  True  True]
  ...
  [ True  True  True ... False False False]
  [ True  True  True ... False False False]
  [ True  True  True ... False False False]]]

but we override it in https://github.com/cogeotiff/rio-tiler/blob/874f1410e04c3e46bd50f6a272340787ef39fb55/rio_tiler/reader.py#L241 😓

vincentsarago commented 8 months ago

😅 well the issue is that you're trying to use -1 as nodata value for a dataset which is of type uint8.

from rio_tiler.io import Reader
from matplotlib.pyplot import imshow
with Reader('https://github.com/giswqs/data/raw/main/raster/landsat7.tif') as reader:
    img = reader.tile(82, 198, 9, nodata=100)

imshow(img.mask)
Screenshot 2024-02-13 at 5 29 08 PM