How to Find Every Tsunami That Reached a Place
On this page
Problem statement
Hilo, on the Big Island of Hawaii, has been struck by tsunamis from Alaska, Chile, Japan and its own coast: the 1946 and 1960 waves destroyed its waterfront. To list every tsunami that reached a town, the run-up table is the place to look, not the event table, because events are located at their source. A run-up's location name is free text written by many hands, so a name search misses spellings. A distance search around the town's coordinates finds them all, and joining back to the events says where each tsunami came from. This page finds every run-up within 10 km of Hilo and summarises the town's tsunami history.
Quick answer
Compute each run-up's great-circle distance to the town and keep those within 10 km, then join the events for their sources:
lat0, lon0 = 19.725, -155.087 # Hilo, Hawaii
la, lo = np.radians(ru.latitude), np.radians(ru.longitude)
ru["km"] = 6371.0 * 2 * np.arcsin(np.sqrt(np.sin((la - np.radians(lat0)) / 2) ** 2
+ np.cos(la) * np.cos(np.radians(lat0)) * np.sin((lo - np.radians(lon0)) / 2) ** 2))
hilo = ru[ru.km <= 10].merge(ev[["id", "year", "locationName", "country", "causeCode", "eventValidity"]],
left_on="tsunamiEventId", right_on="id", suffixes=("", "_source"))
print(f"run-ups within 10 km: {len(hilo)}; tsunamis: {hilo.tsunamiEventId.nunique()}; first {hilo.year.min()}, last {hilo.year.max()}")
print(hilo.groupby("tsunamiEventId").agg(year=("year", "first"), source=("country_source", "first"), max_m=("runupHt", "max")).nlargest(6, "max_m").to_string(index=False))
run-ups within 10 km: 253; tsunamis: 125; first 1819, last 2026
year source max_m
1946 USA 10.7
1960 CHILE 10.7
1923 RUSSIA 6.1
1837 CHILE 6.0
1957 USA 5.2
1877 CHILE 4.8
253 run-ups within 10 km of Hilo record 125 different tsunamis from 1819 to 2026. The highest were the 1946 wave from the Aleutians and the 1960 wave from southern Chile, both 10.7 m, followed by tsunamis from Kamchatka in 1923, Chile in 1837 and the Aleutians again in 1957.
Names, sources and heights
1. A name search misses run-ups
named = ru.locationName.str.contains("HILO", na=False)
print(f"named Hilo: {named.sum()}; within 10 km: {(ru.km <= 10).sum()}; within 10 km but not named Hilo: {((ru.km <= 10) & ~named).sum()}")
print(ru[(ru.km <= 10) & ~named].locationName.value_counts().head(4).to_string())
named Hilo: 247; within 10 km: 253; within 10 km but not named Hilo: 31
locationName
KEAUKAHA, HAWAII, HI 5
KEAAU, HAWAII, HI 4
ONOMEA BAY, HAWAII, HI 4
PAPAIKOU, HAWAII, HI 3
247 run-ups are named Hilo, but 31 of the 253 within 10 km carry the names of neighbouring places, such as Keaukaha, Keaau, Onomea Bay and Papaikou. A text search also catches run-ups elsewhere whose names contain the same letters. The distance search finds the place, whatever it was called.
2. Where Hilo's tsunamis came from
print(hilo.groupby("tsunamiEventId").country_source.first().value_counts().to_string())
country_source
USA 30
RUSSIA 24
CHILE 16
JAPAN 11
MEXICO 9
PERU 6
TONGA 5
NEW ZEALAND 4
SOLOMON ISLANDS 3
PAPUA NEW GUINEA 3
CANADA 2
GUATEMALA 2
VANUATU 2
ECUADOR 1
COSTA RICA 1
FIJI 1
COLOMBIA 1
NICARAGUA 1
INDONESIA 1
SAMOA 1
UK TERRITORY 1
Hilo's tsunamis came from all around the Pacific: 30 from sources in the United States, mostly Alaska and Hawaii itself, 24 from Russia's Kamchatka and Kurils, 16 from Chile and 11 from Japan. Hawaii sits in the middle of the ocean, so waves from every subduction zone around its rim arrive there.
3. The deadly ones
print(hilo.groupby("tsunamiEventId").agg(year=("year", "first"), source=("locationName_source", "first"), deaths=("deaths", "sum")).query("deaths > 0").to_string(index=False))
year source deaths
1837 SOUTHERN CHILE 14.0
1877 NORTHERN CHILE 5.0
1923 KAMCHATKA 1.0
1946 UNIMAK ISLAND, AK 96.0
1960 SOUTHERN CHILE 61.0
Five tsunamis killed people in and around Hilo according to the run-up records: 96 in 1946 from the Aleutians, 61 in 1960 from Chile, and smaller tolls in 1837, 1877 and 1923. The two worst led to Hawaii's tsunami warning system and to moving Hilo's waterfront back from the bay.
Other places
The data used on this page
NOAA's Global Historical Tsunami Database from the National Centers for Environmental Information (doi:10.7289/V5PN93H7), public domain: every event and run-up from its HazEL API, downloaded on 19 October 2026 and saved on this site, because records are added and revised. Heights are in metres and distances in kilometres.
from pathlib import Path
import json
import zipfile
import geopandas as gpd
import numpy as np
import pandas as pd
import requests
Path("data").mkdir(exist_ok=True)
# NOAA NCEI Global Historical Tsunami Database (doi:10.7289/V5PN93H7): every event and every run-up from the HazEL API,
# with Natural Earth's 1:50m land and 1:110m countries. Saved 19 October 2026: events and run-ups are added and revised.
# Live: https://www.ngdc.noaa.gov/hazel/hazard-service/api/v1/tsunamis/events?page=1 (and .../runups), 200 a page
ARCHIVE = "ncei-tsunami-2026-10-19.zip"
if not Path(f"data/{ARCHIVE}").exists():
Path(f"data/{ARCHIVE}").write_bytes(requests.get(f"https://data.spatialworkflow.io/{ARCHIVE}", timeout=600).content)
Z = zipfile.ZipFile(f"data/{ARCHIVE}")
ev = pd.DataFrame(json.loads(Z.read("tsunami-events.json")))
ru = pd.DataFrame(json.loads(Z.read("tsunami-runups.json")))
print(f"NCEI tsunami database: {len(ev):,} events, {len(ru):,} run-ups")
NCEI tsunami database: 3,138 events, 35,129 run-ups
Example 1: a function for any place
def tsunamis_near(lat, lon, km=10):
la, lo = np.radians(ru.latitude), np.radians(ru.longitude)
d = 6371.0 * 2 * np.arcsin(np.sqrt(np.sin((la - np.radians(lat)) / 2) ** 2 + np.cos(la) * np.cos(np.radians(lat)) * np.sin((lo - np.radians(lon)) / 2) ** 2))
return ru[d <= km].tsunamiEventId.nunique()
for name, lat, lon in [("Hilo", 19.725, -155.087), ("Crescent City", 41.756, -124.202), ("Valparaíso", -33.046, -71.620), ("Lisbon", 38.708, -9.139)]:
print(f"{name:>14}: {tsunamis_near(lat, lon)} tsunamis")
Hilo: 125 tsunamis
Crescent City: 48 tsunamis
Valparaíso: 55 tsunamis
Lisbon: 2 tsunamis
Hilo has records of 125 tsunamis, Valparaíso 55 and Crescent City in California 48, while Lisbon has 2, though one of them, in 1755, was among the deadliest in history. The count reflects how exposed a coast is, and how long and how carefully its tide gauges and observers have recorded small waves.
Example 2: travel times to Hilo
h = hilo.dropna(subset=["travHours"]).groupby("tsunamiEventId").agg(year=("year", "first"), source=("country_source", "first"), hours=("travHours", "min"))
print(h.sort_values("hours").tail(5).to_string(index=False))
year source hours
1868 CHILE 14.0
1960 CHILE 14.0
1906 CHILE 16.0
1837 CHILE 16.0
2004 INDONESIA 26.0
The waves that took longest to reach Hilo came from Chile, 14 to 16 hours, and the 2004 tsunami from Sumatra, which reached the Hawaiian gauges 26 hours later, having travelled through the Indian Ocean into the Pacific.
Internal links
- Travel times across the Pacific — the speeds behind Example 2
- Tsunamis counted in the wrong country — events are at the source, run-ups at the coast
- Loading events and run-ups — the two tables
- Nearest-neighbour joins — distance searches between two point sets