地震情報の配信
eq.report.v1 は、サーバーが中央気象署(CWA)の新しい地震情報を取得したときに、その内容を送ります。形式は 地震情報 API の 1 件に近いものです。同じ情報は通常 2 件以上届きます。先にリアルタイム版が届き、データベースへ書き込んだあともう 1 件届きます。新しい地震情報の通知に向いています。
接続方法、復号、再接続の規則は SSE 接続 を参照してください。
データライセンス
Section titled “データライセンス”推奨表記データ出典:中央気象署、ExpTech Studio(exptech.dev)
- 中央気象署:中央気象署のデータ利用規約(政府データオープンライセンスなど)も適用されます。気象・地震情報は中央気象署の発表を正とします。
表記の場所、例、商用利用の申請はデータの利用条件と出典表記をご覧ください。
token は不要です(公開 topic)。有効な token を付けると、利用量は権限 eq.report に記録され、クォータを超えても中断されません。詳細は SSE 接続:認証 を参照してください。
- 多重化の入口:
/api/v1/trem/sse?topics=eq.report.v1。イベント名はeq.report.v1(プレフィックスはeqであり、tremではありません)。 - 単一 topic のパス:
tokenAPI Token(任意)。Authorization: Bearer header の使用を推奨します。
curl -N --compressed "https://api.lb.exptech.dev/api/v1/trem/sse/report"curl -N --compressed "https://api.lb-tpe1.exptech.dev/api/v1/trem/sse/report"curl -N --compressed "https://api.lb-khh1.exptech.dev/api/v1/trem/sse/report"const res = await fetch("https://api.lb.exptech.dev/api/v1/trem/sse/report");if (!res.ok) throw new Error(`HTTP ${res.status}`);
// text/event-stream:逐段讀取const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();for (;;) { const { value, done } = await reader.read(); if (done) break; console.log(value);}const res = await fetch("https://api.lb-tpe1.exptech.dev/api/v1/trem/sse/report");if (!res.ok) throw new Error(`HTTP ${res.status}`);
// text/event-stream:逐段讀取const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();for (;;) { const { value, done } = await reader.read(); if (done) break; console.log(value);}const res = await fetch("https://api.lb-khh1.exptech.dev/api/v1/trem/sse/report");if (!res.ok) throw new Error(`HTTP ${res.status}`);
// text/event-stream:逐段讀取const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();for (;;) { const { value, done } = await reader.read(); if (done) break; console.log(value);}import requests
with requests.get( "https://api.lb.exptech.dev/api/v1/trem/sse/report", stream=True, timeout=30,) as res: res.raise_for_status() for line in res.iter_lines(decode_unicode=True): if line: print(line)import requests
with requests.get( "https://api.lb-tpe1.exptech.dev/api/v1/trem/sse/report", stream=True, timeout=30,) as res: res.raise_for_status() for line in res.iter_lines(decode_unicode=True): if line: print(line)import requests
with requests.get( "https://api.lb-khh1.exptech.dev/api/v1/trem/sse/report", stream=True, timeout=30,) as res: res.raise_for_status() for line in res.iter_lines(decode_unicode=True): if line: print(line)text/event-stream。data は base64(gzip(JSON))
retry: 3000
event: infodata: {"location":"lb-tpe1"}
data: H4sIAAAAAAAC/...token が無効、期限切れ、または取り消し済み
invalid tokenmode はこの topic に影響せず、すべての件が送られます。
メッセージ形式
Section titled “メッセージ形式”各 data を復号すると、1 つの 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 } } } } }}| フィールド | 型 | 説明 |
|---|---|---|
reason | int | このメッセージの出所とタイミング。0–3。下の表を参照してください。 |
data.id | string | 情報 ID。形式は XXXXXX-YYYY-MMDD-HHMMSS。GET /api/v2/eq/report/{id} と同じです。 |
data.lat | number | 震央の緯度(度)。 |
data.lon | number | 震央の経度(度)。 |
data.depth | number | 震源の深さ(キロメートル)。 |
data.loc | string | 震央の位置の説明。 |
data.mag | number | リヒターマグニチュード。 |
data.time | int | 地震発生時刻。Unix ミリ秒。 |
data.list | object | 各県・市の震度。key は県・市の名前です。 |
<縣市>.int | int | その県・市の最大震度。0–9。 |
<縣市>.town.<測站> | object | 観測点の lat、lon(度)と int(震度 0–9)。 |
震度の値 0–9 の対応は 地震情報:震度の表し方 を参照してください。メッセージに int(台湾全域の最大震度)、md5、trem はありません。台湾全域の最大震度が必要なときは、list の各県・市の int の最大値を取ってください。
reason
Section titled “reason”| 値 | 出所 | いつ送るか | HTTP API で取得できるか |
|---|---|---|---|
0 |
気象署のリアルタイム発表 | 新しい情報を検知したとき(最も早い 1 件) | 必ずではない |
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を付け、各地の震度が必要なときは 1 件を検索してください。 - 同じ情報は通常 2 件以上届きます。 先に
reasonが0の 1 件が届き、データベースへ書き込んだあとに1、2、または3が届きます。data.idで重複を除き、あとから来た内容で先の内容を置き換えてください。 reasonが0のとき、HTTP API ではまだ見つからないことがあります。 このときGET /api/v2/eq/report/{id}は404を返すことがあり、他のreasonを受け取ったあとなら取得できます。- 2 件の
listは少し違うことがあります。reasonが0の件と、そのあとの件はデータの出所が違い、観測点と座標が違うことがあります。あとの件の座標は小数第 2 位に丸められます。 reasonが3の多くは古い情報ですが、そのまま無視しないでください。 たとえば取得できなかった情報は、定期メンテナンスのときに3で送られます。直近の情報だけが必要なときは、data.timeで絞ってください。- 時刻の単位。
data.timeは Unix のミリ秒です。
