Skip to content

Commit

Permalink
Merge branch 'develop' into feature/climada_osmflex
Browse files Browse the repository at this point in the history
  • Loading branch information
luseverin authored Jan 13, 2025
2 parents d254d81 + 0bcbc61 commit 2615def
Show file tree
Hide file tree
Showing 20 changed files with 439 additions and 202 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,6 @@ jobs:
needs: build-and-test
with:
core_branch: ${{ github.ref }}
petals_branch: feature/exposures_crs
petals_branch: develop
permissions:
checks: write
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ Code freeze date: YYYY-MM-DD

- Add `osm-flex` package to CLIMADA core [#981](https://github.com/CLIMADA-project/climada_python/pull/981)
- `doc.tutorial.climada_entity_Exposures_osm.ipynb` tutorial explaining how to use `osm-flex`with CLIMADA
- `climada.util.coordinates.bounding_box_global` function [#980](https://github.com/CLIMADA-project/climada_python/pull/980)
- `climada.util.coordinates.bounding_box_from_countries` function [#980](https://github.com/CLIMADA-project/climada_python/pull/980)
- `climada.util.coordinates.bounding_box_from_cardinal_bounds` function [#980](https://github.com/CLIMADA-project/climada_python/pull/980)
- `climada.engine.impact.Impact.local_return_period` method [#971](https://github.com/CLIMADA-project/climada_python/pull/971)
- `doc.tutorial.climada_util_local_exceedance_values.ipynb` tutorial explaining `Hazard.local_exceedance_intensity`, `Hazard.local_return_period`, `Impact.local_exceedance_impact`, and `Impact.local_return_period` methods [#971](https://github.com/CLIMADA-project/climada_python/pull/971)
- `Hazard.local_exceedance_intensity`, `Hazard.local_return_period` and `Impact.local_exceedance_impact`, that all use the `climada.util.interpolation` module [#918](https://github.com/CLIMADA-project/climada_python/pull/918)
Expand All @@ -30,6 +33,8 @@ Code freeze date: YYYY-MM-DD

### Changed

- `Centroids.append` now takes multiple arguments and provides a performance boost when doing so [#989](https://github.com/CLIMADA-project/climada_python/pull/989)
- `climada.util.coordinates.get_country_geometries` function: Now throwing a ValueError if unregognized ISO country code is given (before, the invalid ISO code was ignored) [#980](https://github.com/CLIMADA-project/climada_python/pull/980)
- Improved scaling factors implemented in `climada.hazard.trop_cyclone.apply_climate_scenario_knu` to model the impact of climate changes to tropical cyclones [#734](https://github.com/CLIMADA-project/climada_python/pull/734)
- In `climada.util.plot.geo_im_from_array`, NaNs are plotted in gray while cells with no centroid are not plotted [#929](https://github.com/CLIMADA-project/climada_python/pull/929)
- Renamed `climada.util.plot.subplots_from_gdf` to `climada.util.plot.plot_from_gdf` [#929](https://github.com/CLIMADA-project/climada_python/pull/929)
Expand All @@ -39,9 +44,13 @@ Code freeze date: YYYY-MM-DD
- latitude and longitude column are no longer present there (the according arrays can be retrieved as properties of the Exposures object: `exp.latitude` instead of `exp.gdf.latitude.values`).
- `Exposures.gdf` has been renamed to `Exposures.data` (it still works though, as it is a property now pointing to the latter)
- the `check` method does not add a default "IMPF_" column to the GeoDataFrame anymore
- Updated IBTrACS version from v4.0 to v4.1 ([#976](https://github.com/CLIMADA-project/climada_python/pull/976)
- Fix xarray future warning in TCTracks for .dims to .sizes
- Fix hazard.concatenate type test for pathos pools

### Fixed

- Resolved an issue where windspeed computation was much slower than in Climada v3 [#989](https://github.com/CLIMADA-project/climada_python/pull/989)
- File handles are being closed after reading netcdf files with `climada.hazard` modules [#953](https://github.com/CLIMADA-project/climada_python/pull/953)
- Avoids a ValueError in the impact calculation for cases with a single exposure point and MDR values of 0, by explicitly removing zeros in `climada.hazard.Hazard.get_mdr` [#933](https://github.com/CLIMADA-project/climada_python/pull/948)

Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ install_test : ## Test installation was successful

.PHONY : data_test
data_test : ## Test data APIs
python script/jenkins/test_data_api.py
pytest $(PYTEST_JUNIT_ARGS) script/jenkins/test_data_api.py

.PHONY : notebook_test
notebook_test : ## Test notebooks in doc/tutorial
python script/jenkins/test_notebooks.py report
pytest $(PYTEST_JUNIT_ARGS) script/jenkins/test_notebooks.py

.PHONY : integ_test
integ_test : ## Integration tests execution with xml reports
Expand Down
4 changes: 2 additions & 2 deletions climada/engine/impact.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ def __init__(
crs=DEF_CRS,
eai_exp=None,
at_event=None,
tot_value=0,
aai_agg=0,
tot_value=0.,
aai_agg=0.,
unit="",
imp_mat=None,
haz_type="",
Expand Down
32 changes: 30 additions & 2 deletions climada/entity/exposures/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,40 @@ def gdf(self):
@property
def latitude(self):
"""Latitude array of exposures"""
return self.data.geometry.y.values
try:
return self.data.geometry.y.values
except ValueError as valerr:
nonpoints = list(
self.data[
self.data.geometry.type != "Point"
].geometry.type.drop_duplicates()
)
if nonpoints:
raise ValueError(
"Can only calculate latitude from Points."
f" GeoDataFrame contains {', '.join(nonpoints)}."
" Please see the lines_polygons module tutorial."
) from valerr
raise

Check warning on line 149 in climada/entity/exposures/base.py

View check run for this annotation

Jenkins - WCR / Code Coverage

Not covered line

Line 149 is not covered by tests

@property
def longitude(self):
"""Longitude array of exposures"""
return self.data.geometry.x.values
try:
return self.data.geometry.x.values
except ValueError as valerr:
nonpoints = list(
self.data[
self.data.geometry.type != "Point"
].geometry.type.drop_duplicates()
)
if nonpoints:
raise ValueError(
"Can only calculate longitude from Points."
f" GeoDataFrame contains {', '.join(nonpoints)}."
" Please see the lines_polygons module tutorial."
) from valerr
raise

Check warning on line 168 in climada/entity/exposures/base.py

View check run for this annotation

Jenkins - WCR / Code Coverage

Not covered line

Line 168 is not covered by tests

@property
def geometry(self):
Expand Down
35 changes: 34 additions & 1 deletion climada/entity/exposures/test/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import rasterio
import scipy as sp
from rasterio.windows import Window
from shapely.geometry import Point
from shapely.geometry import MultiPolygon, Point, Polygon
from sklearn.metrics import DistanceMetric

import climada.util.coordinates as u_coord
Expand Down Expand Up @@ -652,6 +652,39 @@ def test_to_crs_epsg_crs(self):
Exposures.to_crs(self, crs="GCS", epsg=26915)
self.assertEqual("one of crs or epsg must be None", str(cm.exception))

def test_latlon_with_polygons(self):
"""Check for proper error message if the data frame contains non-Point shapes"""
poly = Polygon(
[(10.0, 0.0), (10.0, 1.0), (11.0, 1.0), (11.0, 0.0), (10.0, 0.0)]
)
point = Point((1, -1))
multi = MultiPolygon(
[
(
((0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 0.0)),
[((0.1, 1.1), (0.1, 1.2), (0.2, 1.2), (0.2, 1.1))],
)
]
)
poly = Polygon()
exp = Exposures(geometry=[poly, point, multi, poly])
with self.assertRaises(ValueError) as valer:
exp.latitude
self.assertEqual(
"Can only calculate latitude from Points."
" GeoDataFrame contains Polygon, MultiPolygon."
" Please see the lines_polygons module tutorial.",
str(valer.exception),
)
with self.assertRaises(ValueError) as valer:
exp.longitude
self.assertEqual(
"Can only calculate longitude from Points."
" GeoDataFrame contains Polygon, MultiPolygon."
" Please see the lines_polygons module tutorial.",
str(valer.exception),
)


class TestImpactFunctions(unittest.TestCase):
"""Test impact function handling"""
Expand Down
3 changes: 1 addition & 2 deletions climada/hazard/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -936,8 +936,7 @@ def append(self, *others):
"The hazards are incompatible and cannot be concatenated."
)
self.haz_type = haz_types.pop()

haz_classes = {type(haz) for haz in haz_list}
haz_classes = {haz.__class__.__name__ for haz in haz_list}
if len(haz_classes) > 1:
raise TypeError(
f"The given hazards are of different classes: {haz_classes}. "
Expand Down
34 changes: 21 additions & 13 deletions climada/hazard/centroids/centr.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,11 +331,16 @@ def from_pnt_bounds(cls, points_bounds, res, crs=DEF_CRS):
}
)

def append(self, centr):
"""Append Centroids
def append(self, *centr):
"""Append Centroids to the current centroid object for concatenation.
This method checks that all centroids use the same CRS, appends the list of centroids to
the initial Centroid object and eventually concatenates them to create a single centroid
object with the union of all centroids.
Note that the result might contain duplicate points if the object to append has an overlap
with the current object.
with the current object. Remove duplicates by either using :py:meth:`union`
or calling :py:meth:`remove_duplicate_points` after appending.
Parameters
----------
Expand All @@ -351,22 +356,25 @@ def append(self, centr):
union : Union of Centroid objects.
remove_duplicate_points : Remove duplicate points in a Centroids object.
"""
if not u_coord.equal_crs(self.crs, centr.crs):
raise ValueError(
f"The given centroids use different CRS: {self.crs}, {centr.crs}. "
"The centroids are incompatible and cannot be concatenated."
)
self.gdf = pd.concat([self.gdf, centr.gdf])
for other in centr:
if not u_coord.equal_crs(self.crs, other.crs):
raise ValueError(
f"The given centroids use different CRS: {self.crs}, {other.crs}. "
"The centroids are incompatible and cannot be concatenated."
)
self.gdf = pd.concat([self.gdf] + [other.gdf for other in centr])

def union(self, *others):
"""Create the union of Centroids objects
"""Create the union of the current Centroids object with one or more other centroids
objects by passing the list of centroids to :py:meth:`append` for concatenation and then
removes duplicates.
All centroids must have the same CRS. Points that are contained in more than one of the
Centroids objects will only be contained once (i.e. duplicates are removed).
Parameters
----------
others : list of Centroids
others : Centroids
Centroids contributing to the union.
Returns
Expand All @@ -375,8 +383,8 @@ def union(self, *others):
Centroids object containing the union of all Centroids.
"""
centroids = copy.deepcopy(self)
for cent in others:
centroids.append(cent)
centroids.append(*others)

return centroids.remove_duplicate_points()

def remove_duplicate_points(self):
Expand Down
14 changes: 14 additions & 0 deletions climada/hazard/centroids/test/test_centr.py
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,20 @@ def test_append_dif_crs(self):
with self.assertRaises(ValueError):
self.centr.append(centr2)

def test_append_multiple_arguments(self):
"""Test passing append() multiple arguments in the form of a list of Centroids."""
# create a single centroid
lat, lon = np.array([1, 2]), np.array([1, 2])
centr = Centroids(lat=lat, lon=lon)
# create a list of centroids
coords = [(np.array([3, 4]), np.array([3, 4]))]
centroids_list = [Centroids(lat=lat, lon=lon) for lat, lon in coords]

centr.append(*centroids_list)

np.testing.assert_array_equal(centr.lat, [1, 2, 3, 4])
np.testing.assert_array_equal(centr.lon, [1, 2, 3, 4])

def test_remove_duplicate_pass(self):
"""Test remove_duplicate_points"""
centr = Centroids(
Expand Down
14 changes: 7 additions & 7 deletions climada/hazard/tc_tracks.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@
IBTRACS_URL = (
"https://www.ncei.noaa.gov/data/"
"international-best-track-archive-for-climate-stewardship-ibtracs/"
"v04r00/access/netcdf"
"v04r01/access/netcdf"
)
"""Site of IBTrACS netcdf file containing all tracks v4.0,
s. https://www.ncdc.noaa.gov/ibtracs/index.php?name=ib-v4-access"""

IBTRACS_FILE = "IBTrACS.ALL.v04r00.nc"
"""IBTrACS v4.0 file all"""
IBTRACS_FILE = "IBTrACS.ALL.v04r01.nc"
"""IBTrACS v4.1 file all"""

IBTRACS_AGENCIES = [
"usa",
Expand Down Expand Up @@ -376,7 +376,7 @@ def from_ibtracs_netcdf(
correct_pres=False,
discard_single_points=True,
additional_variables=None,
file_name="IBTrACS.ALL.v04r00.nc",
file_name=IBTRACS_FILE,
):
"""Create new TCTracks object from IBTrACS databse.
Expand Down Expand Up @@ -485,7 +485,7 @@ def from_ibtracs_netcdf(
compatiblity with other functions such as `equal_timesteps`. Default: True.
file_name : str, optional
Name of NetCDF file to be dowloaded or located at climada/data/system.
Default: 'IBTrACS.ALL.v04r00.nc'
Default: 'IBTrACS.ALL.v04r01.nc'
additional_variables : list of str, optional
If specified, additional IBTrACS data variables are extracted, such as "nature" or
"storm_speed". Only variables that are not agency-specific are supported.
Expand Down Expand Up @@ -731,7 +731,7 @@ def from_ibtracs_netcdf(
)
ibtracs_ds = ibtracs_ds.sel(storm=valid_storms_mask)

if ibtracs_ds.dims["storm"] == 0:
if ibtracs_ds.sizes["storm"] == 0:
LOGGER.info(
"After discarding IBTrACS events without valid values by the selected "
"reporting agencies, there are no tracks left that match the specified "
Expand Down Expand Up @@ -2576,7 +2576,7 @@ def ibtracs_fit_param(explained, explanatory, year_range=(1980, 2019), order=1):
raise KeyError("Unknown ibtracs variable: %s" % var)

# load ibtracs dataset
fn_nc = SYSTEM_DIR.joinpath("IBTrACS.ALL.v04r00.nc")
fn_nc = SYSTEM_DIR.joinpath(IBTRACS_FILE)

Check warning on line 2579 in climada/hazard/tc_tracks.py

View check run for this annotation

Jenkins - WCR / Code Coverage

Not covered line

Line 2579 is not covered by tests
with xr.open_dataset(fn_nc) as ibtracs_ds:
# choose specified year range
years = ibtracs_ds.sid.str.slice(0, 4).astype(int)
Expand Down
6 changes: 3 additions & 3 deletions climada/hazard/test/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@


def download_ibtracs():
"""This makes sure a IBTrACS.ALL.v04r00.nc file is present in SYSTEM_DIR
"""This makes sure a IBTrACS.ALL.v04r01.nc file is present in SYSTEM_DIR
First, downloading from the original sources is attempted. If that fails an old version
is downloaded from the CLIMADA Data API
"""
Expand All @@ -44,9 +44,9 @@ def download_ibtracs():
): # plan b: download an old version of that file from the climada api
client = Client()
dsinfo = client.get_dataset_info(
name="IBTrACS", version="v04r00", status="external"
name="IBTrACS", version="v04r01", status="external"
)
[fileinfo] = [
fi for fi in dsinfo.files if fi.file_name == "IBTrACS.ALL.v04r00.nc"
fi for fi in dsinfo.files if fi.file_name == "IBTrACS.ALL.v04r01.nc"
]
client._download_file(local_path=SYSTEM_DIR, fileinfo=fileinfo)
Loading

0 comments on commit 2615def

Please sign in to comment.