fig = go.Figure()
# A-deck 12Z
fig.add_trace(go.Scattermap(
lat=adeck_12z["lat"], lon=adeck_12z["lon"],
mode="lines+markers",
line={"width": 2, "color": "#3388ff"},
marker={
"size": [14 if lt == 0 else 6 for lt in adeck_12z["leadtime"]],
"color": "#3388ff",
},
name="A-deck 12Z (analysis+forecast)",
hoverinfo="text",
text=[
f"A-deck 12Z<br>lt={int(lt)}h valid={vt}<br>"
f"{la:.1f}N {abs(lo):.1f}W {ws:.0f}kt"
for lt, vt, la, lo, ws in zip(
adeck_12z["leadtime"], adeck_12z["valid_time"],
adeck_12z["lat"], adeck_12z["lon"],
adeck_12z["wind_speed"],
)
],
))
# A-deck 18Z
fig.add_trace(go.Scattermap(
lat=adeck_18z["lat"], lon=adeck_18z["lon"],
mode="lines+markers",
line={"width": 2, "color": "#2a9d8f"},
marker={
"size": [14 if lt == 0 else 6 for lt in adeck_18z["leadtime"]],
"color": "#2a9d8f",
},
name="A-deck 18Z (new cycle)",
hoverinfo="text",
text=[
f"A-deck 18Z<br>lt={int(lt)}h valid={vt}<br>"
f"{la:.1f}N {abs(lo):.1f}W {ws:.0f}kt"
for lt, vt, la, lo, ws in zip(
adeck_18z["leadtime"], adeck_18z["valid_time"],
adeck_18z["lat"], adeck_18z["lon"],
adeck_18z["wind_speed"],
)
],
))
# TCM Advisory 17 (15Z): t=0 from observed track + forecast
tcm_t0_lat = float(tcm_t0.iloc[0]["latitude"])
tcm_t0_lon = float(tcm_t0.iloc[0]["longitude"])
tcm_lats = [tcm_t0_lat] + list(tcm_adv17["latitude"].astype(float))
tcm_lons = [tcm_t0_lon] + list(tcm_adv17["longitude"].astype(float))
tcm_labels = [
f"TCM Adv 17 t=0<br>15Z Oct 25<br>"
f"{tcm_t0_lat:.1f}N {abs(tcm_t0_lon):.1f}W"
] + [
f"TCM Adv 17 forecast<br>valid={vt}<br>"
f"{la}N {abs(lo)}W {ws}kt"
for vt, la, lo, ws in zip(
tcm_adv17["validTime"], tcm_adv17["latitude"],
tcm_adv17["longitude"], tcm_adv17["maxwind"],
)
]
fig.add_trace(go.Scattermap(
lat=tcm_lats, lon=tcm_lons,
mode="lines+markers",
line={"width": 3, "color": "#e63946"},
marker={"size": [16] + [8] * len(tcm_adv17), "color": "#e63946"},
name="TCM Adv 17 (15Z)",
hoverinfo="text",
text=tcm_labels,
))
# TCP Advisory 17A (18Z): single point from NHC archive
fig.add_trace(go.Scattermap(
lat=[tcp_17a_lat], lon=[tcp_17a_lon],
mode="markers",
marker={"size": 22, "color": "rgba(255,140,0,0.6)"},
name="TCP Adv 17A (18Z, t=0 update only)",
hoverinfo="text",
text=[
f"TCP 17A (intermediate)<br>18Z Oct 25<br>"
f"{tcp_17a_lat}N {abs(tcp_17a_lon)}W<br>"
f"{tcp_17a_wind}<br>"
f"Position update only, no new forecast"
],
))
fig.update_layout(
title=dict(
text=(
"<b>MELISSA Oct 25: NHC Analysis Cycle</b><br>"
"<span style='font-size:11px'>"
"A-deck 12Z synoptic analysis (blue) -> 15Z TCM "
"advisory (red) -> 18Z intermediate TCP (orange) "
"+ new A-deck 18Z cycle (teal)"
"</span>"
),
font_size=14,
),
map={
"style": "carto-positron",
"center": {"lat": 17, "lon": -76.5},
"zoom": 6,
},
height=700,
margin={"l": 20, "r": 20, "t": 70, "b": 20},
legend={
"yanchor": "top", "y": 0.98, "x": 0.01,
"bgcolor": "rgba(255,255,255,0.9)",
},
)
fig.show()