HURDAT2 vs IBTrACS — source comparison¶
The same basin and season pulled from both data sources tropycal serves — HURDAT2 (NHC post-season reanalysis) and IBTrACS (multi-agency global archive) — compared on fix counts and peak intensity. The source is selected via the backend's source= kwarg, and the result carries a source column.
What this notebook does¶
- Download the North Atlantic 2017 best-track fixes from each source (HURDAT2 and IBTrACS);
tropycalis a vector backend, sodownload()writes a file and returns aFeatureCollectionper source. - Compare the two archives on fix counts, storm counts, and peak wind.
- Plot the per-storm peak intensity from each source side by side.
The live query downloads and parses a whole basin best-track file (the first IBTrACS load is slow); the rest of the notebook is guarded so it skips cleanly if the query fails.
Setup¶
Consolidate the imports up front: EarthLens is the unified entry point and matplotlib renders the final comparison plot. We also pick an output directory for the files tropycal writes.
from pathlib import Path
import matplotlib.pyplot as plt
from earthlens import EarthLens
OUT_DIR = Path('tropycal_output')
OUT_DIR.mkdir(exist_ok=True)
1 · Download both sources¶
We loop over the two source keys and download the same request from each. The construct and download() steps are kept on separate lines so the request is easy to read, and the whole block is wrapped in a graceful-skip guard so a failed live query leaves by_source empty instead of raising.
by_source = {}
try:
for src in ('hurdat', 'ibtracs'):
query = EarthLens(
variables=['north_atlantic'],
data_source='tropycal',
start='2017-08-01',
end='2017-10-31',
aoi=[-100.0, 5.0, -15.0, 45.0],
source=src,
path=str(OUT_DIR),
)
by_source[src] = query.download(progress_bar=False)
print({s: len(fc) for s, fc in by_source.items()}, 'fixes')
except Exception as exc:
by_source = {}
print(f'skipped live query: {type(exc).__name__}: {exc}')
2 · Compare the archives¶
For each source we print the number of fixes, the number of distinct storms, and the peak max wind. This makes any divergence between the two best-track archives immediately visible.
if by_source:
for src, fc in by_source.items():
print(
f"{src:8s}: {len(fc):4d} fixes, {fc['name'].nunique():2d} storms, "
f"peak {fc['vmax_kt'].max():.0f} kt"
)
3 · Peak intensity by source¶
Rank each storm by its peak wind and plot the two sources together. Overlapping curves mean the archives agree on intensity; gaps highlight storms one source rates higher than the other.
if by_source:
fig, ax = plt.subplots(figsize=(9, 5))
for src, fc in by_source.items():
peak = fc.groupby('name')['vmax_kt'].max().sort_values(ascending=False)
ax.plot(range(len(peak)), peak.values, 'o-', label=src)
ax.set_xlabel('Storm rank (by peak wind)')
ax.set_ylabel('Peak max wind (kt)')
ax.set_title('North Atlantic 2017 - peak intensity by source')
ax.legend()
plt.tight_layout()
plt.show()