免责声明:以下算法只用于法律合规的测试、演示与教育。它不具备真实业务可用的物理精度,不能用于实际防灾决策。
本项目的“模拟地震/海啸”目标更偏向:
- 可控:用户能指定震中、深度、震级、发震/发报延迟,并按页递进送报。
- 可解释:给出“为什么这些地区被选中”和“到达时间怎么来的”的直觉解释。
- 可读:UI 不应一次塞入 60+ 个区域;更需要分层递进、可视化一致。
- 隔离生产逻辑:不改真实海啸数据源的 ingestion,只在模拟路径运行。
下面是对现有“按最近区域 + 固定数量”方案的升级思路:引入两个可解释的量——到达时间与影响强度(高度),用它们驱动区域选择与展示。
对每个海岸区域(用 centroid 近似)计算震中到区域中心的球面距离:
$R \approx 6371,km$ -
$\varphi$ 为纬度(弧度),$\lambda$ 为经度(弧度)
这个距离用于:
- 排序(近的优先)
- 传播时间与高度衰减的输入
深海海啸的相速度可用浅水波近似:
$g \approx 9.80665,m/s^2$ -
$h$ 为有效水深(m)。在 demo 中可取常数(例如$4000,m$ )。
然后到达时间(相对发震时刻):
-
$d$ :距离(m) -
$s$ :近岸减速因子(例如$1.15$ ,表示到岸附近整体更慢)
最终:
关键点:到达时间应基于发震时刻而不是“本页发报时刻”。否则第 2 页/第 3 页到达时间会被错误“整体推迟”。
真实海啸高度需要断层几何、海底形变、地形/水深等数据,这里用一个可调的启发式来满足:
- 随震级增大而增大
- 随震源深度增大而减小
- 随距离增大而衰减
建议形式:
其中:
一组对 UI 友好的默认参数(可后续暴露为高级设置):
$H_{ref}=0.5m$ $M_{pivot}=6.8$ $D=50km$ $L=200km$ $p=1.3$
这会带来两个很实用的性质:
- 更大的地震影响更广(更多区域满足阈值)
- 更高等级(更高阈值)影响更“集中”(区域数量减少)
为了满足你提到的体验目标:
小警报覆盖范围应该比大警报更广(至少不应更小)
最直接的做法是:按等级设置高度阈值,选择 H >= threshold 的区域:
- 注意报(status=1):$H \ge 0.3m$
- 警报(status=2):$H \ge 1.0m$
- 大警报(status=3):$H \ge 3.0m$
因为阈值单调递增,区域集合天然满足:
当震级较小/很深导致所有区域都低于阈值时,仍然需要“有内容可演示”。因此加一个回退:
- 若筛选为空,按距离取最近 N 个区域(N 随等级递减,确保层次递进仍成立)
例如:
- JMA:注意报 18、警报 12、大警报 6
- NMEFC:注意报 5、警报 4、大警报 3(总共就 5 个)
再加一个 maxAreas 上限(例如 JMA 25),避免 UI 列表过长。
实现文件:
src/utils/TsunamiSimulation.js
核心函数:
haversineDistanceKm(...)calcTsunamiSpeedMs(h)estimateTsunamiTravelTimeSec(d, h, slowdown)estimateTsunamiWaveHeightM({Mw, depthKm, distanceKm, ...})buildAutoTsunamiAreaImpacts(...)
关键实现片段(与公式一一对应):
// v = sqrt(g*h)
export const calcTsunamiSpeedMs = (oceanDepthM = 4000) =>
Math.sqrt(9.80665 * Math.max(1, Number(oceanDepthM) || 4000))
// t = d/v * slowdown
export const estimateTsunamiTravelTimeSec = (distanceKm, oceanDepthM = 4000, slowdown = 1.15) => {
const distM = Math.max(0, Number(distanceKm) || 0) * 1000
return (distM / calcTsunamiSpeedMs(oceanDepthM)) * Math.max(1, Number(slowdown) || 1)
}
// H(d)=H0(Mw)*exp(-depth/D) / (1 + (d/L)^p)
export const estimateTsunamiWaveHeightM = ({
magnitudeMw, depthKm, distanceKm,
sourceHeightM = 0.5, magnitudePivot = 6.8,
depthDecayKm = 50, distanceScaleKm = 200, distancePower = 1.3,
}) => {
const mw = Number(magnitudeMw)
const dep = Math.max(0, Number(depthKm) || 0)
const dist = Math.max(0, Number(distanceKm) || 0)
const h0 = sourceHeightM * 10 ** (0.5 * (mw - magnitudePivot))
return h0 * Math.exp(-dep / depthDecayKm) / (1 + (dist / distanceScaleKm) ** distancePower)
}模拟入口已改为使用这些输出:
src/components/components/MockEew.vue
关键行为变化:
- 到达时间基于
originTime+ travelTime(不再基于“发报时间 + 固定分钟数”) - 自动区域选择由“固定数量最近点”升级为“高度阈值筛选 + 空集回退”
- 增加“是否近海”门槛:通过“离海岸线距离”粗略判断内陆震是否生成海啸(需要 coast polyline / landmask 数据)。
- 区域方向性(扇形):按震中到区域的方位角加入 direction factor,模拟能量主要沿海沟方向传播(需要断层走向/经验假设)。
- 离散 travel-time 表:离线预计算(网格化)到达时间表(比实时计算更贴近真实地形/路径,但需要数据与构建脚本)。
- 多级区域混合:同一份报文中允许同时出现注意报/警报/大警报区域(更接近真实 JMA),但会让“本页等级”语义变复杂,需要 UI 上明确说明“本页为最大等级”。