2121
2222__all__ = ['ImpactFreqCurve' , 'Impact' ]
2323
24+ import ast
2425import logging
2526import csv
2627from itertools import zip_longest
3031import xlsxwriter
3132
3233from climada .entity .tag import Tag
34+ from climada .entity .exposures .base import Exposures , DEF_CRS
3335from climada .hazard .tag import Tag as TagHaz
3436from climada .entity .exposures .base import INDICATOR_IF , INDICATOR_CENTR
3537import climada .util .plot as u_plot
@@ -65,6 +67,7 @@ def __init__(self):
6567 self .event_name = list ()
6668 self .date = np .array ([], int )
6769 self .coord_exp = np .ndarray ([], float )
70+ self .crs = DEF_CRS
6871 self .eai_exp = np .array ([])
6972 self .at_event = np .array ([])
7073 self .frequency = np .array ([])
@@ -157,6 +160,7 @@ def calc(self, exposures, impact_funcs, hazard, save_mat=False):
157160 self .eai_exp = np .zeros (exposures .value .size )
158161 self .tag = {'exp' : exposures .tag , 'if_set' : impact_funcs .tag ,
159162 'haz' : hazard .tag }
163+ self .crs = exposures .crs
160164
161165 # Select exposures with positive value and assigned centroid
162166 exp_idx = np .where (np .logical_and (exposures .value > 0 , \
@@ -215,10 +219,10 @@ def calc(self, exposures, impact_funcs, hazard, save_mat=False):
215219 if save_mat :
216220 self .imp_mat = self .imp_mat .tocsr ()
217221
218- def plot_eai_exposure (self , mask = None , ignore_zero = True ,
219- pop_name = True , buffer = 0.0 , extend = 'neither' ,
220- var_name = None , ** kwargs ):
221- """Plot expected annual impact of each exposure.
222+ def plot_hexbin_eai_exposure (self , mask = None , ignore_zero = True ,
223+ pop_name = True , buffer = 0.0 , extend = 'neither' ,
224+ var_name = None , ** kwargs ):
225+ """Plot hexbin expected annual impact of each exposure.
222226
223227 Parameters:
224228 mask (np.array, optional): mask to apply to eai_exp plotted.
@@ -235,61 +239,90 @@ def plot_eai_exposure(self, mask=None, ignore_zero=True,
235239 Returns:
236240 matplotlib.figure.Figure, cartopy.mpl.geoaxes.GeoAxesSubplot
237241 """
238- title = 'Expected annual impact'
239- if var_name is None :
240- var_name = 'Impact (' + self .unit + ')'
241- if mask is None :
242- mask = np .ones ((self .eai_exp .size ,), dtype = bool )
243- if ignore_zero :
244- pos_vals = self .eai_exp [mask ] > 0
245- else :
246- pos_vals = np .ones ((self .eai_exp [mask ].size ,), dtype = bool )
247- if 'reduce_C_function' not in kwargs :
248- kwargs ['reduce_C_function' ] = np .sum
249- return u_plot .geo_bin_from_array (self .eai_exp [mask ][pos_vals ], \
250- self .coord_exp [mask ][pos_vals ], var_name , title , pop_name , \
251- buffer , extend , ** kwargs )
242+ eai_exp = self ._build_exp ()
243+ fig , ax = eai_exp .plot_hexbin (mask , ignore_zero , pop_name , buffer ,
244+ extend , ** kwargs )
245+ ax [0 , 0 ].set_title ('Expected annual impact' )
246+ return fig , ax
252247
253- def _exp_impact (self , exp_iimp , exposures , hazard , imp_fun , insure_flag ):
254- """Compute impact for inpute exposure indexes and impact function.
248+ def plot_scatter_eai_exposure (self , mask = None , ignore_zero = True ,
249+ pop_name = True , buffer = 0.0 , extend = 'neither' ,
250+ var_name = None , ** kwargs ):
251+ """Plot scatter expected annual impact of each exposure.
255252
256253 Parameters:
257- exp_iimp (np.array): exposures indexes
258- exposures (Exposures): exposures instance
259- hazard (Hazard): hazard instance
260- imp_fun (ImpactFunc): impact function instance
261- insure_flag (bool): consider deductible and cover of exposures
254+ mask (np.array, optional): mask to apply to eai_exp plotted.
255+ ignore_zero (bool, optional): flag to indicate if zero and negative
256+ values are ignored in plot. Default: False
257+ pop_name (bool, optional): add names of the populated places
258+ buffer (float, optional): border to add to coordinates.
259+ Default: 1.0.
260+ extend (str, optional): extend border colorbar with arrows.
261+ [ 'neither' | 'both' | 'min' | 'max' ]
262+ var_name (str, optional): Colorbar label
263+ kwargs (optional): arguments for hexbin matplotlib function
264+
265+ Returns:
266+ matplotlib.figure.Figure, cartopy.mpl.geoaxes.GeoAxesSubplot
262267 """
263- if not exp_iimp .size :
264- return
268+ eai_exp = self ._build_exp ()
269+ fig , ax = eai_exp .plot_scatter (mask , ignore_zero , pop_name , buffer ,
270+ extend , ** kwargs )
271+ ax [0 , 0 ].set_title ('Expected annual impact' )
272+ return fig , ax
265273
266- # get assigned centroids
267- icens = exposures [INDICATOR_CENTR + hazard .tag .haz_type ].values [exp_iimp ]
274+ def plot_raster_eai_exposure (self , res = None , raster_res = None , save_tiff = None ,
275+ raster_f = lambda x : np .log10 ((np .fmax (x + 1 , 1 ))),
276+ label = 'value (log10)' , ** kwargs ):
277+ """Plot raster expected annual impact of each exposure.
268278
269- # get affected intensities
270- inten_val = hazard .intensity [:, icens ]
271- # get affected fractions
272- fract = hazard .fraction [:, icens ]
273- # impact = fraction * mdr * value
274- inten_val .data = imp_fun .calc_mdr (inten_val .data )
275- impact = fract .multiply (inten_val ).multiply (exposures .value .values [exp_iimp ])
279+ Parameters:
280+ res (float, optional): resolution of current data in units of latitude
281+ and longitude, approximated if not provided.
282+ raster_res (float, optional): desired resolution of the raster
283+ save_tiff (str, optional): file name to save the raster in tiff
284+ format, if provided
285+ raster_f (lambda function): transformation to use to data. Default:
286+ log10 adding 1.
287+ label (str): colorbar label
288+ kwargs (optional): arguments for imshow matplotlib function
276289
277- if insure_flag and impact .nonzero ()[0 ].size :
278- inten_val = hazard .intensity [:, icens ].todense ()
279- paa = np .interp (inten_val , imp_fun .intensity , imp_fun .paa )
280- impact = np .minimum (np .maximum (impact - \
281- exposures .deductible .values [exp_iimp ] * paa , 0 ), \
282- exposures .cover .values [exp_iimp ])
283- self .eai_exp [exp_iimp ] += np .sum (np .asarray (impact ) * \
284- hazard .frequency .reshape (- 1 , 1 ), axis = 0 )
285- else :
286- self .eai_exp [exp_iimp ] += np .squeeze (np .asarray (np .sum ( \
287- impact .multiply (hazard .frequency .reshape (- 1 , 1 )), axis = 0 )))
290+ Returns:
291+ matplotlib.figure.Figure, cartopy.mpl.geoaxes.GeoAxesSubplot
292+ """
293+ eai_exp = self ._build_exp ()
294+ fig , ax = eai_exp .plot_raster (res , raster_res , save_tiff , raster_f ,
295+ label , ** kwargs )
296+ ax [0 , 0 ].set_title ('Expected annual impact' )
297+ return fig , ax
298+
299+ def plot_basemap_eai_exposure (self , mask = None , ignore_zero = False , pop_name = True ,
300+ buffer = 0.0 , extend = 'neither' , zoom = 10 ,
301+ url = 'http://tile.stamen.com/terrain/tileZ/tileX/tileY.png' ,
302+ ** kwargs ):
303+ """Plot basemap expected annual impact of each exposure.
288304
289- self .at_event += np .squeeze (np .asarray (np .sum (impact , axis = 1 )))
290- self .tot_value += np .sum (exposures .value .values [exp_iimp ])
291- if not isinstance (self .imp_mat , list ):
292- self .imp_mat [:, exp_iimp ] = impact
305+ Parameters:
306+ mask (np.array, optional): mask to apply to eai_exp plotted.
307+ ignore_zero (bool, optional): flag to indicate if zero and negative
308+ values are ignored in plot. Default: False
309+ pop_name (bool, optional): add names of the populated places
310+ buffer (float, optional): border to add to coordinates. Default: 0.0.
311+ extend (str, optional): extend border colorbar with arrows.
312+ [ 'neither' | 'both' | 'min' | 'max' ]
313+ zoom (int, optional): zoom coefficient used in the satellite image
314+ url (str, optional): image source, e.g. ctx.sources.OSM_C
315+ kwargs (optional): arguments for scatter matplotlib function, e.g.
316+ cmap='Greys'. Default: 'Wistia'
317+
318+ Returns:
319+ matplotlib.figure.Figure, cartopy.mpl.geoaxes.GeoAxesSubplot
320+ """
321+ eai_exp = self ._build_exp ()
322+ fig , ax = eai_exp .plot_basemap (mask , ignore_zero , pop_name , buffer ,
323+ extend , zoom , url , ** kwargs )
324+ ax [0 , 0 ].set_title ('Expected annual impact' )
325+ return fig , ax
293326
294327 def write_csv (self , file_name ):
295328 """ Write data into csv file. imp_mat is not saved.
@@ -302,15 +335,16 @@ def write_csv(self, file_name):
302335 imp_wr .writerow (["tag_hazard" , "tag_exposure" , "tag_impact_func" ,
303336 "unit" , "tot_value" , "aai_agg" , "event_id" ,
304337 "event_name" , "event_date" , "event_frequency" ,
305- "at_event" , "eai_exp" , "exp_lat" , "exp_lon" ])
338+ "at_event" , "eai_exp" , "exp_lat" , "exp_lon" , "exp_crs" ])
306339 csv_data = [[[self .tag ['haz' ].haz_type ], [self .tag ['haz' ].file_name ],
307340 [self .tag ['haz' ].description ]],
308341 [[self .tag ['exp' ].file_name ], [self .tag ['exp' ].description ]],
309342 [[self .tag ['if_set' ].file_name ], [self .tag ['if_set' ].description ]],
310343 [self .unit ], [self .tot_value ], [self .aai_agg ],
311344 self .event_id , self .event_name , self .date ,
312345 self .frequency , self .at_event ,
313- self .eai_exp , self .coord_exp [:, 0 ], self .coord_exp [:, 1 ]]
346+ self .eai_exp , self .coord_exp [:, 0 ], self .coord_exp [:, 1 ],
347+ [str (self .crs )]]
314348 for values in zip_longest (* csv_data ):
315349 imp_wr .writerow (values )
316350
@@ -333,7 +367,7 @@ def write_col(i_col, imp_ws, xls_data):
333367 header = ["tag_hazard" , "tag_exposure" , "tag_impact_func" ,
334368 "unit" , "tot_value" , "aai_agg" , "event_id" ,
335369 "event_name" , "event_date" , "event_frequency" ,
336- "at_event" , "eai_exp" , "exp_lat" , "exp_lon" ]
370+ "at_event" , "eai_exp" , "exp_lat" , "exp_lon" , "exp_crs" ]
337371 for icol , head_dat in enumerate (header ):
338372 imp_ws .write (0 , icol , head_dat )
339373 data = [self .tag ['haz' ].haz_type , str (self .tag ['haz' ].file_name ),
@@ -354,6 +388,7 @@ def write_col(i_col, imp_ws, xls_data):
354388 write_col (11 , imp_ws , self .eai_exp )
355389 write_col (12 , imp_ws , self .coord_exp [:, 0 ])
356390 write_col (13 , imp_ws , self .coord_exp [:, 1 ])
391+ write_col (14 , imp_ws , [str (self .crs )])
357392
358393 imp_wb .close ()
359394
@@ -398,6 +433,7 @@ def read_csv(self, file_name):
398433 self .coord_exp = np .zeros ((num_exp , 2 ))
399434 self .coord_exp [:, 0 ] = imp_df .exp_lat [:num_exp ]
400435 self .coord_exp [:, 1 ] = imp_df .exp_lon [:num_exp ]
436+ self .crs = ast .literal_eval (imp_df .exp_crs .values [0 ])
401437 self .tag ['haz' ] = TagHaz (str (imp_df .tag_hazard [0 ]),
402438 str (imp_df .tag_hazard [1 ]),
403439 str (imp_df .tag_hazard [2 ]))
@@ -439,6 +475,58 @@ def read_excel(self, file_name):
439475 self .coord_exp = np .zeros ((self .eai_exp .size , 2 ))
440476 self .coord_exp [:, 0 ] = dfr .exp_lat .values [:self .eai_exp .size ]
441477 self .coord_exp [:, 1 ] = dfr .exp_lon .values [:self .eai_exp .size ]
478+ self .crs = ast .literal_eval (dfr .exp_crs .values [0 ])
479+
480+ def _exp_impact (self , exp_iimp , exposures , hazard , imp_fun , insure_flag ):
481+ """Compute impact for inpute exposure indexes and impact function.
482+
483+ Parameters:
484+ exp_iimp (np.array): exposures indexes
485+ exposures (Exposures): exposures instance
486+ hazard (Hazard): hazard instance
487+ imp_fun (ImpactFunc): impact function instance
488+ insure_flag (bool): consider deductible and cover of exposures
489+ """
490+ if not exp_iimp .size :
491+ return
492+
493+ # get assigned centroids
494+ icens = exposures [INDICATOR_CENTR + hazard .tag .haz_type ].values [exp_iimp ]
495+
496+ # get affected intensities
497+ inten_val = hazard .intensity [:, icens ]
498+ # get affected fractions
499+ fract = hazard .fraction [:, icens ]
500+ # impact = fraction * mdr * value
501+ inten_val .data = imp_fun .calc_mdr (inten_val .data )
502+ impact = fract .multiply (inten_val ).multiply (exposures .value .values [exp_iimp ])
503+
504+ if insure_flag and impact .nonzero ()[0 ].size :
505+ inten_val = hazard .intensity [:, icens ].todense ()
506+ paa = np .interp (inten_val , imp_fun .intensity , imp_fun .paa )
507+ impact = np .minimum (np .maximum (impact - \
508+ exposures .deductible .values [exp_iimp ] * paa , 0 ), \
509+ exposures .cover .values [exp_iimp ])
510+ self .eai_exp [exp_iimp ] += np .sum (np .asarray (impact ) * \
511+ hazard .frequency .reshape (- 1 , 1 ), axis = 0 )
512+ else :
513+ self .eai_exp [exp_iimp ] += np .squeeze (np .asarray (np .sum ( \
514+ impact .multiply (hazard .frequency .reshape (- 1 , 1 )), axis = 0 )))
515+
516+ self .at_event += np .squeeze (np .asarray (np .sum (impact , axis = 1 )))
517+ self .tot_value += np .sum (exposures .value .values [exp_iimp ])
518+ if not isinstance (self .imp_mat , list ):
519+ self .imp_mat [:, exp_iimp ] = impact
520+
521+ def _build_exp (self ):
522+ eai_exp = Exposures ()
523+ eai_exp ['value' ] = self .eai_exp
524+ eai_exp ['latitude' ] = self .coord_exp [:, 0 ]
525+ eai_exp ['longitude' ] = self .coord_exp [:, 1 ]
526+ eai_exp .crs = self .crs
527+ eai_exp .value_unit = self .unit
528+ eai_exp .check ()
529+ return eai_exp
442530
443531class ImpactFreqCurve ():
444532 """ Impact exceedence frequency curve.
0 commit comments