77
88import pandas
99import xarray
10+ from pandas import Series
1011from shapely import ops
1112from shapely .geometry import MultiPolygon
1213from shapely .geometry import Polygon
@@ -49,12 +50,15 @@ def __init__(
4950 year : int ,
5051 start_date : datetime = None ,
5152 end_date : datetime = None ,
53+ synthetic : bool = False ,
54+ ** kwargs ,
5255 ):
5356 """
5457 :param name: storm name
5558 :param year: storm year
5659 :param start_date: starting time
5760 :param end_date: ending time
61+ :param synthetic: whether the storm actually exists; `True` will skip lookup in the storm table
5862
5963 >>> StormEvent('florence', 2018)
6064 StormEvent(name='FLORENCE', year=2018, start_date=Timestamp('2018-08-30 06:00:00'), end_date=Timestamp('2018-09-18 12:00:00'))
@@ -72,17 +76,30 @@ def __init__(
7276 StormEvent(name='IDA', year=2021, start_date=Timestamp('2021-08-27 18:00:00'), end_date=Timestamp('2021-08-29 18:00:00'))
7377 """
7478
75- storms = nhc_storms (year = year )
76- storms = storms [storms ["name" ].str .contains (name .upper ())]
77- if len (storms ) > 0 :
78- self .__entry = storms .iloc [0 ]
79+ if not synthetic :
80+ storms = nhc_storms (year = year )
81+ storms = storms [storms ["name" ].str .contains (name .upper ())]
82+ if len (storms ) > 0 :
83+ self .__entry = storms .iloc [0 ]
84+ else :
85+ raise ValueError (f'storm "{ name } { year } " not found in NHC database' )
7986 else :
80- raise ValueError (f'storm "{ name } { year } " not found in NHC database' )
87+ self .__entry = Series (
88+ {
89+ "name" : name ,
90+ "year" : year ,
91+ "start_date" : start_date ,
92+ "end_date" : end_date ,
93+ ** kwargs ,
94+ },
95+ index = None ,
96+ )
8197
8298 self .__usgs_id = None
8399 self .__is_usgs_flood_event = True
84100 self .__high_water_marks = None
85101 self .__previous_configuration = {"name" : self .name , "year" : self .year }
102+ self .__synthetic = synthetic
86103
87104 self .start_date = start_date
88105 self .end_date = end_date
@@ -268,6 +285,10 @@ def status(self) -> StormStatus:
268285 else :
269286 return StormStatus .HISTORICAL
270287
288+ @property
289+ def synthetic (self ) -> bool :
290+ return self .__synthetic
291+
271292 def track (
272293 self ,
273294 start_date : datetime = None ,
@@ -514,3 +535,49 @@ def __repr__(self) -> str:
514535 f"end_date={ repr (self .end_date )} "
515536 f")"
516537 )
538+
539+ def __copy__ (self ) -> "StormEvent" :
540+ return self .__class__ (
541+ self .name ,
542+ year = self .year ,
543+ start_date = self .start_date ,
544+ end_date = self .end_date ,
545+ )
546+
547+ def perturb (
548+ self ,
549+ name : str = None ,
550+ year : int = None ,
551+ start_date : datetime = None ,
552+ end_date : datetime = None ,
553+ ** kwargs ,
554+ ) -> "StormEvent" :
555+ """
556+ :param name: storm name
557+ :param year: storm year
558+ :param start_date: starting time
559+ :param end_date: ending time
560+ :return: a new synthetic storm based on parameters from the current storm
561+ """
562+
563+ if name is None :
564+ name = self .name
565+ if year is None :
566+ year = self .year
567+ if start_date is None :
568+ start_date = self .start_date
569+ elif isinstance (start_date , timedelta ):
570+ start_date = self .start_date + start_date
571+ if end_date is None :
572+ end_date = self .end_date
573+ elif isinstance (end_date , timedelta ):
574+ end_date = self .end_date + end_date
575+
576+ return self .__class__ (
577+ name = name ,
578+ year = year ,
579+ start_date = start_date ,
580+ end_date = end_date ,
581+ synthetic = True ,
582+ ** kwargs ,
583+ )
0 commit comments