Repository: corteva/geocube

Issue:
REF: Update grid mapping attribute handling
https://github.com/corteva/geocube/runs/2592740629

```
 ValueError: failed to prevent overwriting existing key grid_mapping in attrs. This is probably an encoding field used by xarray to describe how a variable is serialized. To proceed, remove this key from the variable's attributes manually.
```

This change likely has something to do with it: https://github.com/corteva/rioxarray/pull/284

Discussion:

Patch:
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 93d97c3..59b31c2 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -110,7 +110,7 @@ Before you submit a pull request, check that it meets these guidelines:
 2. If the pull request adds functionality, the docs should be updated. Put
    your new functionality into a function with a docstring, and add the
    feature to the list in README.rst.
-3. The pull request should work for Python 3.6-3.9.
+3. The pull request should work for Python 3.7-3.9.
 
 Tips
 ----
diff --git a/docs/history.rst b/docs/history.rst
index 7c2b3e4..2a40e80 100644
--- a/docs/history.rst
+++ b/docs/history.rst
@@ -3,6 +3,8 @@ History
 
 Latest
 ------
+- DEP: Python 3.7+ (#67)
+- REF: Write grid_mapping to encoding instead of attrs (#66)
 
 0.0.16
 ------
diff --git a/geocube/vector_to_cube.py b/geocube/vector_to_cube.py
index a606743..4cf553a 100644
--- a/geocube/vector_to_cube.py
+++ b/geocube/vector_to_cube.py
@@ -183,7 +183,6 @@ class VectorToCube:
         return dict(
             name=measurement_name,
             long_name=measurement_name,
-            grid_mapping=DEFAULT_GRID_MAP,
             _FillValue=fill_value,
         )
 
@@ -314,7 +313,12 @@ class VectorToCube:
         if df_group is not None and "datetime" in str(df_group[measurement_name].dtype):
             self._update_time_attrs(attrs, image_data)
 
-        return (group_by, "y", "x"), image_data, attrs
+        return (
+            (group_by, "y", "x"),
+            image_data,
+            attrs,
+            {"grid_mapping": DEFAULT_GRID_MAP},
+        )
 
     def _get_grid(self, dataframe, measurement_name):
         """Retrieve the variable data to append to the ssurgo :obj:`xarray.Dataset`
@@ -357,4 +361,9 @@ class VectorToCube:
         if "datetime" in str(dataframe[measurement_name].dtype):
             self._update_time_attrs(attrs, image_data)
 
-        return ("y", "x"), numpy.array(image_data), attrs
+        return (
+            ("y", "x"),
+            numpy.array(image_data),
+            attrs,
+            {"grid_mapping": DEFAULT_GRID_MAP},
+        )
diff --git a/setup.py b/setup.py
index 4345122..d52c855 100644
--- a/setup.py
+++ b/setup.py
@@ -15,8 +15,8 @@ requirements = [
     "datacube",
     "geopandas>=0.7",
     "rasterio",
-    "rioxarray>=0.0.30",
-    "xarray",
+    "rioxarray>=0.4",
+    "xarray>=0.17",
     "pyproj>=2",
 ]
 
@@ -46,7 +46,6 @@ setup(
         "License :: OSI Approved :: BSD License",
         "Natural Language :: English",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
         "Programming Language :: Python :: 3.8",
         "Programming Language :: Python :: 3.9",
@@ -66,5 +65,5 @@ setup(
     url="https://github.com/corteva/geocube",
     version=__version__,
     zip_safe=False,
-    python_requires=">=3.6",
+    python_requires=">=3.7",
 )


Test patch:
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 9b23b46..e01011c 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -23,13 +23,8 @@ jobs:
     strategy:
       fail-fast: true
       matrix:
-        os: [ubuntu-latest, macos-latest]
-        python-version: [3.6, 3.7, 3.8] # skip 3.9 for now
-        include:
-          - os: windows-latest
-            python-version: 3.7
-          - os: windows-latest
-            python-version: 3.8
+        os: [ubuntu-latest, macos-latest, windows-latest]
+        python-version: [3.7, 3.8] # skip 3.9 for now
 
     steps:
       - uses: actions/checkout@v2
diff --git a/test/integration/api/test_core_integration.py b/test/integration/api/test_core_integration.py
index 78276c6..a597a3a 100644
--- a/test/integration/api/test_core_integration.py
+++ b/test/integration/api/test_core_integration.py
@@ -70,7 +70,9 @@ def test_make_geocube(input_geodata, tmpdir):
 
     # test output data
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -106,6 +108,7 @@ def test_make_geocube__categorical(input_geodata, tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat_categorical.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -148,6 +151,7 @@ def test_make_geocube__interpolate_na(input_geodata, tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat_interpolate_na.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -172,7 +176,9 @@ def test_make_geocube__like(input_geodata, tmpdir):
     ]
 
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         out_grid = make_geocube(
             vector_data=input_geodata,
@@ -223,6 +229,7 @@ def test_make_geocube__only_resolution(input_geodata, tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat_original_crs.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -248,7 +255,9 @@ def test_make_geocube__convert_time(input_geodata, tmpdir):
 
     # test output data
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "time_vector_data.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "time_vector_data.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -278,7 +287,9 @@ def test_make_geocube__like_error_invalid_args(load_extra_kwargs):
     ]
 
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         with pytest.raises(AssertionError):
             make_geocube(
@@ -311,7 +322,9 @@ def test_make_geocube__no_measurements(input_geodata, tmpdir):
 
     # test output data
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -335,6 +348,7 @@ def test_make_geocube__no_geom(tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_flat_no_geom.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -402,7 +416,9 @@ def test_make_geocube__group_by(input_geodata, tmpdir):
 
     # test output data
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -444,6 +460,7 @@ def test_make_geocube__group_by__categorical(input_geodata, tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group_categorical.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -470,7 +487,9 @@ def test_make_geocube__group_by_like(input_geodata, tmpdir):
     ]
 
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         out_grid = make_geocube(
             vector_data=input_geodata,
@@ -517,6 +536,7 @@ def test_make_geocube__group_by_only_resolution(input_geodata, tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_grouped_original_crs.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -547,6 +567,7 @@ def test_make_geocube__group_by_time(input_geodata, tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "vector_time_data_group.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -578,6 +599,7 @@ def test_make_geocube__group_by_convert_with_time(input_geodata, tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "vector_data_group.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -609,7 +631,9 @@ def test_make_geocube__group_by_like_error_invalid_args(load_extra_kwargs):
     ]
 
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         with pytest.raises(AssertionError):
             make_geocube(
@@ -648,7 +672,9 @@ def test_make_geocube__group_by_no_measurements(input_geodata, tmpdir):
 
     # test output data
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group.nc"),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -673,6 +699,7 @@ def test_make_geocube__group_by__no_geom(tmpdir):
     with xarray.open_dataset(
         os.path.join(TEST_COMPARE_DATA_DIR, "soil_grid_group_no_geom.nc"),
         mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc)
 
@@ -739,7 +766,9 @@ def test_make_geocube__custom_rasterize_function(function, compare_name, tmpdir)
 
     # test output data
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, compare_name), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, compare_name),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc, rtol=0.1, atol=0.1)
 
@@ -784,6 +813,8 @@ def test_make_geocube__custom_rasterize_function__filter_null(
 
     # test output data
     with xarray.open_dataset(
-        os.path.join(TEST_COMPARE_DATA_DIR, compare_name), mask_and_scale=False
+        os.path.join(TEST_COMPARE_DATA_DIR, compare_name),
+        mask_and_scale=False,
+        decode_coords="all",
     ) as xdc:
         xarray.testing.assert_allclose(out_grid, xdc, rtol=0.1, atol=0.1)