Repository: rasterio/rasterio

Issue:
get_data_window fails with `nan` nodata value
<!--

WELCOME ABOARD!

Hi and welcome to the Rasterio project. We appreciate bug reports, questions
about documentation, and suggestions for new features. This issue template
isn't intended to ward you off; only to intercept and redirect some particular
categories of reports, and to collect a few important facts that issue reporters
often omit.

The primary forum for questions about installation and usage of Rasterio is 
https://rasterio.groups.io/g/main. The authors and other users will answer 
questions when they have expertise to share and time to explain. Please take the
time to craft a clear question and be patient about responses. Please do not
bring these questions to Rasterio's issue tracker, which we want to reserve for
bug reports and other actionable issues.

Questions about development of Rasterio, brainstorming, requests for comment,
and not-yet-actionable proposals are welcome in the project's developers 
discussion group https://rasterio.groups.io/g/dev. Issues opened in Rasterio's
GitHub repo which haven't been socialized there may be perfunctorily closed.

Please note: Rasterio contains extension modules and is thus susceptible to
C library compatibility issues. If you are reporting an installation or module
import issue, please note that this project only accepts reports about problems
with packages downloaded from the Python Package Index. Conda users should take
issues to one of the following trackers:

- https://github.com/ContinuumIO/anaconda-issues/issues
- https://github.com/conda-forge/rasterio-feedstock

You think you've found a bug? We believe you!
-->

## Expected behavior and actual behavior.

The `get_data_window` works great for most nodata values, but not for `nan`s.

## Steps to reproduce the problem.

```python
from rasterio.windows import get_data_window
import numpy as np

arr = np.ones((3, 3))
arr[0, :] = np.nan
window = get_data_window(arr, nodata=0)
print(window)
# Window(col_off=0, row_off=0, width=3, height=3)
```

#### Environment Information
<!-- If you have rasterio>=1.3.0 -->
 - Output from: `rio --show-versions` or `python -c "import rasterio; rasterio.show_versions()"`
```
(mapping) staniewi:rasterio$ python -c "import rasterio; rasterio.show_versions()"
rasterio info:
  rasterio: 1.4dev
      GDAL: 3.5.0
      PROJ: 9.0.1
      GEOS: 3.11.0
 PROJ DATA: /Users/staniewi/miniconda3/envs/mapping/share/proj
 GDAL DATA: /Users/staniewi/miniconda3/envs/mapping/share/gdal

System:
    python: 3.10.5 | packaged by conda-forge | (main, Jun 14 2022, 07:07:06) [Clang 13.0.1 ]
executable: /Users/staniewi/miniconda3/envs/mapping/bin/python
   machine: macOS-12.6.1-arm64-arm-64bit

Python deps:
    affine: 2.3.1
     attrs: 21.4.0
   certifi: 2022.09.24
     click: 8.1.3
     cligj: 0.7.2
    cython: 0.29.30
     numpy: 1.22.4
    snuggs: 1.4.7
click-plugins: None
setuptools: 62.6.0
```

## Installation Method

Happens with either current conda install, or from source.



Discussion:

Resolving patch:
diff --git a/rasterio/__init__.py b/rasterio/__init__.py
--- a/rasterio/__init__.py
+++ b/rasterio/__init__.py
@@ -20,7 +20,7 @@
     else:
         if "PATH" in os.environ:
             for p in os.environ["PATH"].split(os.pathsep):
-                if glob.glob(os.path.join(p, "gdal*.dll")):
+                if p and glob.glob(os.path.join(p, "gdal*.dll")):
                     os.add_dll_directory(p)
 
 
diff --git a/rasterio/windows.py b/rasterio/windows.py
--- a/rasterio/windows.py
+++ b/rasterio/windows.py
@@ -160,7 +160,10 @@ def get_data_window(arr, nodata=None):
     # Otherwise retrieve mask from array (if it is masked)
     # Finally try returning a full window (nodata=None and nothing in arr is masked)
     if nodata is not None:
-        arr_mask = arr != nodata
+        if np.isnan(nodata):
+            arr_mask = ~np.isnan(arr)
+        else:
+            arr_mask = arr != nodata
     elif np.ma.is_masked(arr):
         arr_mask = ~np.ma.getmask(arr)
     else:
@@ -180,10 +183,10 @@ def get_data_window(arr, nodata=None):
             v.append((nz.min(), nz.max() + 1))
         else:
             v.append((0, 0))
-    
+
     if arr_mask.ndim == 1:
         v.append((0, 0))
-        
+
     return Window.from_slices(*v)
 
 


Test patch:
diff --git a/tests/test_warp.py b/tests/test_warp.py
--- a/tests/test_warp.py
+++ b/tests/test_warp.py
@@ -1966,12 +1966,11 @@ def http_error_server(data):
     import multiprocessing
     import http.server
 
-    PORT = 8000
     Handler = functools.partial(RangeRequestErrorHandler, directory=str(data))
-    httpd = http.server.HTTPServer(("", PORT), Handler)
+    httpd = http.server.HTTPServer(("", 0), Handler)
     p = multiprocessing.Process(target=httpd.serve_forever)
     p.start()
-    yield
+    yield f'{httpd.server_name}:{httpd.server_port}'
     p.terminate()
     p.join()
 
@@ -1984,7 +1983,7 @@ def test_reproject_error_propagation(http_error_server, caplog):
     """Propagate errors up from ChunkAndWarpMulti and check for a retry."""
 
     with rasterio.open(
-        "/vsicurl?max_retry=1&retry_delay=.1&url=http://localhost:8000/RGB.byte.tif"
+        f"/vsicurl?max_retry=1&retry_delay=.1&url=http://{http_error_server}/RGB.byte.tif"
     ) as src:
         out = np.zeros((src.count, src.height, src.width), dtype="uint8")
 
diff --git a/tests/test_windows.py b/tests/test_windows.py
--- a/tests/test_windows.py
+++ b/tests/test_windows.py
@@ -508,6 +508,14 @@ def test_data_window_nodata():
     assert window == Window.from_slices((1, 3), (0, 3))
 
 
+def test_data_window_nodata_nan():
+    """Get window of arr with nodata."""
+    arr = np.ones((3, 3))
+    arr[0, :] = np.nan
+    window = get_data_window(arr, nodata=np.nan)
+    assert window == Window.from_slices((1, 3), (0, 3))
+
+
 def test_data_window_novalid():
     """Get window of arr with nodata."""
     arr = np.ones((3, 3))