Multi-basin season — ACE by basin¶
Request two basins (North Atlantic + East Pacific) in one call and compare their storm counts and Accumulated Cyclone Energy (ACE) for the 2018 season. This is a track-mode query: one LineString per storm, carrying a per-storm ace and a basin column. tropycal is a vector backend, so download() returns the union of storms across both basins as a GeoDataFrame.
Setup¶
Consolidate the imports up front: Path for the output directory, EarthLens as the unified entry point, and matplotlib for the plots further down.
from pathlib import Path
import matplotlib.pyplot as plt
from earthlens import EarthLens
Output directory¶
tropycal's download() writes one file per basin, so give it a folder to write into.
OUT_DIR = Path('tropycal_output')
OUT_DIR.mkdir(exist_ok=True)
Request both basins¶
Build the request first — both basins as variables, the 2018 season window, a bounding box spanning the North Atlantic and East Pacific, and geometry='track' for one LineString per storm. Keeping construction on its own line makes the request easy to read and re-run.
query = EarthLens(
variables=['north_atlantic', 'east_pacific'],
data_source='tropycal',
start='2018-05-01',
end='2018-12-01',
aoi=[-160.0, 0.0, -10.0, 60.0],
source='hurdat',
geometry='track',
path=str(OUT_DIR),
)
Download the tracks¶
download() fetches and parses each basin's best-track file (the first load can be slow) and returns the union as a GeoDataFrame. The whole call is wrapped in a graceful-skip guard so the notebook still renders if the live query is unavailable.
tracks = None
try:
tracks = query.download(progress_bar=False)
print(len(tracks), 'storm tracks across', tracks['basin'].nunique(), 'basins')
except Exception as exc:
print(f'skipped live query: {type(exc).__name__}: {exc}')
Storm counts and total ACE¶
Group the storms by basin to compare how many distinct storms each basin saw and their summed Accumulated Cyclone Energy.
if tracks is not None and len(tracks):
summary = tracks.groupby('basin').agg(
storms=('name', 'nunique'), total_ace=('ace', 'sum')
)
print(summary)
ACE by basin¶
A horizontal bar chart of summed ACE makes the contrast between the two basins immediate.
if tracks is not None and len(tracks):
fig, ax = plt.subplots(figsize=(6, 5))
tracks.groupby('basin')['ace'].sum().sort_values().plot.barh(ax=ax, color='teal')
ax.set_xlabel('Total ACE')
ax.set_title('2018 ACE by basin')
plt.tight_layout()
plt.show()
Storm tracks map¶
Each storm's LineString plotted in geographic coordinates, coloured by basin, gives the spatial picture behind the ACE totals.
if tracks is not None and len(tracks):
fig, ax = plt.subplots(figsize=(8, 5))
for basin, grp in tracks.groupby('basin'):
grp.plot(ax=ax, linewidth=1.2, label=basin)
ax.set_title('2018 storm tracks (NA + E Pacific)')
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.legend()
plt.tight_layout()
plt.show()