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

fix hardcoded text elements in the plots of the forecast class #769

Merged
merged 8 commits into from
Aug 17, 2023
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
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