Repository: rasterio/rasterio

Issue:
Can't set nodata value to `inf` / `-inf` 
## Expected behavior and actual behavior.

`inf`, `-inf` are valid nodata values in GDAL. 


## Steps to reproduce the problem.

```python
import rasterio
from rasterio.io import MemoryFile

with MemoryFile() as mem:
    with mem.open(driver="GTiff", width=1, height=1, count=1, dtype='float32', nodata=float('inf')) as src:
         print(src.nodata)

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-21-e1f368f0f629> in <module>
      1 with MemoryFile() as mem:
----> 2     with mem.open(driver="GTiff", width=1, height=1, count=1, dtype='float32', nodata=float('inf')) as src:
      3         print(src.nodata)
      4 
      5 

~/Dev/venv/python38/lib/python3.8/site-packages/rasterio/env.py in wrapper(*args, **kwds)
    382     def wrapper(*args, **kwds):
    383         if local._env:
--> 384             return f(*args, **kwds)
    385         else:
    386             with Env.from_defaults():

~/Dev/venv/python38/lib/python3.8/site-packages/rasterio/io.py in open(self, driver, width, height, count, crs, transform, dtype, nodata, sharing, **kwargs)
    132         else:
    133             writer = get_writer_for_driver(driver)
--> 134             return writer(mempath, 'w+', driver=driver, width=width,
    135                           height=height, count=count, crs=crs,
    136                           transform=transform, dtype=dtype,

rasterio/_io.pyx in rasterio._io.DatasetWriterBase.__init__()

ValueError: Given nodata value, inf, is beyond the valid range of its data type, float32.
```

I need to dig deeper but to me it seems the error comes from https://github.com/mapbox/rasterio/blob/db03b66e81b489d3f5f01c9edfb6fc720250a2c1/rasterio/_io.pyx#L309-L311

Just noting when editing nodata value from an opened file I don't get error but the nodata is then set to None 

```python
import rasterio
with rasterio.open("test.tif", "w", driver="GTiff", width=1, height=1, count=1, dtype='float32') as src:
    print(src.nodata)
>> None

with rasterio.open("test.tif", "r+") as src:
    src._set_nodatavals((float("-inf"),))
    print(src.nodata)
>> -inf

with rasterio.open("test.tif", "r") as src:
    print(src.nodata)
>> None
```

## Operating system

Mac OS X M1 

## Rasterio version and provenance

rasterio 1.2.6 built from sources with GDAL 3.2.2



Discussion:

Resolving patch:
diff --git a/rasterio/dtypes.py b/rasterio/dtypes.py
--- a/rasterio/dtypes.py
+++ b/rasterio/dtypes.py
@@ -5,6 +5,7 @@
 not break.
 
 """
+import numpy
 
 bool_ = 'bool'
 ubyte = uint8 = 'uint8'
@@ -70,6 +71,16 @@
     'float64': (-1.7976931348623157e+308, 1.7976931348623157e+308)}
 
 
+def in_dtype_range(value, dtype):
+    """
+    Check if the value is within the dtype range
+    """
+    if numpy.dtype(dtype).kind == "f" and (numpy.isinf(value) or numpy.isnan(value)):
+        return True
+    range_min, range_max = dtype_ranges[dtype]
+    return range_min <= value <= range_max
+
+
 def _gdal_typename(dt):
     try:
         return typename_fwd[dtype_rev[dt]]
diff --git a/rasterio/rio/edit_info.py b/rasterio/rio/edit_info.py
--- a/rasterio/rio/edit_info.py
+++ b/rasterio/rio/edit_info.py
@@ -168,7 +168,7 @@ def edit(ctx, input, bidx, nodata, unset_nodata, crs, unset_crs, transform,
 
     def in_dtype_range(value, dtype):
         kind = np.dtype(dtype).kind
-        if kind == 'f' and np.isnan(value):
+        if kind == 'f' and (np.isnan(value) or np.isinf(value)):
             return True
         infos = {'c': np.finfo, 'f': np.finfo, 'i': np.iinfo,
                  'u': np.iinfo}


Test patch:
diff --git a/tests/test_nodata.py b/tests/test_nodata.py
--- a/tests/test_nodata.py
+++ b/tests/test_nodata.py
@@ -4,7 +4,10 @@
 import subprocess
 import sys
 
+import numpy
+
 import rasterio
+from rasterio.io import MemoryFile
 
 logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
 
@@ -43,3 +46,41 @@ def test_set_nodata(tmpdir):
     assert re.search(pattern, info, re.DOTALL) is not None
     pattern = b'Band 2.*?NoData Value=42'
     assert re.search(pattern, info, re.DOTALL) is not None
+
+
+def test_set_mem_inf_nodata():
+    with MemoryFile() as mem, mem.open(
+        driver="GTiff", width=1, height=1, count=1, dtype='float32', nodata=float('inf')
+    ) as src:
+          assert numpy.isinf(src.nodata)
+
+
+def test_set_inf_nodata(tmpdir):
+    dst_path = str(tmpdir.join('lol.tif'))
+    with rasterio.open('tests/data/RGB.byte.tif') as src:
+        meta = src.meta
+        meta['dtype'] ='float32'
+        meta['nodata'] = float('inf')
+        with rasterio.open(dst_path, 'w', **meta) as dst:
+            assert numpy.isinf(dst.nodata)
+            assert numpy.isinf(dst.meta['nodata'])
+            assert numpy.isinf(dst.nodatavals).all()
+
+
+def test_set_mem_nan_nodata():
+    with MemoryFile() as mem, mem.open(
+        driver="GTiff", width=1, height=1, count=1, dtype='float32', nodata=float('nan')
+    ) as src:
+          assert numpy.isnan(src.nodata)
+
+
+def test_set_nan_nodata(tmpdir):
+    dst_path = str(tmpdir.join('lol.tif'))
+    with rasterio.open('tests/data/RGB.byte.tif') as src:
+        meta = src.meta
+        meta['dtype'] ='float32'
+        meta['nodata'] = float('nan')
+        with rasterio.open(dst_path, 'w', **meta) as dst:
+            assert numpy.isnan(dst.nodata)
+            assert numpy.isnan(dst.meta['nodata'])
+            assert numpy.isnan(dst.nodatavals).all()
diff --git a/tests/test_rio_edit_info.py b/tests/test_rio_edit_info.py
--- a/tests/test_rio_edit_info.py
+++ b/tests/test_rio_edit_info.py
@@ -4,6 +4,7 @@
 import json
 
 import click
+import numpy
 import pytest
 
 import rasterio
@@ -77,6 +78,14 @@ def test_edit_nodata(data, runner):
     with rasterio.open(inputfile) as src:
         assert src.nodata == 255.0
 
+def test_edit_nodata__inf(data, runner):
+    inputfile = str(data.join('float.tif'))
+    result = runner.invoke(
+        main_group, ['edit-info', inputfile, '--nodata', 'inf'])
+    assert result.exit_code == 0
+    with rasterio.open(inputfile) as src:
+        assert numpy.isinf(src.nodata)
+
 
 def test_edit_crs_err(data, runner):
     inputfile = str(data.join('RGB.byte.tif'))