earthpy icon indicating copy to clipboard operation
earthpy copied to clipboard

plot_rgb fails with stretch=True for masked array with masked NaNs

Open adamancer opened this issue 3 years ago • 1 comments

Describe the bug Using ep.plot_rgb() with stretch=True fails when plotting a masked array when the original array contains NaNs. Plotting returns a black plot and gives the following warning "UserWarning: One or more intensity levels are NaN. Rescaling will broadcast NaN to the full image. Provide intensity levels yourself to avoid this. E.g. with np.nanmin(image), np.nanmax(image)."

I think this bug results from the use of np.percentile in the function earthpy uses to stretch the image. Neither np.percentile nor np.nanpercentile work intuitively with masked arrays. For a masked array where NaNs are masked, they return NaN(s). For a masked array where numeric values are masked, they return values based on the original (not masked) array, resulting in washed out images where nodata values are low (see screenshot below). This behavior has been reported as an issue but not fixed at https://github.com/numpy/numpy/issues/4767 (although that link does provide potential workarounds).

To Reproduce

import earthpy.plot as ep
import numpy as np

# Create a 9x9 array with some pretty colors and some NaNs
r = [[128,  64, np.nan], [128,   0,   0], [  0, 128, 128]]
g = [[128,  64, np.nan], [  0, 128,   0], [128,   0, 128]]
b = [[128,  64, np.nan], [  0,   0, 128], [128, 128,   0]]

arr = np.array([r, g, b])

# Works as expected if NaNs are zeroed
arr_nan_zeroed = np.nan_to_num(arr)
ep.plot_rgb(arr_nan_zeroed, rgb=(2, 1, 0), stretch=True, title="NaN zeroed")

# Generates a warning and returns a black square if NaNs are masked
arr_nan_masked = np.ma.masked_invalid(arr)
ep.plot_rgb(arr_nan_masked, rgb=(2, 1, 0), stretch=True, title="NaN masked")

Expected behavior The plot of the masked array should look similar/identical to the plot of the zeroed array

Screenshots This is from the plot_rgb vignette when stretch is applied using the same approach as above:

plot_rgb_vignette

What Operating System Are you Running?

  • Windows

Additional context The rioxarray library seems to convert nodata values to NaN automatically when loading rasters, so the masked arrays needed by plot_rgb are not stretchable for anything loaded using that library.

adamancer avatar Mar 18 '21 05:03 adamancer

hi @adamancer !! thank you for this issue!! @nkorinek and I were just talking last week about how we could make plot_rgb support xarray objects. While this issue is separate from that i think we could definitely tackle this as well when we start working on it. thank you for an excellent first issue to earthpy!!!

lwasser avatar Mar 22 '21 16:03 lwasser