Repository: earthlab/earthpy

Issue:
Issue with plotting nan values. 
When plotting `nan` values, occasionally the plots will be broken because of how we stretch values. Need a fix to make sure `earthpy.plot.plot_rgb()` can account for `nan` values and gracefully plot the data regardless. 

Discussion:

Patch:
diff --git a/dev-requirements.txt b/dev-requirements.txt
index 5d0fe81..bd5c149 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,13 +1,13 @@
-pre-commit==2.11.1
+pre-commit==2.13.0
 bumpversion==0.6.0
 codecov==2.1.11
-importlib-metadata==3.10.0
-pytest==6.2.3
+importlib-metadata==4.5.0
+pytest==6.2.4
 pytest-vcr==1.0.2
-sphinx==3.5.3
-pytest-cov==2.11.1
+sphinx==4.0.2
+pytest-cov==2.12.1
 sphinx-autobuild==2021.3.14
-sphinx_gallery==0.8.2
-sphinx_rtd_theme==0.5.1
-tox==3.23.0
+sphinx_gallery==0.9.0
+sphinx_rtd_theme==0.5.2
+tox==3.23.1
 pip>=19.0
diff --git a/earthpy/plot.py b/earthpy/plot.py
index 53cdfa8..44f82ca 100644
--- a/earthpy/plot.py
+++ b/earthpy/plot.py
@@ -331,7 +331,7 @@ def _stretch_im(arr, str_clip):
     s_max = 100 - str_clip
     arr_rescaled = np.zeros_like(arr)
     for ii, band in enumerate(arr):
-        lower, upper = np.percentile(band, (s_min, s_max))
+        lower, upper = np.nanpercentile(band, (s_min, s_max))
         arr_rescaled[ii] = exposure.rescale_intensity(
             band, in_range=(lower, upper)
         )
@@ -405,6 +405,11 @@ def plot_rgb(
     if stretch:
         rgb_bands = _stretch_im(rgb_bands, str_clip)
 
+    nan_check = np.isnan(rgb_bands)
+
+    if np.any(nan_check):
+        rgb_bands = np.ma.masked_array(rgb_bands, nan_check)
+
     # If type is masked array - add alpha channel for plotting
     if ma.is_masked(rgb_bands):
         # Build alpha channel
@@ -582,7 +587,11 @@ def hist(
             hist_range = (np.nanmin(arr_comp), np.nanmax(arr_comp))
         fig, ax = plt.subplots(figsize=figsize)
         ax.hist(
-            arr_comp, range=hist_range, bins=bins, color=colors[0], alpha=alpha
+            arr_comp,
+            range=hist_range,
+            bins=bins,
+            color=colors[0],
+            alpha=alpha,
         )
         if title:
             ax.set(title=title[0])
diff --git a/earthpy/spatial.py b/earthpy/spatial.py
index 436cc36..ed945ee 100644
--- a/earthpy/spatial.py
+++ b/earthpy/spatial.py
@@ -627,6 +627,34 @@ def hillshade(arr, azimuth=30, altitude=30):
     return 255 * (shaded + 1) / 2
 
 
+def crs_check(path):
+    """Get the CRS of a raster file from a file path.
+
+    Parameters
+    ----------
+    path : string
+        Path to the raster file.
+
+    Returns
+    -------
+    crs : crs object
+        The CRS object stored in the raster file.
+    """
+
+    try:
+        with rio.open(path) as src:
+            crs = src.crs
+            # This section runs when the data is in a hierarchial format
+            if crs is None:
+                for data in src.subdatasets:
+                    with rio.open(data) as data_src:
+                        crs = data_src.crs
+        return crs
+    except rio.errors.RasterioIOError as e:
+        print("Please only input files that can be read as a raster.\n")
+        raise rio.errors.RasterioIOError(e.__str__())
+
+
 # @deprecate
 def stack_raster_tifs(band_paths, out_path, arr_out=True):
     """This function has been deprecated from earthpy.


Test patch:
diff --git a/earthpy/tests/test_epsg.py b/earthpy/tests/test_epsg.py
index 893c70c..046ed2c 100644
--- a/earthpy/tests/test_epsg.py
+++ b/earthpy/tests/test_epsg.py
@@ -1,7 +1,22 @@
+import pytest
+import rasterio as rio
 import earthpy as et
+import earthpy.spatial as es
+from earthpy.io import path_to_example
 
 
 def test_epsg():
     """Unit test for loading EPSG to Proj4 string dictionary."""
 
     assert et.epsg["4326"] == "+proj=longlat +datum=WGS84 +no_defs"
+
+
+def test_crs_check_tif():
+    """Test crs check works properly."""
+    crs = es.crs_check(path_to_example("rmnp-rgb.tif"))
+    assert(crs.to_epsg() == 4326)
+
+
+def test_crs_check_bad_file():
+    with pytest.raises(rio.errors.RasterioIOError):
+        es.crs_check(path_to_example("rmnp.shp"))
diff --git a/earthpy/tests/test_plot_rgb.py b/earthpy/tests/test_plot_rgb.py
index ff42bbb..7f615c7 100644
--- a/earthpy/tests/test_plot_rgb.py
+++ b/earthpy/tests/test_plot_rgb.py
@@ -132,6 +132,22 @@ def test_stretch_image(rgb_image):
     plt.close()
 
 
+def test_stretch_image_nan(rgb_image):
+    """Test that running stretch actually stretches the data
+    to a max value of 255 and min value of 0 when nan values
+    are present within the plot_rgb fun."""
+
+    im, _ = rgb_image
+    np.place(im, im > 150, [0])
+    im = np.where(im < 25, np.nan, im)
+
+    ax = plot_rgb(im, stretch=True)
+    max_val = ax.get_images()[0].get_array().max()
+    min_val = ax.get_images()[0].get_array().min()
+    assert max_val == 255 and min_val == 0
+    plt.close()
+
+
 def test_masked_im(rgb_image):
     """Test that a masked image will be plotted using an alpha channel.
     Thus it should return an array that has a 4th dimension representing