Repository: rasterio/rasterio

Issue:
Data get promoted to float64 when using WarpedVRT
This was first raised over https://github.com/cogeotiff/rio-cogeo/issues/85
After some digging, I couldn't find the bug myself. I believe the problem comes from our WarpedVRT implementation but when reading the code I cannot find what's going on.

The behaviour only happens with the file shared by the user `https://esip-pangeo-uswest2.s3-us-west-2.amazonaws.com/sciencebase/tiny.tif` and I couldn't reproduce it with other files.

## Expected behavior and actual behavior.

keep original datatype

## Steps to reproduce the problem.

File: https://esip-pangeo-uswest2.s3-us-west-2.amazonaws.com/sciencebase/tiny.tif

```python
import rasterio
from rasterio.vrt import WarpedVRT

src_path = "tiny.tif"

with rasterio.open(src_path) as src_dst:
    print(src_dst.meta)
    with WarpedVRT(src_dst) as vrt:
        print(vrt.meta)

> {'driver': 'GTiff', 'dtype': 'float32', 'nodata': -3.4028230607370965e+38, 'width': 512, 'height': 512, 'count': 1, 'crs': CRS.from_epsg(26911), 'transform': Affine(1.0, 0.0, 179523.99999999822,
       0.0, -1.0, 3824832.0)}
> {'driver': 'VRT', 'dtype': 'float64', 'nodata': -3.4028230607370965e+38, 'width': 512, 'height': 512, 'count': 1, 'crs': CRS.from_epsg(26911), 'transform': Affine(1.0, 0.0, 179523.99999999822,
       0.0, -1.0, 3824832.0)}
```

## Operating system

Mac and Ubuntu 

## Rasterio version and provenance

1.24 and master


cc @rsignell-usgs


Discussion:
I'm looking into whether we should be setting the working data type when we call GDALCreateWarpedVRT.
Interestingly, I can't reproduce the issue on my computer with GDAL 3.0.1. PR #1767 is checking with older versions of GDAL.
I'll check with GDAL 3.0 too 
I can't reproduce this on any version of GDAL @vincentsarago . Can you check that my test is correct? https://github.com/mapbox/rasterio/pull/1767/files#diff-e2937f93425979eec69dffba64475224R397
@sgillies I could just reproduce the bug using the file I shared 
@vincentsarago I looked in GDAL and there's a GDALWarpResolveWorkingDataType function that calls GDALDataTypeUnion or GDALDataTypeUnionWithValue. Those functions determine based on the nodata value that float64 is the right data type for the warped VRT. Without a nodata value, you would get "float32". Can you eliminate the nodata value in your case? Or change it to `nan`? (though I'm not sure what would happen ther) I'm not sure I want to work around GDAL in this situation.

Resolving patch:
diff --git a/rasterio/features.py b/rasterio/features.py
--- a/rasterio/features.py
+++ b/rasterio/features.py
@@ -419,8 +419,8 @@ def geometry_window(dataset, shapes, pad_x=0, pad_y=0, north_up=True,
         right = min(dataset.shape[1], right)
         bottom = min(dataset.shape[0], bottom)
         # convert the bounds back to the CRS domain
-        left, top = (left, top) * dataset.transform
-        right, bottom = (right, bottom) * dataset.transform
+        left, top = dataset.transform * (left, top)
+        right, bottom = dataset.transform * (right, bottom)
 
     window = dataset.window(left, bottom, right, top)
     window_floored = window.round_offsets(op='floor', pixel_precision=pixel_precision)
diff --git a/rasterio/vrt.py b/rasterio/vrt.py
--- a/rasterio/vrt.py
+++ b/rasterio/vrt.py
@@ -21,6 +21,57 @@ class WarpedVRT(WarpedVRTReaderBase, WindowMethodsMixin,
 
     This class is backed by an in-memory GDAL VRTWarpedDataset VRT file.
 
+    Parameters
+    ----------
+    src_dataset : dataset object
+        The warp source.
+    src_crs : CRS or str, optional
+        Overrides the coordinate reference system of `src_dataset`.
+    src_transfrom : Affine, optional
+        Overrides the transform of `src_dataset`.
+    src_nodata : float, optional
+        Overrides the nodata value of `src_dataset`, which is the
+        default.
+    crs : CRS or str, optional
+        The coordinate reference system at the end of the warp
+        operation.  Default: the crs of `src_dataset`. dst_crs is
+        a deprecated alias for this parameter.
+    transform : Affine, optional
+        The transform for the virtual dataset. Default: will be
+        computed from the attributes of `src_dataset`. dst_transform
+        is a deprecated alias for this parameter.
+    height, width: int, optional
+        The dimensions of the virtual dataset. Defaults: will be
+        computed from the attributes of `src_dataset`. dst_height
+        and dst_width are deprecated alias for these parameters.
+    nodata : float, optional
+        Nodata value for the virtual dataset. Default: the nodata
+        value of `src_dataset` or 0.0. dst_nodata is a deprecated
+        alias for this parameter.
+    resampling : Resampling, optional
+        Warp resampling algorithm. Default: `Resampling.nearest`.
+    tolerance : float, optional
+        The maximum error tolerance in input pixels when
+        approximating the warp transformation. Default: 0.125,
+        or one-eigth of a pixel.
+    src_alpha : int, optional
+        Index of a source band to use as an alpha band for warping.
+    add_alpha : bool, optional
+        Whether to add an alpha masking band to the virtual dataset.
+        Default: False. This option will cause deletion of the VRT
+        nodata value.
+    init_dest_nodata : bool, optional
+        Whether or not to initialize output to `nodata`. Default:
+        True.
+    warp_mem_limit : int, optional
+        The warp operation's memory limit in MB. The default (0)
+        means 64 MB with GDAL 2.2.
+    dtype : str, optional
+        The working data type for warp operation and output.
+    warp_extras : dict
+        GDAL extra warp options. See
+        http://www.gdal.org/structGDALWarpOptions.html.
+
     Attributes
     ----------
     src_dataset : dataset
@@ -39,6 +90,8 @@ class WarpedVRT(WarpedVRTReaderBase, WindowMethodsMixin,
         The nodata value used to initialize the destination; it will
         remain in all areas not covered by the reprojected source.
         Defaults to the value of src_nodata, or 0 (gdal default).
+    working_dtype : str, optional
+        The working data type for warp operation and output.
     warp_extras : dict
         GDAL extra warp options. See
         http://www.gdal.org/structGDALWarpOptions.html.


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -27,8 +27,8 @@
     reduce = functools.reduce
 
 test_files = [os.path.join(os.path.dirname(__file__), p) for p in [
-    'data/RGB.byte.tif', 'data/float.tif', 'data/float_nan.tif',
-    'data/shade.tif', 'data/RGBA.byte.tif']]
+    'data/RGB.byte.tif', 'data/float.tif', 'data/float32.tif',
+    'data/float_nan.tif', 'data/shade.tif', 'data/RGBA.byte.tif']]
 
 
 def pytest_cmdline_main(config):
diff --git a/tests/test_warpedvrt.py b/tests/test_warpedvrt.py
--- a/tests/test_warpedvrt.py
+++ b/tests/test_warpedvrt.py
@@ -399,3 +399,37 @@ def test_warpedvrt_float32_preserve(data):
     with rasterio.open("tests/data/float32.tif") as src:
         with WarpedVRT(src, src_crs="EPSG:4326") as vrt:
             assert src.dtypes == vrt.dtypes == ("float32",)
+
+
+def test_warpedvrt_float32_override(data):
+    """Override GDAL defaults for working data type"""
+    float32file = str(data.join("float32.tif"))
+    with rasterio.open(float32file, "r+") as dst:
+        dst.nodata = -3.4028230607370965e+38
+
+    with rasterio.open(float32file) as src:
+        with WarpedVRT(src, src_crs="EPSG:4326", dtype="float32") as vrt:
+            assert src.dtypes == vrt.dtypes == ("float32",)
+
+
+def test_warpedvrt_float32_overridei_nodata(data):
+    """Override GDAL defaults for working data type"""
+    float32file = str(data.join("float32.tif"))
+    with rasterio.open(float32file, "r+") as dst:
+        dst.nodata = -3.4028230607370965e+38
+
+    with rasterio.open(float32file) as src:
+        with WarpedVRT(src, src_crs="EPSG:4326", nodata=0.0001, dtype="float32") as vrt:
+            assert src.dtypes == vrt.dtypes == ("float32",)
+
+
+@pytest.mark.xfail(reason="GDAL's output defaults to float64")
+def test_warpedvrt_issue1744(data):
+    """Reproduce the bug reported in 1744"""
+    float32file = str(data.join("float32.tif"))
+    with rasterio.open(float32file, "r+") as dst:
+        dst.nodata = -3.4028230607370965e+38
+
+    with rasterio.open(float32file) as src:
+        with WarpedVRT(src, src_crs="EPSG:4326") as vrt:
+            assert src.dtypes == vrt.dtypes == ("float32",)