跳到內容
主控台

地震報告推送

eq.report.v1 在伺服器取得中央氣象署(CWA)新的地震報告時推送報告內容,格式與 地震報告 API 的單筆報告相近。同一筆報告通常會收到兩則以上:先收到即時版本,寫入資料庫後再收到一則。適合用來做新地震報告通知。

連線方式、解碼與重連規則見 SSE 連線。

上游條款
  • 中央氣象署:另須遵守中央氣象署的資料使用規範(例如政府資料開放授權條款);氣象與地震資訊以中央氣象署發布的內容為準。

標示位置、範例與商業使用申請見資料授權與來源標示。

不需要 token(公開 topic)。帶有效 token 時,用量記在權限 eq.report 下,超過配額也不會中斷。詳見 SSE 連線:驗證。

  • 多工入口:/api/v1/trem/sse?topics=eq.report.v1,事件名稱 eq.report.v1(前綴是 eq,不是 trem)。
  • 單一 topic 路徑:
GEThttps://api.lb.exptech.dev/api/v1/trem/sse/report
Query 參數
  • tokenstring選填

    API Token(選填)。建議改用 Authorization: Bearer header。

請求範例
curl -N --compressed "https://api.lb.exptech.dev/api/v1/trem/sse/report"
回應

text/event-stream;data 是 base64(gzip(JSON))

retry: 3000
event: info
data: {"location":"lb-tpe1"}
data: H4sIAAAAAAAC/...

token 無效、過期或已撤銷

invalid token

mode 對這個 topic 沒有影響,每一則都會送出。

每則 data 解碼後是一個 JSON 物件:

{
"reason": 1,
"data": {
"id": "115079-2026-1109-122157",
"lat": 23.62,
"lon": 120.66,
"depth": 10.5,
"loc": "雲林縣政府東南東方 23.2 公里 (位於雲林縣古坑鄉)",
"mag": 4.2,
"time": 1794198117000,
"list": {
"雲林縣": {
"int": 3,
"town": {
"草嶺": { "lat": 23.6, "lon": 120.68, "int": 3 }
}
}
}
}
}
欄位類型說明
reasonint這則訊息的來源與時機,0–3,見下表。
data.idstring報告 ID,格式 XXXXXX-YYYY-MMDD-HHMMSS,與 GET /api/v2/eq/report/{id} 相同。
data.latnumber震央緯度(度)。
data.lonnumber震央經度(度)。
data.depthnumber震源深度(公里)。
data.locstring震央位置描述。
data.magnumber芮氏規模。
data.timeint發震時間,Unix 毫秒。
data.listobject各縣市震度,key 是縣市名稱。
<縣市>.intint該縣市的最大震度,0–9。
<縣市>.town.<測站>object測站的 lat、lon(度)與 int(震度 0–9)。

震度數值 0–9 的對照見 地震報告:震度表示法。訊息沒有 int(全臺最大震度)、md5、trem;需要全臺最大震度時,取 list 裡各縣市 int 的最大值。

值 來源 何時送出 已可用 HTTP API 查到
0 氣象署即時發布 偵測到新報告時(最早的一則) 不一定
1 氣象署報告頁面 當天發生的報告寫入資料庫時 是
2 氣象署報告頁面 補上之前漏掉的報告時 是
3 氣象署報告頁面 定期維護時寫入或回填報告(多半是舊報告) 是

瀏覽器用單一 topic 路徑接收:onmessage 收資料並解碼,連上後用 HTTP API 補抓最近的報告,再以 data.id 去重。

async function decodeData(b64) {
const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
const stream = new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"));
return await new Response(stream).text();
}
const seen = new Set();
async function catchUp() {
// 這個 topic 不補送:連上後用 HTTP API 補抓最近的報告
const res = await fetch("https://api.core.exptech.dev/api/v2/eq/report?limit=10&minIntensity=0");
for (const r of await res.json()) seen.add(r.id);
}
const es = new EventSource("https://api.lb.exptech.dev/api/v1/trem/sse/report");
es.addEventListener("info", () => catchUp());
es.onmessage = async (e) => {
const { reason, data } = JSON.parse(await decodeData(e.data));
if (reason === 3 && Date.now() - data.time > 24 * 3600 * 1000) return; // 回填的舊報告
const isNew = !seen.has(data.id);
seen.add(data.id);
const maxInt = Math.max(0, ...Object.values(data.list).map((a) => a.int));
console.log(isNew ? "新地震報告" : "報告更新", data.id, `M${data.mag}`, `最大震度 ${maxInt}`);
};
  • 不補送。 連線(或重連)時不會送出之前的報告。斷線期間的報告請在重連後呼叫 GET /api/v2/eq/report 補齊;要包含震度 0 的報告請帶 minIntensity=0,需要各地震度時再查單筆報告。
  • 同一筆報告通常會收到兩則以上。 先收到 reason 為 0 的一則,寫入資料庫後再收到 1、2 或 3。請用 data.id 去重,後到的內容取代先到的。
  • reason 為 0 時,HTTP API 可能還查不到。 這時查 GET /api/v2/eq/report/{id} 可能回 404,收到其他 reason 後就查得到。
  • 兩則的 list 可能略有不同。 reason 為 0 的一則與之後的一則資料來源不同,測站與座標可能有差異;之後那則的座標四捨五入到小數 2 位。
  • reason 為 3 多半是舊報告,但不要直接忽略。 例如補不到的報告會在定期維護時以 3 送出。只需要近期報告時,請依 data.time 過濾。
  • 時間單位。 data.time 是 Unix 毫秒。