Skip to content

Commit

Permalink
Fix hardcoded text elements in the plots of the forecast class (#769)
Browse files Browse the repository at this point in the history
* fix hardcoded text elements
* format forecast module
* rework tests of forecast module
* Update CHANGELOG.md

---------

Co-authored-by: Thomas Roosli <thomas.roeoesli@meteoswiss.ch>
Co-authored-by: Lukas Riedel <34276446+peanutfun@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 17, 2023
1 parent 438be81 commit 015fdbf
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Removed:
- `util.lines_polys_handler` solve polygon disaggregation issue in metre-based projection [#666](https://github.com/CLIMADA-project/climada_python/pull/666)
- Problem with `pyproj.CRS` as `Impact` attribute, [#706](https://github.com/CLIMADA-project/climada_python/issues/706). Now CRS is always stored as `str` in WKT format.
- Correctly handle assertion errors in `Centroids.values_from_vector_files` and fix the associated test [#768](https://github.com/CLIMADA-project/climada_python/pull/768/)
- Text in `Forecast` class plots can now be adjusted [#769](https://github.com/CLIMADA-project/climada_python/issues/769)

### Deprecated

Expand Down
43 changes: 29 additions & 14 deletions climada/engine/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ def calc(self, force_reassign=False):
def plot_imp_map(
self,
run_datetime=None,
explain_str=None,
save_fig=True,
close_fig=False,
polygon_file=None,
Expand All @@ -321,13 +322,17 @@ def plot_imp_map(
figsize=(9, 13),
adapt_fontsize=True,
):
"""plot a map of the impacts
""" plot a map of the impacts
Parameters
----------
run_datetime : datetime.datetime, optional
Select the used hazard by the run_datetime,
default is first element of attribute run_datetime.
explain_str : str, optional
Short str which explains type of impact, explain_str is included
in the title of the figure.
default is 'mean building damage caused by wind'
save_fig : bool, optional
Figure is saved if True, folder is within your configurable
save_dir and filename is derived from the method summary_str()
Expand Down Expand Up @@ -372,7 +377,7 @@ def plot_imp_map(
"run_start": (
run_datetime.strftime("%d.%m.%Y %HUTC +") + lead_time_str + "d"
),
"explain_text": ("mean building damage caused by wind"),
"explain_text": "mean building damage caused by wind" if explain_str is None else explain_str,
"model_text": "CLIMADA IMPACT",
}
fig, axes = self._plot_imp_map(
Expand Down Expand Up @@ -526,15 +531,24 @@ def _plot_imp_map(
return fig, axis_sub

def plot_hist(
self, run_datetime=None, save_fig=True, close_fig=False, figsize=(9, 8)
self,
run_datetime=None,
explain_str=None,
save_fig=True,
close_fig=False,
figsize=(9, 8),
):
"""plot histogram of the forecasted impacts all ensemble members
""" plot histogram of the forecasted impacts all ensemble members
Parameters
----------
run_datetime : datetime.datetime, optional
Select the used hazard by the run_datetime,
default is first element of attribute run_datetime.
explain_str : str, optional
Short str which explains type of impact, explain_str is included
in the title of the figure.
default is 'total building damage'
save_fig : bool, optional
Figure is saved if True, folder is within your configurable
save_dir and filename is derived from the method summary_str()
Expand Down Expand Up @@ -603,7 +617,7 @@ def plot_hist(
axes.xaxis.set_ticks(x_ticks)
axes.xaxis.set_ticklabels(x_ticklabels)
plt.xticks(rotation=15, horizontalalignment="right")
plt.xlim([(10**-0.25) * bins[0], (10**0.25) * bins[-1]])
plt.xlim([(10 ** -0.25) * bins[0], (10 ** 0.25) * bins[-1]])

lead_time_str = "{:.0f}".format(
self.lead_time(run_datetime).days
Expand All @@ -614,7 +628,7 @@ def plot_hist(
"run_start": (
run_datetime.strftime("%d.%m.%Y %HUTC +") + lead_time_str + "d"
),
"explain_text": ("total building damage"),
"explain_text": ("total building damage") if explain_str is None else explain_str,
"model_text": "CLIMADA IMPACT",
}
title_position = {
Expand Down Expand Up @@ -656,8 +670,9 @@ def plot_hist(
plt.text(
0.75,
0.85,
"mean damage:\nCHF "
+ self._number_to_str(self._impact[haz_ind].at_event.mean()),
"mean impact:\n "
+ self._number_to_str(self._impact[haz_ind].at_event.mean())
+ ' ' + self._impact[haz_ind].unit,
horizontalalignment="center",
verticalalignment="center",
transform=axes.transAxes,
Expand Down Expand Up @@ -740,7 +755,7 @@ def plot_exceedence_prob(
The default is (9, 13)
adapt_fontsize : bool, optional
If set to true, the size of the fonts will be adapted to the size of the figure.
Otherwise the default matplotlib font size is used. Default is True.
Otherwise, the default matplotlib font size is used. Default is True.
Returns
-------
Expand All @@ -750,10 +765,10 @@ def plot_exceedence_prob(
if run_datetime is None:
run_datetime = self.run_datetime[0]
haz_ind = np.argwhere(np.isin(self.run_datetime, run_datetime))[0][0]
wind_map_file_name = (
exceedence_map_file_name = (
self.summary_str(run_datetime) + "_exceed_" + str(threshold) + "_map.jpeg"
)
wind_map_file_name_full = FORECAST_PLOT_DIR / wind_map_file_name
exceedence_map_file_name_full = FORECAST_PLOT_DIR / exceedence_map_file_name
lead_time_str = "{:.0f}".format(
self.lead_time(run_datetime).days
+ self.lead_time(run_datetime).seconds / 60 / 60 / 24
Expand Down Expand Up @@ -783,7 +798,7 @@ def plot_exceedence_prob(
adapt_fontsize=adapt_fontsize,
)
if save_fig:
plt.savefig(wind_map_file_name_full)
plt.savefig(exceedence_map_file_name_full)
if close_fig:
plt.clf()
plt.close(fig)
Expand Down Expand Up @@ -974,7 +989,7 @@ def plot_warn_map(
Figure is not drawn if True. The default is False.
adapt_fontsize : bool, optional
If set to true, the size of the fonts will be adapted to the size of the figure.
Otherwise the default matplotlib font size is used. Default is True.
Otherwise, the default matplotlib font size is used. Default is True.
Returns
-------
Expand Down Expand Up @@ -1086,7 +1101,7 @@ def _plot_warn(
decision_dict_functions[aggregation] = np.mean
else:
raise ValueError(
"Parameter area_aggregation of "
"Parameter " + aggregation + " of "
+ "Forecast.plot_warn_map() must eiter be "
+ "a float between [0..1], which "
+ "specifys a quantile. or 'sum' or 'mean'."
Expand Down
61 changes: 47 additions & 14 deletions climada/engine/test/test_forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ class TestPlot(unittest.TestCase):

def test_Forecast_plot(self):
"""Test cplotting functions from the Forecast class"""
#hazard
## given a forecast based on hazard exposure and vulnerability
#hazard
haz1 = StormEurope.from_cosmoe_file(
HAZ_DIR.joinpath('storm_europe_cosmoe_forecast_vmax_testfile.nc'),
run_datetime=dt.datetime(2018,1,1),
Expand Down Expand Up @@ -149,21 +150,41 @@ def test_Forecast_plot(self):
for f in source:
if f['properties']['adm0_a3'] == 'CHE':
sink.write(f)
#test plotting functions
## test plotting functions
# should save plot without failing
forecast.plot_imp_map(run_datetime=dt.datetime(2017,12,31),
explain_str='test text',
polygon_file=str(cantons_file),
save_fig=True, close_fig=True)
map_file_name = (forecast.summary_str(dt.datetime(2017,12,31)) +
'_impact_map' +
'.jpeg')
map_file_name_full = Path(FORECAST_PLOT_DIR) / map_file_name
map_file_name_full.absolute().unlink(missing_ok=False)
forecast.plot_hist(run_datetime=dt.datetime(2017,12,31),
save_fig=False, close_fig=True)
forecast.plot_exceedence_prob(run_datetime=dt.datetime(2017,12,31),
threshold=5000, save_fig=False, close_fig=True)


#should contain title strings
ax = forecast.plot_hist(run_datetime=dt.datetime(2017,12,31),
explain_str='test text',
save_fig=False, close_fig=False)
title_artists = ax.get_figure().get_children()
title_texts = [x.get_text() for x in title_artists if isinstance(x, plt.Text)]
self.assertIn('test text', title_texts)
self.assertIn('Wed 03 Jan 2018 00-24UTC', title_texts)
self.assertIn('31.12.2017 00UTC +3d', title_texts)
#should contain average impact in axes
artists = ax.get_children()
texts = [x.get_text() for x in artists if type(x) == plt.Text]
self.assertIn('mean impact:\n 26 USD', texts)
ax.get_figure().clf()
#should contain title strings
ax = forecast.plot_exceedence_prob(run_datetime=dt.datetime(2017,12,31),
threshold=5000, explain_str='test text exceedence',
save_fig=False, close_fig=False)[0][0]
title_artists = ax.get_figure().get_children()
title_texts = [x.get_text() for x in title_artists if isinstance(x, plt.Text)]
self.assertIn('test text exceedence', title_texts)
self.assertIn('Wed 03 Jan 2018 00-24UTC', title_texts)
self.assertIn('31.12.2017 00UTC +3d', title_texts)
ax.get_figure().clf()
forecast.plot_warn_map(str(cantons_file),
decision_level = 'polygon',
thresholds=[100000,500000,
Expand All @@ -187,36 +208,48 @@ def test_Forecast_plot(self):
close_fig=True)
forecast.plot_hexbin_ei_exposure()
plt.close()
with self.assertRaises(ValueError):
# should fail because of invalid decision_level
with self.assertRaises(ValueError) as cm:
forecast.plot_warn_map(str(cantons_file),
decision_level = 'test_fail',
decision_level='test_fail',
probability_aggregation=0.2,
area_aggregation=0.2,
title="Building damage warning",
explain_text="warn level based on aggregated damages",
save_fig=False,
close_fig=True)
plt.close()
with self.assertRaises(ValueError):
self.assertIn(
"Parameter decision_level", str(cm.exception)
)
# should fail because of invalid probability_aggregation
with self.assertRaises(ValueError) as cm:
forecast.plot_warn_map(str(cantons_file),
decision_level = 'exposure_point',
decision_level='exposure_point',
probability_aggregation='test_fail',
area_aggregation=0.2,
title="Building damage warning",
explain_text="warn level based on aggregated damages",
save_fig=False,
close_fig=True)
plt.close()
with self.assertRaises(ValueError):
self.assertIn(
"Parameter probability_aggregation", str(cm.exception)
)
# should fail because of invalid area_aggregation
with self.assertRaises(ValueError) as cm:
forecast.plot_warn_map(str(cantons_file),
decision_level = 'exposure_point',
decision_level='exposure_point',
probability_aggregation=0.2,
area_aggregation='test_fail',
title="Building damage warning",
explain_text="warn level based on aggregated damages",
save_fig=False,
close_fig=True)
plt.close()
self.assertIn(
"Parameter area_aggregation", str(cm.exception)
)


# Execute Tests
Expand Down

0 comments on commit 015fdbf

Please sign in to comment.