Repository: corteva/rioxarray

Issue:
Keep in memory original data type for writing
It could be very nice to keep in memory the original data type (ie. `uint16` for landsat data or `uint8` for classified data).

Indeed, for now (unless I missed something), if we open a dataset with `masked=True` and the data is casted to float, we lose the information of the original datatype.
So we always write the dataset on disk in float, even when it is not justified (when the nodata is replaced by its true value)

Discussion:

Patch:
diff --git a/docs/history.rst b/docs/history.rst
index 2732124a..5448ef84 100644
--- a/docs/history.rst
+++ b/docs/history.rst
@@ -12,6 +12,7 @@ Latest
 - BUG: Return correct transform in `rio.transform` with non-rectilinear transform (discussions #280)
 - BUG: Update to handle WindowError in rasterio 1.2.2 (issue #286)
 - BUG: Don't generate x,y coords in `rio` methods if not previously there (pull #294)
+- BUG: Preserve original data type for writing to disk (issue #305)
 
 0.3.2
 -----
diff --git a/rioxarray/_io.py b/rioxarray/_io.py
index d4e664ab..0f72ffbd 100644
--- a/rioxarray/_io.py
+++ b/rioxarray/_io.py
@@ -812,6 +812,8 @@ def open_rasterio(
     encoding = {}
     if mask_and_scale and "_Unsigned" in attrs:
         unsigned = variables.pop_to(attrs, encoding, "_Unsigned") == "true"
+    if masked:
+        encoding["dtype"] = str(riods.dtypes[0])
 
     da_name = attrs.pop("NETCDF_VARNAME", default_name)
     data = indexing.LazilyOuterIndexedArray(
diff --git a/rioxarray/raster_array.py b/rioxarray/raster_array.py
index ce9a0cd5..eeda38f6 100644
--- a/rioxarray/raster_array.py
+++ b/rioxarray/raster_array.py
@@ -882,7 +882,11 @@ class RasterArray(XRasterBase):
         if driver is None and LooseVersion(rasterio.__version__) < LooseVersion("1.2"):
             driver = "GTiff"
 
-        dtype = str(self._obj.dtype) if dtype is None else dtype
+        dtype = (
+            self._obj.encoding.get("dtype", str(self._obj.dtype))
+            if dtype is None
+            else dtype
+        )
         # get the output profile from the rasterio object
         # if opened with xarray.open_rasterio()
         try:


Test patch:
diff --git a/test/integration/test_integration__io.py b/test/integration/test_integration__io.py
index 54447852..5bb68151 100644
--- a/test/integration/test_integration__io.py
+++ b/test/integration/test_integration__io.py
@@ -270,7 +270,11 @@ def test_open_rasterio_mask_chunk_clip():
         assert np.isnan(xdi.values).sum() == 52119
         test_encoding = dict(xdi.encoding)
         assert test_encoding.pop("source").endswith("small_dem_3m_merged.tif")
-        assert test_encoding == {"_FillValue": 0.0, "grid_mapping": "spatial_ref"}
+        assert test_encoding == {
+            "_FillValue": 0.0,
+            "grid_mapping": "spatial_ref",
+            "dtype": "uint16",
+        }
         attrs = dict(xdi.attrs)
         assert_almost_equal(
             tuple(xdi.rio._cached_transform())[:6],
@@ -307,7 +311,11 @@ def test_open_rasterio_mask_chunk_clip():
         _assert_xarrays_equal(clipped, comp_subset)
         test_encoding = dict(clipped.encoding)
         assert test_encoding.pop("source").endswith("small_dem_3m_merged.tif")
-        assert test_encoding == {"_FillValue": 0.0, "grid_mapping": "spatial_ref"}
+        assert test_encoding == {
+            "_FillValue": 0.0,
+            "grid_mapping": "spatial_ref",
+            "dtype": "uint16",
+        }
 
         # test dataset
         clipped_ds = xdi.to_dataset(name="test_data").rio.clip(
@@ -317,7 +325,11 @@ def test_open_rasterio_mask_chunk_clip():
         _assert_xarrays_equal(clipped_ds, comp_subset_ds)
         test_encoding = dict(clipped.encoding)
         assert test_encoding.pop("source").endswith("small_dem_3m_merged.tif")
-        assert test_encoding == {"_FillValue": 0.0, "grid_mapping": "spatial_ref"}
+        assert test_encoding == {
+            "_FillValue": 0.0,
+            "grid_mapping": "spatial_ref",
+            "dtype": "uint16",
+        }
 
 
 ##############################################################################
@@ -882,6 +894,7 @@ def test_mask_and_scale(open_rasterio):
                 "_FillValue": 32767.0,
                 "missing_value": 32767,
                 "grid_mapping": "crs",
+                "dtype": "uint16",
             }
             attrs = rds.air_temperature.attrs
             assert attrs == {
@@ -912,6 +925,7 @@ def test_no_mask_and_scale(open_rasterio):
             "_FillValue": 32767.0,
             "missing_value": 32767,
             "grid_mapping": "crs",
+            "dtype": "uint16",
         }
         attrs = rds.air_temperature.attrs
         assert attrs == {
diff --git a/test/integration/test_integration_rioxarray.py b/test/integration/test_integration_rioxarray.py
index ed4ae546..ebe6a735 100644
--- a/test/integration/test_integration_rioxarray.py
+++ b/test/integration/test_integration_rioxarray.py
@@ -1160,6 +1160,7 @@ def test_to_raster(
         assert_array_equal(rds.read(1), xds.fillna(xds.rio.encoded_nodata).values)
         assert rds.count == 1
         assert rds.tags() == {"AREA_OR_POINT": "Area", **test_tags, **xds_attrs}
+        assert rds.dtypes == ("int16",)
 
 
 @pytest.mark.parametrize(