URL_PAISES = ("https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/"
"ne_50m_admin_0_countries.geojson")
archivo_paises = DATA / "paises_ne50m.geojson"
if not archivo_paises.exists():
archivo_paises.write_bytes(requests.get(URL_PAISES, timeout=120).content)
paises = gpd.read_file(archivo_paises)
paises = paises.cx[BBOX["lon_min"] - 6:BBOX["lon_max"] + 6, BBOX["lat_min"] - 6:BBOX["lat_max"] + 6]
colombia = paises[paises["ADMIN"] == "Colombia"]
CMAP = LinearSegmentedColormap.from_list("profundidad", ["#fde3df", "#f6a6b2", "#c2408c", "#6a1b7a", "#1b0b2e"])
NORM = Normalize(0, PROF_MAX)
def tam(mag):
"""Área del marcador: crece exponencialmente con la magnitud (M2≈1, M4≈8, M6≈57, M8≈430)."""
return 2.75 ** (np.clip(np.asarray(mag, dtype=float), 2, 9.5) - 2)
def mapa_base(ax):
ax.set_facecolor("#b7cde8")
paises.plot(ax=ax, color="#8a9c95", edgecolor="#333", linewidth=0.5)
colombia.plot(ax=ax, color="#7d9189", edgecolor="k", linewidth=0.9)
ax.set_xlim(BBOX["lon_min"], BBOX["lon_max"]); ax.set_ylim(BBOX["lat_min"], BBOX["lat_max"])
ax.set_aspect(1 / np.cos(np.radians(LAT_MEDIA)))
ax.set_xlabel("Longitud"); ax.set_ylabel("Latitud")
for lado in ("top", "right"):
ax.spines[lado].set_visible(True)
def leyenda_magnitud(ax, loc="lower left"):
marcas = [ax.scatter([], [], s=tam(m), color="0.2", label=f"M{m}") for m in (4, 6, 8)]
hueco = ax.scatter([], [], s=40, facecolors="none", edgecolors="0.25", linewidths=0.8,
label="Prof. desconocida")
ax.legend(handles=marcas + [hueco], loc=loc, fontsize=7, frameon=True, framealpha=0.85,
labelspacing=1.1, borderpad=0.9)
def dibujar_sismos(ax, df, alpha=0.75):
df = df.sort_values("mag", ascending=False) # grandes debajo
con, sin = df[df.prof_km.notna()], df[df.prof_km.isna()]
sc = ax.scatter(con.lon, con.lat, s=tam(con.mag), c=con.prof_km, cmap=CMAP, norm=NORM,
alpha=alpha, linewidths=0, rasterized=True)
ax.scatter(sin.lon, sin.lat, s=tam(sin.mag), facecolors="none", edgecolors="0.25", linewidths=0.8)
return sc
fig, ax = plt.subplots(figsize=(7.2, 8.2))
mapa_base(ax)
sc = dibujar_sismos(ax, cat)
fig.colorbar(sc, ax=ax, shrink=0.6, pad=0.02, extend="max", label="Profundidad (km)")
leyenda_magnitud(ax)
for _, r in cat.nlargest(6, "mag").iterrows(): # etiquetas por magnitud
ax.annotate(f"M{r.mag:.1f} · {r.anio}", (r.lon, r.lat), xytext=(5, 5), textcoords="offset points",
fontsize=7, fontweight="bold")
if len(noaa) and noaa.muertes.notna().any(): # etiquetas por impacto
for _, r in noaa.dropna(subset=["muertes", "lat", "lon"]).nlargest(6, "muertes").iterrows():
ax.plot(r.lon, r.lat, marker="x", color="#b00020", ms=5, mew=1.2)
ax.annotate(f"{r.lugar} {int(r.anio_dec)} · {miles(r.muertes)} muertes", (r.lon, r.lat),
xytext=(5, -9), textcoords="offset points", fontsize=6.5, color="#b00020")
ax.plot(EVENTO_REF["lon"], EVENTO_REF["lat"], marker="*", ms=13, color="#ffd400", mec="k", mew=0.8)
ax.set_title(f"Sismicidad de Colombia y territorios vecinos, {cat.anio.min()}–{cat.anio.max()}\n"
f"{miles(len(cat))} sismos · ★ {EVENTO_REF['nombre']}", fontsize=10)
fig.text(0.01, 0.005, f"{CREDITO} Límites: Natural Earth.", fontsize=6.5, color="0.35")
fig.savefig(FIG / "fig4_mapa.png"); plt.show()