Skip to content

Commit 11a707b

Browse files
Feature/test file lock (#652)
* hazard.from_raster_xarray: close opened dataset at last * hazard.test_base_xarray: close datasets use setUpClass and tearDownClass for testfiles * CHANGELOG: added `from_raster_xarray` * hazard.Hazard.from_raster_xarray: idiomatic use of `with open` * Have two Hazard classmethods for reading xarray Dataset and file Split the existing classmethod into two methods 'from_xarray_raster' and 'from_xarray_raster_file'. One first loads data from a dataset, the second opens a file and then calls the first. * Split classmethods. * Update unit tests. * Update docstrings. * Improve tests for Hazard.from_xarray_raster(_file) * Use temporary directory for storing data instead of local directory. * Consistently open data files in context managers. * Fix linter issue "no-else-raise" * Update CHANGELOG.md --------- Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
1 parent 8317442 commit 11a707b

3 files changed

Lines changed: 341 additions & 238 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ updated:
2323

2424
### Added
2525

26-
- `climada.engine.impact.Impact` objects have new methods `from_hdf5` and `write_hdf5` for reading their data from, and writing it to, H5 files [#606](https://github.com/CLIMADA-project/climada_python/pull/606).
26+
- `climada.hazard.Hazard.from_xarray_raster(_file)` class methods for reading `Hazard` objects from an `xarray.Dataset`, or from a file that can be read by `xarray`.
27+
[#507](https://github.com/CLIMADA-project/climada_python/pull/507),
28+
[#589](https://github.com/CLIMADA-project/climada_python/pull/589),
29+
[#652](https://github.com/CLIMADA-project/climada_python/pull/652).
30+
- `climada.engine.impact.Impact` objects have new methods `from_hdf5` and `write_hdf5` for reading their data from, and writing it to, H5 files [#606](https://github.com/CLIMADA-project/climada_python/pull/606)
2731
- `climada.engine.impact.Impact` objects has a new class method `concat` for concatenation of impacts based on the same exposures [#529](https://github.com/CLIMADA-project/climada_python/pull/529).
2832
- `climada.engine.impact_calc`: this module was separated from `climada.engine.impact` and contains the code that dealing with impact _calculation_ while the latter focuses on impact _data_ [#560](https://github.com/CLIMADA-project/climada_python/pull/560).
2933
- The classes `Hazard`, `Impact` and `ImpactFreqCurve` have a novel attribute `frequency_unit`. Before it was implicitly set to annual, now it can be specified and accordingly displayed in plots.

climada/hazard/base.py

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,52 @@ def set_vector(self, *args, **kwargs):
410410
self.__dict__ = Hazard.from_vector(*args, **kwargs).__dict__
411411

412412
@classmethod
413-
def from_raster_xarray(
413+
def from_xarray_raster_file(
414+
cls, filepath: Union[pathlib.Path, str], *args, **kwargs
415+
):
416+
"""Read raster-like data from a file that can be loaded with xarray
417+
418+
This wraps :py:meth:`~Hazard.from_xarray_raster` by first opening the target file
419+
as xarray dataset and then passing it to that classmethod. Use this wrapper as a
420+
simple alternative to opening the file yourself. The signature is exactly the
421+
same, except for the first argument, which is replaced by a file path here.
422+
423+
Additional (keyword) arguments are passed to
424+
:py:meth:`~Hazard.from_xarray_raster`.
425+
426+
Parameters
427+
----------
428+
filepath : Path or str
429+
Path of the file to read with xarray. May be any file type supported by
430+
xarray. See https://docs.xarray.dev/en/stable/user-guide/io.html
431+
432+
Returns
433+
-------
434+
hazard : climada.Hazard
435+
A hazard object created from the input data
436+
437+
Examples
438+
--------
439+
440+
>>> hazard = Hazard.from_xarray_raster_file("path/to/file.nc", "", "")
441+
442+
Notes
443+
-----
444+
445+
If you have specific requirements for opening a data file, prefer opening it
446+
yourself and using :py:meth:`~Hazard.from_xarray_raster`, following this pattern:
447+
448+
>>> open_kwargs = dict(engine="h5netcdf", chunks=dict(x=-1, y="auto"))
449+
>>> with xarray.open_dataset("path/to/file.nc", **open_kwargs) as dset:
450+
... hazard = Hazard.from_xarray_raster(dset, "", "")
451+
"""
452+
with xr.open_dataset(filepath, chunks="auto") as dset:
453+
return cls.from_xarray_raster(dset, *args, **kwargs)
454+
455+
@classmethod
456+
def from_xarray_raster(
414457
cls,
415-
data: Union[xr.Dataset, str, pathlib.Path],
458+
data: xr.Dataset,
416459
hazard_type: str,
417460
intensity_unit: str,
418461
*,
@@ -422,7 +465,7 @@ def from_raster_xarray(
422465
crs: str = DEF_CRS,
423466
rechunk: bool = False,
424467
):
425-
"""Read raster-like data from an xarray Dataset or a raster data file
468+
"""Read raster-like data from an xarray Dataset
426469
427470
This method reads data that can be interpreted using three coordinates for event,
428471
latitude, and longitude. The data and the coordinates themselves may be organized
@@ -443,12 +486,13 @@ def from_raster_xarray(
443486
meaning that the object can be used in all CLIMADA operations without throwing
444487
an error due to missing data or faulty data types.
445488
489+
Use :py:meth:`~Hazard.from_xarray_raster_file` to open a file on disk
490+
and load the resulting dataset with this method in one step.
491+
446492
Parameters
447493
----------
448-
data : xarray.Dataset or str
449-
The data to read from. May be an opened dataset or a path to a raster data
450-
file, in which case the file is opened first. Works with any file format
451-
supported by ``xarray``.
494+
data : xarray.Dataset
495+
The dataset to read from.
452496
hazard_type : str
453497
The type identifier of the hazard. Will be stored directly in the hazard
454498
object.
@@ -499,6 +543,11 @@ def from_raster_xarray(
499543
hazard : climada.Hazard
500544
A hazard object created from the input data
501545
546+
See Also
547+
--------
548+
:py:meth:`~Hazard.from_xarray_raster_file`
549+
Use this method if you want CLIMADA to open and read a file on disk for you.
550+
502551
Notes
503552
-----
504553
* Single-valued coordinates given by ``coordinate_vars``, that are not proper
@@ -534,7 +583,7 @@ def from_raster_xarray(
534583
... longitude=[0, 1, 2],
535584
... ),
536585
... )
537-
>>> hazard = Hazard.from_raster_xarray(dset, "", "")
586+
>>> hazard = Hazard.from_xarray_raster(dset, "", "")
538587
539588
For non-default coordinate names, use the ``coordinate_vars`` argument.
540589
@@ -551,7 +600,7 @@ def from_raster_xarray(
551600
... longitude=[0, 1, 2],
552601
... ),
553602
... )
554-
>>> hazard = Hazard.from_raster_xarray(
603+
>>> hazard = Hazard.from_xarray_raster(
555604
... dset, "", "", coordinate_vars=dict(event="day", latitude="lat")
556605
... )
557606
@@ -568,7 +617,7 @@ def from_raster_xarray(
568617
... latitude=(["y", "x"], [[0.0, 0.0, 0.0], [0.1, 0.1, 0.1]]),
569618
... ),
570619
... )
571-
>>> hazard = Hazard.from_raster_xarray(dset, "", "")
620+
>>> hazard = Hazard.from_xarray_raster(dset, "", "")
572621
573622
Optional data is read from the dataset if the default keys are found. Users can
574623
specify custom variables in the data, or that the default keys should be ignored,
@@ -593,7 +642,7 @@ def from_raster_xarray(
593642
... longitude=[0, 1, 2],
594643
... ),
595644
... )
596-
>>> hazard = Hazard.from_raster_xarray(
645+
>>> hazard = Hazard.from_xarray_raster(
597646
... dset,
598647
... "",
599648
... "",
@@ -627,7 +676,7 @@ def from_raster_xarray(
627676
... longitude=[0, 1, 2],
628677
... ),
629678
... )
630-
>>> hazard = Hazard.from_raster_xarray(dset, "", "") # Same as first example
679+
>>> hazard = Hazard.from_xarray_raster(dset, "", "") # Same as first example
631680
632681
If one coordinate is missing altogehter, you must add it or expand the dimensions
633682
before loading the dataset:
@@ -645,14 +694,15 @@ def from_raster_xarray(
645694
... ),
646695
... )
647696
>>> dset = dset.expand_dims(time=[numpy.datetime64("2000-01-01")])
648-
>>> hazard = Hazard.from_raster_xarray(dset, "", "")
697+
>>> hazard = Hazard.from_xarray_raster(dset, "", "")
649698
"""
650-
# If the data is a string, open the respective file
699+
# Check data type for better error message
651700
if not isinstance(data, xr.Dataset):
652-
LOGGER.info("Loading Hazard from file: %s", data)
653-
data: xr.Dataset = xr.open_dataset(data, chunks="auto")
654-
else:
655-
LOGGER.info("Loading Hazard from xarray Dataset")
701+
if isinstance(data, (pathlib.Path, str)):
702+
raise TypeError("Passing a path to this classmethod is not supported. "
703+
"Use Hazard.from_xarray_raster_file instead.")
704+
705+
raise TypeError("This method only supports xarray.Dataset as input data")
656706

657707
# Initialize Hazard object
658708
hazard_kwargs = dict(haz_type=hazard_type, units=intensity_unit)

0 commit comments

Comments
 (0)