Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

REF: Store grid_mapping in encoding instead of attrs #284

Merged
merged 2 commits into from
Apr 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ jobs:
rasterio-version: '*'
xarray-version: '*'
- os: ubuntu-latest
python-version: 3.6
python-version: 3.7
rasterio-version: 1.1
xarray-version: 0.16
xarray-version: '*'
steps:
- uses: actions/checkout@v2

Expand Down
3 changes: 3 additions & 0 deletions docs/history.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ History

Latest
------
- DEP: Python 3.6+ (issue #215)
- DEP: xarray 0.17+ (needed for issue #282)
- REF: Store `grid_mapping` in `encoding` instead of `attrs` (issue #282)

0.3.2
-----
Expand Down
7 changes: 1 addition & 6 deletions rioxarray/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@

import rioxarray.raster_array # noqa
import rioxarray.raster_dataset # noqa
from rioxarray._io import open_rasterio # noqa
from rioxarray._options import set_options # noqa
from rioxarray._show_versions import show_versions # noqa
from rioxarray._version import __version__ # noqa

try:
# This requires xarray >= 0.12.3
from rioxarray._io import open_rasterio # noqa
except ImportError:
from xarray import open_rasterio # noqa
9 changes: 3 additions & 6 deletions rioxarray/_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,8 @@ def _handle_encoding(result, mask_and_scale, masked, da_name):
"""
Make sure encoding handled properly
"""
if "grid_mapping" in result.attrs:
variables.pop_to(result.attrs, result.encoding, "grid_mapping", name=da_name)
if mask_and_scale:
if "scale_factor" in result.attrs:
variables.pop_to(
Expand Down Expand Up @@ -851,7 +853,6 @@ def open_rasterio(

# handle encoding
_handle_encoding(result, mask_and_scale, masked, da_name)

# Affine transformation matrix (always available)
# This describes coefficients mapping pixel coordinates to CRS
# For serialization store as tuple of 6 floats, the last row being
Expand All @@ -865,11 +866,7 @@ def open_rasterio(
result = _prepare_dask(result, riods, filename, chunks)

# Make the file closeable
try:
# xarray 0.17 +
result.set_close(manager.close)
except AttributeError:
result._file_obj = manager
result.set_close(manager.close)
result.rio._manager = manager
# add file path to encoding
result.encoding["source"] = riods.name
Expand Down
4 changes: 1 addition & 3 deletions rioxarray/raster_array.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ def _generate_attrs(src_data_array, dst_nodata):
if src_data_array.rio.encoded_nodata is None and fill_value is not None:
new_attrs["_FillValue"] = fill_value

# add raster spatial information
new_attrs["grid_mapping"] = src_data_array.rio.grid_mapping

return new_attrs


Expand All @@ -75,6 +72,7 @@ def _add_attrs_proj(new_data_array, src_data_array):
new_data_array.rio.set_attrs(new_attrs, inplace=True)

# make sure projection added
new_data_array.rio.write_grid_mapping(src_data_array.rio.grid_mapping, inplace=True)
new_data_array.rio.write_crs(src_data_array.rio.crs, inplace=True)
new_data_array.rio.write_coordinate_system(inplace=True)
new_data_array.rio.write_transform(inplace=True)
Expand Down
87 changes: 75 additions & 12 deletions rioxarray/rioxarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,11 @@ def grid_mapping(self):
"""
str: The CF grid_mapping attribute. 'spatial_ref' is the default.
"""
try:
return self._obj.attrs["grid_mapping"]
except KeyError:
pass
grid_mapping = self._obj.encoding.get(
"grid_mapping", self._obj.attrs.get("grid_mapping")
)
if grid_mapping is not None:
return grid_mapping
grid_mapping = DEFAULT_GRID_MAP
# search the dataset for the grid mapping name
if hasattr(self._obj, "data_vars"):
Expand All @@ -245,11 +246,12 @@ def grid_mapping(self):
self._obj[var].rio.y_dim
except DimensionError:
continue
try:
grid_mapping = self._obj[var].attrs["grid_mapping"]
var_grid_mapping = self._obj[var].encoding.get(
"grid_mapping", self._obj[var].attrs.get("grid_mapping")
)
if var_grid_mapping is not None:
grid_mapping = var_grid_mapping
grid_mappings.add(grid_mapping)
except KeyError:
pass
if len(grid_mappings) > 1:
raise RioXarrayError("Multiple grid mappings exist.")
return grid_mapping
Expand Down Expand Up @@ -279,12 +281,22 @@ def write_grid_mapping(self, grid_mapping_name=DEFAULT_GRID_MAP, inplace=False):
except DimensionError:
continue

data_obj[var].rio.update_attrs(
# remove grid_mapping from attributes if it exists
# and update the grid_mapping in encoding
new_attrs = dict(data_obj[var].attrs)
new_attrs.pop("grid_mapping", None)
data_obj[var].rio.update_encoding(
dict(grid_mapping=grid_mapping_name), inplace=True
).rio.set_spatial_dims(x_dim=x_dim, y_dim=y_dim, inplace=True)
return data_obj.rio.update_attrs(
).rio.update_attrs(new_attrs, inplace=True).rio.set_spatial_dims(
x_dim=x_dim, y_dim=y_dim, inplace=True
)
# remove grid_mapping from attributes if it exists
# and update the grid_mapping in encoding
new_attrs = dict(data_obj.attrs)
new_attrs.pop("grid_mapping", None)
return data_obj.rio.update_encoding(
dict(grid_mapping=grid_mapping_name), inplace=True
)
).rio.update_attrs(new_attrs, inplace=True)

def write_crs(self, input_crs=None, grid_mapping_name=None, inplace=False):
"""
Expand Down Expand Up @@ -591,6 +603,57 @@ def update_attrs(self, new_attrs, inplace=False):
data_attrs.update(**new_attrs)
return self.set_attrs(data_attrs, inplace=inplace)

def set_encoding(self, new_encoding, inplace=False):
"""
Set the encoding of the dataset/dataarray and reset
rioxarray properties to re-search for them.

.. versionadded:: 0.4

Parameters
----------
new_encoding: dict
A dictionary for encoding.
inplace: bool, optional
If True, it will write to the existing dataset. Default is False.

Returns
-------
:obj:`xarray.Dataset` | :obj:`xarray.DataArray`:
Modified dataset with new attributes.
"""
data_obj = self._get_obj(inplace=inplace)
# set the attributes
data_obj.encoding = new_encoding
# reset rioxarray properties depending
# on attributes to be generated
data_obj.rio._nodata = None
data_obj.rio._crs = None
return data_obj

def update_encoding(self, new_encoding, inplace=False):
"""
Update the encoding of the dataset/dataarray and reset
rioxarray properties to re-search for them.

.. versionadded:: 0.4

Parameters
----------
new_encoding: dict
A dictionary with encoding values to update with.
inplace: bool, optional
If True, it will write to the existing dataset. Default is False.

Returns
-------
:obj:`xarray.Dataset` | :obj:`xarray.DataArray`:
Modified dataset with updated attributes.
"""
data_encoding = dict(self._obj.encoding)
data_encoding.update(**new_encoding)
return self.set_encoding(data_encoding, inplace=inplace)

def set_spatial_dims(self, x_dim, y_dim, inplace=True):
"""
This sets the spatial dimensions of the dataset.
Expand Down
5 changes: 2 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def get_version():
with open("README.rst") as readme_file:
readme = readme_file.read()

requirements = ["rasterio", "scipy", "xarray", "pyproj>=2.2"]
requirements = ["rasterio", "scipy", "xarray>=0.17", "pyproj>=2.2"]

test_requirements = ["pytest>=3.6", "pytest-cov", "dask"]
doc_requirements = ["sphinx-click==1.1.0", "nbsphinx", "sphinx_rtd_theme"]
Expand Down Expand Up @@ -52,7 +52,6 @@ def get_version():
"Natural Language :: English",
"Topic :: Scientific/Engineering :: GIS",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
Expand All @@ -71,5 +70,5 @@ def get_version():
url="https://github.com/corteva/rioxarray",
version=get_version(),
zip_safe=False,
python_requires=">=3.6",
python_requires=">=3.7",
)
7 changes: 2 additions & 5 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def _assert_attrs_equal(input_xr, compare_xr, decimal_precision):
attr != "_FillValue"
and attr not in UNWANTED_RIO_ATTRS
and attr != "creation_date"
and attr != "grid_mapping"
):
try:
assert_almost_equal(
Expand Down Expand Up @@ -80,10 +81,6 @@ def _assert_xarrays_equal(
"_FillValue", input_xarray.encoding.get("_FillValue")
)
assert_array_equal([input_fill_value], [compare_fill_value])
assert "grid_mapping" in compare_xarray.attrs
assert (
input_xarray[input_xarray.attrs["grid_mapping"]]
== compare_xarray[compare_xarray.attrs["grid_mapping"]]
)
assert input_xarray.rio.grid_mapping == compare_xarray.rio.grid_mapping
for unwanted_attr in UNWANTED_RIO_ATTRS:
assert unwanted_attr not in input_xarray.attrs
14 changes: 6 additions & 8 deletions test/integration/test_integration__io.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,14 @@ def test_open_group_load_attrs():
assert sorted(attrs) == [
"_FillValue",
"add_offset",
"grid_mapping",
"long_name",
"scale_factor",
"units",
]
assert attrs["long_name"] == "500m Surface Reflectance Band 5 - first layer"
assert attrs["units"] == "reflectance"
assert attrs["_FillValue"] == -28672.0
assert attrs["grid_mapping"] == "spatial_ref"
assert rds["sur_refl_b05_1"].encoding["grid_mapping"] == "spatial_ref"


def test_open_rasterio_mask_chunk_clip():
Expand All @@ -267,14 +266,13 @@ 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}
assert test_encoding == {"_FillValue": 0.0, "grid_mapping": "spatial_ref"}
attrs = dict(xdi.attrs)
assert_almost_equal(
tuple(xdi.rio._cached_transform())[:6],
(3.0, 0.0, 425047.68381405267, 0.0, -3.0, 4615780.040546387),
)
assert attrs == {
"grid_mapping": "spatial_ref",
"add_offset": 0.0,
"scale_factor": 1.0,
}
Expand Down Expand Up @@ -305,7 +303,7 @@ 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}
assert test_encoding == {"_FillValue": 0.0, "grid_mapping": "spatial_ref"}

# test dataset
clipped_ds = xdi.to_dataset(name="test_data").rio.clip(
Expand All @@ -315,7 +313,7 @@ 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}
assert test_encoding == {"_FillValue": 0.0, "grid_mapping": "spatial_ref"}


##############################################################################
Expand Down Expand Up @@ -895,14 +893,14 @@ def test_mask_and_scale():
"scale_factor": 0.1,
"_FillValue": 32767.0,
"missing_value": 32767,
"grid_mapping": "crs",
}
attrs = rds.air_temperature.attrs
assert attrs == {
"coordinates": "day",
"coordinate_system": "WGS84,EPSG:4326",
"description": "Daily Maximum Temperature",
"dimensions": "lon lat time",
"grid_mapping": "crs",
"long_name": "tmmx",
"standard_name": "tmmx",
"units": "K",
Expand All @@ -923,6 +921,7 @@ def test_no_mask_and_scale():
assert test_encoding == {
"_FillValue": 32767.0,
"missing_value": 32767,
"grid_mapping": "crs",
}
attrs = rds.air_temperature.attrs
assert attrs == {
Expand All @@ -932,7 +931,6 @@ def test_no_mask_and_scale():
"coordinate_system": "WGS84,EPSG:4326",
"description": "Daily Maximum Temperature",
"dimensions": "lon lat time",
"grid_mapping": "crs",
"long_name": "tmmx",
"scale_factor": 0.1,
"standard_name": "tmmx",
Expand Down
Loading