Skip to content

Commit 6e40e2f

Browse files
authored
Merge pull request #624 from dcs4cop/forman-623-fix_fs_data_accessors
Fix FsDataAccessor.write_data() impls
2 parents 2d3f8bb + 8ac31b0 commit 6e40e2f

File tree

6 files changed

+40
-15
lines changed

6 files changed

+40
-15
lines changed

CHANGES.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@
44

55
### Fixes
66

7-
* Images with ascending y-values are tiled correctly. This fixes an issue where
8-
some datasets seemed to be shifted in the y-(latitude-) direction and were
9-
misplaced on maps. (#626)
7+
* Fixed `FsDataAccessor.write_data()` implementations,
8+
which now always return the passed in `data_id`. (#623)
9+
10+
* Fixes an issue where some datasets seemed to be shifted in the
11+
y-(latitude-) direction and were misplaced on maps whose tiles
12+
are served by `xcube serve`. Images with ascending y-values are
13+
now tiled correctly. (#626)
1014

1115
### Other
1216

test/core/store/fs/test_registry.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ def assertDatasetSupported(
117117
self.assertEqual([], list(data_store.get_data_ids()))
118118

119119
data = new_cube(variables=dict(A=8, B=9))
120-
data_store.write_data(data, data_id)
120+
written_data_id = data_store.write_data(data, data_id)
121+
self.assertEqual(data_id, written_data_id)
121122
self.assertEqual({expected_data_type_alias},
122123
set(data_store.get_data_types_for_data(data_id)))
123124
self.assertEqual(True, data_store.has_data(data_id))

xcube/core/store/fs/impl/dataset.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ def write_data(self,
191191
data: xr.Dataset,
192192
data_id: str,
193193
replace=False,
194-
**write_params):
194+
**write_params) -> str:
195195
assert_instance(data, xr.Dataset, name='data')
196196
assert_instance(data_id, str, name='data_id')
197197
fs, write_params = self.load_fs(write_params)
@@ -209,6 +209,7 @@ def write_data(self,
209209
except ValueError as e:
210210
raise DataStoreError(f'Failed to write'
211211
f' dataset {data_id!r}: {e}') from e
212+
return data_id
212213

213214
def delete_data(self,
214215
data_id: str,
@@ -277,7 +278,7 @@ def write_data(self,
277278
data: xr.Dataset,
278279
data_id: str,
279280
replace=False,
280-
**write_params):
281+
**write_params) -> str:
281282
assert_instance(data, xr.Dataset, name='data')
282283
assert_instance(data_id, str, name='data_id')
283284
fs, write_params = self.load_fs(write_params)
@@ -298,3 +299,4 @@ def write_data(self,
298299
data.to_netcdf(file_path, engine=engine, **write_params)
299300
if not is_local:
300301
fs.put_file(file_path, data_id)
302+
return data_id

xcube/core/store/fs/impl/geodataframe.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,10 @@ def get_write_data_params_schema(self) -> JsonObjectSchema:
7979
),
8080
)
8181

82-
def write_data(self, data: gpd.GeoDataFrame, data_id: str, **write_params):
82+
def write_data(self,
83+
data: gpd.GeoDataFrame,
84+
data_id: str,
85+
**write_params) -> str:
8386
# TODO: implement me correctly,
8487
# this is not valid for shapefile AND geojson
8588
assert_instance(data, (gpd.GeoDataFrame, pd.DataFrame), 'data')
@@ -92,6 +95,7 @@ def write_data(self, data: gpd.GeoDataFrame, data_id: str, **write_params):
9295
data.to_file(file_path, driver=self.get_driver_name(), **write_params)
9396
if not is_local:
9497
fs.put_file(file_path, data_id)
98+
return data_id
9599

96100

97101
class GeoDataFrameShapefileFsDataAccessor(GeoDataFrameFsDataAccessor, ABC):

xcube/core/store/fs/impl/mldataset.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def write_data(self,
7171
data: Union[xr.Dataset, MultiLevelDataset],
7272
data_id: str,
7373
replace=False,
74-
**write_params):
74+
**write_params) -> str:
7575
assert_instance(data, (xr.Dataset, MultiLevelDataset), name='data')
7676
assert_instance(data_id, str, name='data_id')
7777
if isinstance(data, MultiLevelDataset):
@@ -108,6 +108,7 @@ def write_data(self,
108108
level_dataset = xr.open_zarr(zarr_store,
109109
consolidated=consolidated)
110110
ml_dataset.set_dataset(index, level_dataset)
111+
return data_id
111112

112113

113114
class FsMultiLevelDataset(LazyMultiLevelDataset):
@@ -139,7 +140,8 @@ def _get_dataset_lazily(self, index: int, parameters) \
139140

140141
def _get_tile_grid_lazily(self) -> TileGrid:
141142
"""
142-
Retrieve, i.e. read or compute, the tile grid used by the multi-level dataset.
143+
Retrieve, i.e. read or compute, the tile grid used
144+
by the multi-level dataset.
143145
144146
:return: the dataset for the level at *index*.
145147
"""

xcube/core/store/fs/store.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,14 @@
3434
from xcube.util.assertions import assert_given
3535
from xcube.util.assertions import assert_in
3636
from xcube.util.assertions import assert_instance
37+
from xcube.util.assertions import assert_true
3738
from xcube.util.extension import Extension
3839
from xcube.util.jsonschema import JsonBooleanSchema
3940
from xcube.util.jsonschema import JsonIntegerSchema
4041
from xcube.util.jsonschema import JsonObjectSchema
4142
from xcube.util.jsonschema import JsonStringSchema
42-
from .accessor import STORAGE_OPTIONS_PARAM_NAME
4343
from .accessor import FsAccessor
44+
from .accessor import STORAGE_OPTIONS_PARAM_NAME
4445
from .helpers import is_local_fs
4546
from .helpers import normalize_path
4647
from ..accessor import DataOpener
@@ -296,11 +297,22 @@ def write_data(self,
296297
data_id = self._ensure_valid_data_id(writer_id, data_id=data_id)
297298
fs_path = self._convert_data_id_into_fs_path(data_id)
298299
self.fs.makedirs(self.root, exist_ok=True)
299-
writer.write_data(data,
300-
fs_path,
301-
replace=replace,
302-
fs=self.fs,
303-
**write_params)
300+
written_fs_path = writer.write_data(data,
301+
fs_path,
302+
replace=replace,
303+
fs=self.fs,
304+
**write_params)
305+
# Verify, accessors fulfill their write_data() contract
306+
assert_true(fs_path == written_fs_path,
307+
message='FsDataAccessor implementations must '
308+
'return the data_id passed in.')
309+
# Return original data_id (which is a relative path).
310+
# Note: it would be cleaner to return written_fs_path
311+
# here, but it is an absolute path.
312+
# --> Possible solution: FsDataAccessor implementations
313+
# should be responsible for resolving their data_id into a
314+
# filesystem path by providing them both with fs and root
315+
# arguments.
304316
return data_id
305317

306318
def get_delete_data_params_schema(self, data_id: str = None) \

0 commit comments

Comments
 (0)