diff --git a/adcircpy/driver.py b/adcircpy/driver.py index 16a1c419..e77129dd 100644 --- a/adcircpy/driver.py +++ b/adcircpy/driver.py @@ -853,6 +853,7 @@ def _start_date(self, start_date): if start_date is None: if isinstance(self.mesh.forcings.wind, BestTrackForcing): start_date = self.mesh.forcings.wind.start_date + self.mesh.forcings.tides.start_date = self.mesh.forcings.wind.start_date else: if isinstance(self.mesh.forcings.tides, Tides): self.mesh.forcings.tides.start_date = start_date @@ -872,6 +873,7 @@ def _end_date(self, end_date): if end_date is None: if isinstance(self.wind_forcing, BestTrackForcing): end_date = self.wind_forcing.end_date + self.mesh.forcings.tides.end_date = self.mesh.forcings.wind.end_date else: if isinstance(self.mesh.forcings.tides, Tides): self.mesh.forcings.tides.end_date = end_date diff --git a/adcircpy/forcing/winds/best_track.py b/adcircpy/forcing/winds/best_track.py index a47b1a1b..a012c220 100644 --- a/adcircpy/forcing/winds/best_track.py +++ b/adcircpy/forcing/winds/best_track.py @@ -26,11 +26,29 @@ from shapely import ops from shapely.geometry import Point, Polygon + from adcircpy.forcing.winds.base import WindForcing logger = logging.getLogger(__name__) +def _dist(points1, points2) -> np.ndarray: + R = 6373.0 # radius of earth + lons1, lats1 = np.hsplit(points1, 2) + lons2, lats2 = np.hsplit(points2, 2) + lats1 = np.radians(lats1) + lons1 = np.radians(lons1) + lats2 = np.radians(lats2) + lons2 = np.radians(lons2) + + dlons = lons2 - lons1 + dlats = lats2 - lats1 + + a = np.sin(dlats / 2) ** 2 + np.cos(lats1) * np.cos(lats2) * np.sin(dlons / 2) ** 2 + c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a)) + return R * c + + def _fetch_and_plot_coastline(_ax, show, filename='BestTrack.png'): save_dir = pathlib.Path(appdirs.user_data_dir('ne_coastline')) save_dir.mkdir(exist_ok=True, parents=True) @@ -94,6 +112,34 @@ def __init__( super().__init__(nws=nws, interval_seconds=interval_seconds) + def summary(self, output: Union[str, os.PathLike] = None, overwrite: bool = False): + min_storm_speed = np.min(self.speed) + max_storm_speed = np.max(self.speed) + track_length = self.track_length + duration = self.duration + min_central_pressure = np.min(self.central_pressure) + max_wind_speed = np.max(self.df['max_sustained_wind_speed']) + start_loc = (self.df['longitude'][0], self.df['latitude'][0]) + end_loc = (self.df['longitude'].iloc[-1], self.df['latitude'].iloc[-1]) + f = [ + f'Summary of storm: {self.storm_id}', + f'min./max. track speed: {min_storm_speed} m/s, {max_storm_speed} m/s', + f'min. central pressure: {min_central_pressure} hPa', + f'max. wind speed: {max_wind_speed} kts', + f'Starting at: {start_loc} and ended at: {end_loc}', + f'Total track length: {track_length:.2f} km', + f'Total track duration: {duration:.2f} days', + ] + summary = '\n'.join(f) + if output is not None: + if not isinstance(output, pathlib.Path): + path = pathlib.Path(output) + if path.exists() and overwrite is False: + raise Exception('File exist, set overwrite=True to allow overwrite.') + with open(path, 'w+') as fh: + fh.write(summary) + return summary + def __str__(self): record_number = self._generate_record_numbers() fort22 = [] @@ -280,6 +326,16 @@ def make_request(): self.__atcf = io.BytesIO(response.read()) + @property + def track_length(self) -> float: + if not hasattr(self, '_track_length'): + lons, lats = self.df['longitude'], self.df['latitude'] + prev = np.asarray([lons.iloc[:-1], lats.iloc[:-1]]).T + curr = np.asarray([lons.iloc[1:], lats.iloc[1:]]).T + distances = _dist(prev, curr) + self._track_length = np.sum(distances) + return self._track_length + @property def start_date(self) -> datetime: return self._start_date @@ -336,6 +392,11 @@ def _end_date(self, end_date: datetime): assert end_date > self.start_date, msg self.__end_date = end_date + @property + def duration(self) -> float: + d = (self.__end_date - self.__start_date).days + return d + @property def name(self) -> str: return self.df['name'].value_counts()[:].index.tolist()[0] @@ -356,6 +417,10 @@ def year(self) -> int: def datetime(self): return self.df['datetime'] + @property + def central_pressure(self): + return self.df['central_pressure'] + @property def speed(self): return self.df['speed']