Skip to content

Commit 015fdbf

Browse files
ThomasRoosliThomas Rooslipeanutfun
authored
Fix hardcoded text elements in the plots of the forecast class (#769)
* fix hardcoded text elements * format forecast module * rework tests of forecast module * Update CHANGELOG.md --------- Co-authored-by: Thomas Roosli <[email protected]> Co-authored-by: Lukas Riedel <[email protected]>
1 parent 438be81 commit 015fdbf

File tree

3 files changed

+77
-28
lines changed

3 files changed

+77
-28
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ Removed:
6363
- `util.lines_polys_handler` solve polygon disaggregation issue in metre-based projection [#666](https://github.com/CLIMADA-project/climada_python/pull/666)
6464
- 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.
6565
- 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/)
66+
- Text in `Forecast` class plots can now be adjusted [#769](https://github.com/CLIMADA-project/climada_python/issues/769)
6667

6768
### Deprecated
6869

climada/engine/forecast.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ def calc(self, force_reassign=False):
313313
def plot_imp_map(
314314
self,
315315
run_datetime=None,
316+
explain_str=None,
316317
save_fig=True,
317318
close_fig=False,
318319
polygon_file=None,
@@ -321,13 +322,17 @@ def plot_imp_map(
321322
figsize=(9, 13),
322323
adapt_fontsize=True,
323324
):
324-
"""plot a map of the impacts
325+
""" plot a map of the impacts
325326
326327
Parameters
327328
----------
328329
run_datetime : datetime.datetime, optional
329330
Select the used hazard by the run_datetime,
330331
default is first element of attribute run_datetime.
332+
explain_str : str, optional
333+
Short str which explains type of impact, explain_str is included
334+
in the title of the figure.
335+
default is 'mean building damage caused by wind'
331336
save_fig : bool, optional
332337
Figure is saved if True, folder is within your configurable
333338
save_dir and filename is derived from the method summary_str()
@@ -372,7 +377,7 @@ def plot_imp_map(
372377
"run_start": (
373378
run_datetime.strftime("%d.%m.%Y %HUTC +") + lead_time_str + "d"
374379
),
375-
"explain_text": ("mean building damage caused by wind"),
380+
"explain_text": "mean building damage caused by wind" if explain_str is None else explain_str,
376381
"model_text": "CLIMADA IMPACT",
377382
}
378383
fig, axes = self._plot_imp_map(
@@ -526,15 +531,24 @@ def _plot_imp_map(
526531
return fig, axis_sub
527532

528533
def plot_hist(
529-
self, run_datetime=None, save_fig=True, close_fig=False, figsize=(9, 8)
534+
self,
535+
run_datetime=None,
536+
explain_str=None,
537+
save_fig=True,
538+
close_fig=False,
539+
figsize=(9, 8),
530540
):
531-
"""plot histogram of the forecasted impacts all ensemble members
541+
""" plot histogram of the forecasted impacts all ensemble members
532542
533543
Parameters
534544
----------
535545
run_datetime : datetime.datetime, optional
536546
Select the used hazard by the run_datetime,
537547
default is first element of attribute run_datetime.
548+
explain_str : str, optional
549+
Short str which explains type of impact, explain_str is included
550+
in the title of the figure.
551+
default is 'total building damage'
538552
save_fig : bool, optional
539553
Figure is saved if True, folder is within your configurable
540554
save_dir and filename is derived from the method summary_str()
@@ -603,7 +617,7 @@ def plot_hist(
603617
axes.xaxis.set_ticks(x_ticks)
604618
axes.xaxis.set_ticklabels(x_ticklabels)
605619
plt.xticks(rotation=15, horizontalalignment="right")
606-
plt.xlim([(10**-0.25) * bins[0], (10**0.25) * bins[-1]])
620+
plt.xlim([(10 ** -0.25) * bins[0], (10 ** 0.25) * bins[-1]])
607621

608622
lead_time_str = "{:.0f}".format(
609623
self.lead_time(run_datetime).days
@@ -614,7 +628,7 @@ def plot_hist(
614628
"run_start": (
615629
run_datetime.strftime("%d.%m.%Y %HUTC +") + lead_time_str + "d"
616630
),
617-
"explain_text": ("total building damage"),
631+
"explain_text": ("total building damage") if explain_str is None else explain_str,
618632
"model_text": "CLIMADA IMPACT",
619633
}
620634
title_position = {
@@ -656,8 +670,9 @@ def plot_hist(
656670
plt.text(
657671
0.75,
658672
0.85,
659-
"mean damage:\nCHF "
660-
+ self._number_to_str(self._impact[haz_ind].at_event.mean()),
673+
"mean impact:\n "
674+
+ self._number_to_str(self._impact[haz_ind].at_event.mean())
675+
+ ' ' + self._impact[haz_ind].unit,
661676
horizontalalignment="center",
662677
verticalalignment="center",
663678
transform=axes.transAxes,
@@ -740,7 +755,7 @@ def plot_exceedence_prob(
740755
The default is (9, 13)
741756
adapt_fontsize : bool, optional
742757
If set to true, the size of the fonts will be adapted to the size of the figure.
743-
Otherwise the default matplotlib font size is used. Default is True.
758+
Otherwise, the default matplotlib font size is used. Default is True.
744759
745760
Returns
746761
-------
@@ -750,10 +765,10 @@ def plot_exceedence_prob(
750765
if run_datetime is None:
751766
run_datetime = self.run_datetime[0]
752767
haz_ind = np.argwhere(np.isin(self.run_datetime, run_datetime))[0][0]
753-
wind_map_file_name = (
768+
exceedence_map_file_name = (
754769
self.summary_str(run_datetime) + "_exceed_" + str(threshold) + "_map.jpeg"
755770
)
756-
wind_map_file_name_full = FORECAST_PLOT_DIR / wind_map_file_name
771+
exceedence_map_file_name_full = FORECAST_PLOT_DIR / exceedence_map_file_name
757772
lead_time_str = "{:.0f}".format(
758773
self.lead_time(run_datetime).days
759774
+ self.lead_time(run_datetime).seconds / 60 / 60 / 24
@@ -783,7 +798,7 @@ def plot_exceedence_prob(
783798
adapt_fontsize=adapt_fontsize,
784799
)
785800
if save_fig:
786-
plt.savefig(wind_map_file_name_full)
801+
plt.savefig(exceedence_map_file_name_full)
787802
if close_fig:
788803
plt.clf()
789804
plt.close(fig)
@@ -974,7 +989,7 @@ def plot_warn_map(
974989
Figure is not drawn if True. The default is False.
975990
adapt_fontsize : bool, optional
976991
If set to true, the size of the fonts will be adapted to the size of the figure.
977-
Otherwise the default matplotlib font size is used. Default is True.
992+
Otherwise, the default matplotlib font size is used. Default is True.
978993
979994
Returns
980995
-------
@@ -1086,7 +1101,7 @@ def _plot_warn(
10861101
decision_dict_functions[aggregation] = np.mean
10871102
else:
10881103
raise ValueError(
1089-
"Parameter area_aggregation of "
1104+
"Parameter " + aggregation + " of "
10901105
+ "Forecast.plot_warn_map() must eiter be "
10911106
+ "a float between [0..1], which "
10921107
+ "specifys a quantile. or 'sum' or 'mean'."

climada/engine/test/test_forecast.py

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ class TestPlot(unittest.TestCase):
104104

105105
def test_Forecast_plot(self):
106106
"""Test cplotting functions from the Forecast class"""
107-
#hazard
107+
## given a forecast based on hazard exposure and vulnerability
108+
#hazard
108109
haz1 = StormEurope.from_cosmoe_file(
109110
HAZ_DIR.joinpath('storm_europe_cosmoe_forecast_vmax_testfile.nc'),
110111
run_datetime=dt.datetime(2018,1,1),
@@ -149,21 +150,41 @@ def test_Forecast_plot(self):
149150
for f in source:
150151
if f['properties']['adm0_a3'] == 'CHE':
151152
sink.write(f)
152-
#test plotting functions
153+
## test plotting functions
154+
# should save plot without failing
153155
forecast.plot_imp_map(run_datetime=dt.datetime(2017,12,31),
156+
explain_str='test text',
154157
polygon_file=str(cantons_file),
155158
save_fig=True, close_fig=True)
156159
map_file_name = (forecast.summary_str(dt.datetime(2017,12,31)) +
157160
'_impact_map' +
158161
'.jpeg')
159162
map_file_name_full = Path(FORECAST_PLOT_DIR) / map_file_name
160163
map_file_name_full.absolute().unlink(missing_ok=False)
161-
forecast.plot_hist(run_datetime=dt.datetime(2017,12,31),
162-
save_fig=False, close_fig=True)
163-
forecast.plot_exceedence_prob(run_datetime=dt.datetime(2017,12,31),
164-
threshold=5000, save_fig=False, close_fig=True)
165-
166-
164+
#should contain title strings
165+
ax = forecast.plot_hist(run_datetime=dt.datetime(2017,12,31),
166+
explain_str='test text',
167+
save_fig=False, close_fig=False)
168+
title_artists = ax.get_figure().get_children()
169+
title_texts = [x.get_text() for x in title_artists if isinstance(x, plt.Text)]
170+
self.assertIn('test text', title_texts)
171+
self.assertIn('Wed 03 Jan 2018 00-24UTC', title_texts)
172+
self.assertIn('31.12.2017 00UTC +3d', title_texts)
173+
#should contain average impact in axes
174+
artists = ax.get_children()
175+
texts = [x.get_text() for x in artists if type(x) == plt.Text]
176+
self.assertIn('mean impact:\n 26 USD', texts)
177+
ax.get_figure().clf()
178+
#should contain title strings
179+
ax = forecast.plot_exceedence_prob(run_datetime=dt.datetime(2017,12,31),
180+
threshold=5000, explain_str='test text exceedence',
181+
save_fig=False, close_fig=False)[0][0]
182+
title_artists = ax.get_figure().get_children()
183+
title_texts = [x.get_text() for x in title_artists if isinstance(x, plt.Text)]
184+
self.assertIn('test text exceedence', title_texts)
185+
self.assertIn('Wed 03 Jan 2018 00-24UTC', title_texts)
186+
self.assertIn('31.12.2017 00UTC +3d', title_texts)
187+
ax.get_figure().clf()
167188
forecast.plot_warn_map(str(cantons_file),
168189
decision_level = 'polygon',
169190
thresholds=[100000,500000,
@@ -187,36 +208,48 @@ def test_Forecast_plot(self):
187208
close_fig=True)
188209
forecast.plot_hexbin_ei_exposure()
189210
plt.close()
190-
with self.assertRaises(ValueError):
211+
# should fail because of invalid decision_level
212+
with self.assertRaises(ValueError) as cm:
191213
forecast.plot_warn_map(str(cantons_file),
192-
decision_level = 'test_fail',
214+
decision_level='test_fail',
193215
probability_aggregation=0.2,
194216
area_aggregation=0.2,
195217
title="Building damage warning",
196218
explain_text="warn level based on aggregated damages",
197219
save_fig=False,
198220
close_fig=True)
199221
plt.close()
200-
with self.assertRaises(ValueError):
222+
self.assertIn(
223+
"Parameter decision_level", str(cm.exception)
224+
)
225+
# should fail because of invalid probability_aggregation
226+
with self.assertRaises(ValueError) as cm:
201227
forecast.plot_warn_map(str(cantons_file),
202-
decision_level = 'exposure_point',
228+
decision_level='exposure_point',
203229
probability_aggregation='test_fail',
204230
area_aggregation=0.2,
205231
title="Building damage warning",
206232
explain_text="warn level based on aggregated damages",
207233
save_fig=False,
208234
close_fig=True)
209235
plt.close()
210-
with self.assertRaises(ValueError):
236+
self.assertIn(
237+
"Parameter probability_aggregation", str(cm.exception)
238+
)
239+
# should fail because of invalid area_aggregation
240+
with self.assertRaises(ValueError) as cm:
211241
forecast.plot_warn_map(str(cantons_file),
212-
decision_level = 'exposure_point',
242+
decision_level='exposure_point',
213243
probability_aggregation=0.2,
214244
area_aggregation='test_fail',
215245
title="Building damage warning",
216246
explain_text="warn level based on aggregated damages",
217247
save_fig=False,
218248
close_fig=True)
219249
plt.close()
250+
self.assertIn(
251+
"Parameter area_aggregation", str(cm.exception)
252+
)
220253

221254

222255
# Execute Tests

0 commit comments

Comments
 (0)