Repository: corteva/rioxarray

Issue:
Document difference between `set_crs` and `write_crs`
`set_crs`:

> Set the CRS value for the Dataset/DataArray without modifying the dataset/data array.

`write_crs`:

> Write the CRS to the dataset in a CF compliant manner.

The `input_crs` and `inplace` arguments are the same; the only difference in signature is that `write_crs` takes an optional `grid_mapping_name`.

In short, I'm confused :wink: 

Maybe consider `write_coordinate_system` in this issue as well.

Discussion:
snowman2: > when exactly should people use rio.set_crs() and not rio.write_crs()?

I would say never use `rio.set_crs` due to its instability. It was added a while a go and probably should be deprecated.
dluks: @snowman2 I've pushed two commits that address the `set_crs` definition. The first adds a warning to the docstring and the second emits an actual `DeprecationWarning` when using `set_crs`. Not sure if raising an actual warning is what you had in mind, so if not I can revert it.

Patch:
diff --git a/docs/getting_started/crs_management.ipynb b/docs/getting_started/crs_management.ipynb
index d6f94bde..54511d46 100644
--- a/docs/getting_started/crs_management.ipynb
+++ b/docs/getting_started/crs_management.ipynb
@@ -527,7 +527,9 @@
     "This modifies the `xarray.Dataset` or `xarray.DataArray` and sets the CRS in a CF compliant manner.\n",
     "\n",
     "- [rio.write_crs()](../rioxarray.rst#rioxarray.rioxarray.XRasterBase.write_crs)\n",
-    "- [rio.crs](../rioxarray.rst#rioxarray.rioxarray.XRasterBase.crs)"
+    "- [rio.crs](../rioxarray.rst#rioxarray.rioxarray.XRasterBase.crs)\n",
+    "\n",
+    "**Note:** It is recommended to use `rio.write_crs()` if you want the CRS to persist on the Dataset/DataArray and to write the CRS CF compliant metadata. Calling only `rio.set_crs()` CRS storage method is lossy and will not modify the Dataset/DataArray metadata."
    ]
   },
   {
@@ -1098,7 +1100,7 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.10.4"
+   "version": "3.12.4"
   }
  },
  "nbformat": 4,
diff --git a/docs/history.rst b/docs/history.rst
index 1dc5c5ac..4b0aa59b 100644
--- a/docs/history.rst
+++ b/docs/history.rst
@@ -1,8 +1,9 @@
 History
 =======
 
-Latest
+0.16.0
 ------
+- ENH: Add `allow_one_dimensional_raster` option to `rio.clip_box` (issue #708)
 
 0.15.7
 ------
diff --git a/rioxarray/raster_array.py b/rioxarray/raster_array.py
index 2d068abe..bfbd6578 100644
--- a/rioxarray/raster_array.py
+++ b/rioxarray/raster_array.py
@@ -713,10 +713,12 @@ class RasterArray(XRasterBase):
         auto_expand: Union[bool, int] = False,
         auto_expand_limit: int = 3,
         crs: Optional[Any] = None,
+        allow_one_dimensional_raster: bool = False,
     ) -> xarray.DataArray:
         """Clip the :obj:`xarray.DataArray` by a bounding box.
 
         .. versionadded:: 0.12 crs
+        .. versionadded:: 0.16 allow_one_dimensional_raster
 
         Parameters
         ----------
@@ -736,16 +738,19 @@ class RasterArray(XRasterBase):
         crs: :obj:`rasterio.crs.CRS`, optional
             The CRS of the bounding box. Default is to assume it is the same
             as the dataset.
+        allow_one_dimensional_raster: bool, optional
+            If True, allow clipping to/from a one dimensional raster.
 
         Returns
         -------
         xarray.DataArray:
             The clipped object.
         """
-        if self.width == 1 or self.height == 1:
+        if not allow_one_dimensional_raster and (self.width == 1 or self.height == 1):
             raise OneDimensionalRaster(
                 "At least one of the raster x,y coordinates has only one point."
-                f"{_get_data_var_message(self._obj)}"
+                f"{_get_data_var_message(self._obj)}. "
+                "Set allow_one_dimensional_raster=True to disable this error."
             )
 
         if crs is not None and self.crs is None:
@@ -819,11 +824,14 @@ class RasterArray(XRasterBase):
                 raise NoDataInBounds(
                     f"No data found in bounds.{_get_data_var_message(self._obj)}"
                 )
-            if cl_array.rio.width == 1 or cl_array.rio.height == 1:
+            if not allow_one_dimensional_raster and (
+                cl_array.rio.width == 1 or cl_array.rio.height == 1
+            ):
                 raise OneDimensionalRaster(
                     "At least one of the clipped raster x,y coordinates"
                     " has only one point."
-                    f"{_get_data_var_message(self._obj)}"
+                    f"{_get_data_var_message(self._obj)}. "
+                    "Set allow_one_dimensional_raster=True to disable this error."
                 )
 
         # make sure correct attributes preserved & projection added
diff --git a/rioxarray/raster_dataset.py b/rioxarray/raster_dataset.py
index 05d5e49b..93c5695e 100644
--- a/rioxarray/raster_dataset.py
+++ b/rioxarray/raster_dataset.py
@@ -256,12 +256,14 @@ class RasterDataset(XRasterBase):
         auto_expand: Union[bool, int] = False,
         auto_expand_limit: int = 3,
         crs: Optional[Any] = None,
+        allow_one_dimensional_raster: bool = False,
     ) -> xarray.Dataset:
         """Clip the :class:`xarray.Dataset` by a bounding box in dimensions 'x'/'y'.
 
         .. warning:: Clips variables that have dimensions 'x'/'y'. Others are appended as is.
 
         .. versionadded:: 0.12 crs
+        .. versionadded:: 0.16 allow_one_dimensional_raster
 
         Parameters
         ----------
@@ -281,6 +283,8 @@ class RasterDataset(XRasterBase):
         crs: :obj:`rasterio.crs.CRS`, optional
             The CRS of the bounding box. Default is to assume it is the same
             as the dataset.
+        allow_one_dimensional_raster: bool, optional
+            If True, allow clipping to/from a one dimensional raster.
 
         Returns
         -------
@@ -302,6 +306,7 @@ class RasterDataset(XRasterBase):
                         auto_expand=auto_expand,
                         auto_expand_limit=auto_expand_limit,
                         crs=crs,
+                        allow_one_dimensional_raster=allow_one_dimensional_raster,
                     )
                 )
             except MissingSpatialDimensionError:
diff --git a/rioxarray/rioxarray.py b/rioxarray/rioxarray.py
index 64e60841..4a418bf8 100644
--- a/rioxarray/rioxarray.py
+++ b/rioxarray/rioxarray.py
@@ -2,6 +2,7 @@
 This module is an extension for xarray to provide rasterio capabilities
 to xarray datasets/dataarrays.
 """
+
 # pylint: disable=too-many-lines
 import json
 import math
@@ -305,7 +306,7 @@ class XRasterBase:
         # pyproj CRS if possible for performance
         for crs_attr in ("spatial_ref", "crs_wkt"):
             try:
-                self.set_crs(
+                self._set_crs(
                     self._obj.coords[self.grid_mapping].attrs[crs_attr],
                     inplace=True,
                 )
@@ -315,14 +316,14 @@ class XRasterBase:
 
         # look in grid_mapping
         try:
-            self.set_crs(
+            self._set_crs(
                 pyproj.CRS.from_cf(self._obj.coords[self.grid_mapping].attrs),
                 inplace=True,
             )
         except (KeyError, pyproj.exceptions.CRSError):
             try:
                 # look in attrs for 'crs'
-                self.set_crs(self._obj.attrs["crs"], inplace=True)
+                self._set_crs(self._obj.attrs["crs"], inplace=True)
             except KeyError:
                 self._crs = False
                 return None
@@ -360,6 +361,10 @@ class XRasterBase:
         Set the CRS value for the Dataset/DataArray without modifying
         the dataset/data array.
 
+        .. deprecated:: 0.15.8
+            It is recommended to use `rio.write_crs()` instead. This
+        method will likely be removed in a future release.
+
         Parameters
         ----------
         input_crs: object
@@ -372,6 +377,34 @@ class XRasterBase:
         :obj:`xarray.Dataset` | :obj:`xarray.DataArray`:
             Dataset with crs attribute.
         """
+        warnings.warn(
+            "It is recommended to use 'rio.write_crs()' instead. 'rio.set_crs()' will likely"
+            "be removed in a future release.",
+            FutureWarning,
+            stacklevel=2,
+        )
+
+        return self._set_crs(input_crs, inplace=inplace)
+
+    def _set_crs(
+        self, input_crs: Any, inplace: bool = True
+    ) -> Union[xarray.Dataset, xarray.DataArray]:
+        """
+        Set the CRS value for the Dataset/DataArray without modifying
+        the dataset/data array.
+
+        Parameters
+        ----------
+        input_crs: object
+            Anything accepted by `rasterio.crs.CRS.from_user_input`.
+        inplace: bool, optional
+            If True, it will write to the existing dataset. Default is False.
+
+        Returns
+        -------
+        xarray.Dataset | xarray.DataArray
+            Dataset with crs attribute.
+        """
         crs = crs_from_user_input(input_crs)
         obj = self._get_obj(inplace=inplace)
         obj.rio._crs = crs
@@ -483,7 +516,7 @@ class XRasterBase:
         >>> raster = raster.rio.write_crs("epsg:4326")
         """
         if input_crs is not None:
-            data_obj = self.set_crs(input_crs, inplace=inplace)
+            data_obj = self._set_crs(input_crs, inplace=inplace)
         else:
             data_obj = self._get_obj(inplace=inplace)
 


Test patch:
diff --git a/test/integration/test_integration_rioxarray.py b/test/integration/test_integration_rioxarray.py
index e01270c7..a5b54457 100644
--- a/test/integration/test_integration_rioxarray.py
+++ b/test/integration/test_integration_rioxarray.py
@@ -418,6 +418,27 @@ def test_clip_box__one_dimension_error(modis_clip):
             )
 
 
+def test_clip_box__one_dimension_error__allowed(modis_clip):
+    with modis_clip["open"](modis_clip["input"]) as xdi:
+        # test after raster clipped
+        assert xdi.rio.clip_box(
+            minx=-7272735.53951584,  # xdi.x[5].values
+            miny=5048834.500182299,  # xdi.y[5].values
+            maxx=-7272735.53951584,  # xdi.x[5].values
+            maxy=5048834.500182299,  # xdi.y[5].values
+            allow_one_dimensional_raster=True,
+        ).rio.shape == (1, 1)
+        # test before raster clipped
+        if not isinstance(modis_clip["open"], partial):
+            assert xdi.isel(x=slice(5, 6), y=slice(5, 6)).rio.clip_box(
+                minx=-7272735.53951584,  # xdi.x[5].values
+                miny=5048371.187465771,  # xdi.y[7].values
+                maxx=-7272272.226799311,  # xdi.x[7].values
+                maxy=5048834.500182299,  # xdi.y[5].values
+                allow_one_dimensional_raster=True,
+            ).rio.shape == (1, 1)
+
+
 def test_clip_box__nodata_in_bounds():
     with rioxarray.open_rasterio(
         os.path.join(TEST_INPUT_DATA_DIR, "clip_bbox__out_of_bounds.tif")