@@ -286,6 +286,11 @@ class Basin_bounds_storm(Enum):
286286 )
287287
288288
289+ BASINS_GDF = gpd .GeoDataFrame (
290+ {"basin" : b , "geometry" : b .value } for b in Basin_bounds_storm
291+ )
292+
293+
289294class TCTracks :
290295 """Contains tropical cyclone tracks.
291296
@@ -415,79 +420,58 @@ def subset(self, filterdict):
415420
416421 return out
417422
418- BASINS_GDF = gpd .GeoDataFrame (
419- {"basin" : b , "geometry" : b .value } for b in Basin_bounds_storm
420- )
423+ def get_basins (track ):
421424
422- def get_basins (
423- track ,
424- ): # this is the method I had in mind for 1. and I'd guess it could be a performance boost
425425 track_coordinates = gpd .GeoDataFrame (
426426 geometry = gpd .points_from_xy (track .lon , track .lat )
427427 )
428428 return track_coordinates .sjoin (BASINS_GDF , how = "left" , predicate = "within" ).basin
429429
430- def subset_by_basin (self ):
430+ def subset_by_basin (self , origin : bool = False ):
431431 """Subset all tropical cyclones tracks by basin.
432432
433- This function iterates through the tropical cyclones in the dataset and assigns each cyclone
434- to a basin based on its geographical location. It checks whether the cyclone's position
435- (latitude and longitude) lies within the boundaries of any of the predefined basins and
436- then groups the cyclones into separate categories for each basin. The resulting dictionary
437- maps each basin's name to a list of tropical cyclones that fall within it.
433+ This function collects for every basin the tracks that crossed them. The resulting dictionary
434+ maps each basin's name to a list of tropical cyclones tracks that intersected them.
438435
439436 Parameters
440437 ----------
441438 self : TCTtracks object
442- The object instance containing the tropical cyclone data (`self.data`) to be processed.
439+ The object instance containing the tropical cyclone data (`self.data`).
440+ origin : bool
441+ Either True or False. If True, the outputs basin will contain only the tracks that originated there.
442+ If False, every track that crossed a basin will be present in the basin.
443443
444444 Returns
445445 -------
446446 dict_tc_basins : dict
447447 A dictionary where the keys are basin names (e.g., "NA", "EP", "WP", etc.) and the
448- values are instances of the `TCTracks` class containing the tropical cyclones that
449- belong to each basin.
450-
451- Example:
452- --------
453- >>> tc = TCTracks.from_ibtracks("")
454- >>> tc_basins = tc.subset_by_basin()
455- >>> tc_basins["NA"] # to access tracks in the North Atlantic
448+ values are instances of the `TCTracks` class: effectively all tracks that intersected or originated
449+ in each basin, depending on the argument "origin".
450+ tracks_outside_basin : list
451+ A list of all tracks that did not cross any basin.
456452
457453 """
458454
459- # Initialize a defaultdict to store lists for each basin
460- basins_dict = defaultdict (list )
455+ basins_dict : dict = defaultdict (list )
461456 tracks_outside_basin : list = []
462- # Iterate over each tropical cyclone
463- for track in self .data :
464- lat , lon = track .lat .values [0 ], track .lon .values [0 ]
465- origin_point = Point (lon , lat )
466- point_in_basin = False
467-
468- # Find the basin that contains the point
469- for basin in Basin_bounds_storm :
470- if basin .value .contains (origin_point ):
471- basins_dict [basin .name ].append (track )
472- point_in_basin = True
473- break
474-
475- if not point_in_basin :
476- tracks_outside_basin .append (track .id_no )
477-
478- if tracks_outside_basin :
479- warnings .warn (
480- f"A total of { len (tracks_outside_basin )} tracks did not originate in any of the \n "
481- f"defined basins. IDs of the tracks outside the basins: { tracks_outside_basin } " ,
482- UserWarning ,
483- )
484457
485- # Create a dictionary with TCTracks for each basin
486- dict_tc_basins = {
487- basin_name : TCTracks (tc_list ) for basin_name , tc_list in basins_dict .items ()
488- }
489-
490- return dict_tc_basins
458+ for track in self .data :
459+ # if only origin basin is of interest (origin = True)
460+ if origin :
461+ origin_basin = TCTracks .get_basins (track )[0 ]
462+ if origin_basin :
463+ basins_dict [origin_basin .name ].append (track )
464+ else :
465+ tracks_outside_basin .append (track )
466+ else : # if every basin crossed is of interest (origin = False)
467+ touched = TCTracks .get_basins (track ).dropna ().drop_duplicates ()
468+ if touched .size :
469+ for basin in touched :
470+ basins_dict [basin .name ].append (track )
471+ else :
472+ tracks_outside_basin .append (track )
473+
474+ return basins_dict , tracks_outside_basin
491475
492476 def subset_year (
493477 self ,
0 commit comments