VWork バイブコーディングフレームワーク

ハザードマップに「いま」を足した——気象警報・避難所の開設状況・発令の記録を住所ひとつで

2026年9月21日

住所を入れると洪水と内水の浸水想定を返す道具(Kurage 洪水・内水ハザードマップ)に、今日4つ機能を足しました。きっかけは新しい要望ではなく、他の自治体の画面と自分の画面を並べて実測したことです。差は機能の数ではありませんでした。

比べたのは、東京都港区と名古屋市の防災ポータル

港区の防災ポータルは Salesforce の Experience Cloud で建てた運用画面でした。名古屋市の防災ポータルは市の共通CMSの1ページで、実体はリンク集です。ここまでは想像どおりでしたが、中身を取得して読んでみると、予想と違う結論になりました。

名古屋市もデータは持っていました。 雨量も水位も気象警報も、名古屋市水防情報システム(NICOS)、上下水道局の雨水情報、監視カメラの各システムにあります。ただし全部が別のサイトで、しかもどれも機械可読な口がありません(NICOS・愛知県 川の防災情報・上下水道局の3つとも、ページの中にJSONやCSVへの参照が0件でした)。

差は2つでした。

  1. 1つの住所について「いま」を束ねる層があるかどうか
  2. 出ていないときに、そう書くかどうか

港区は空でも枠があります。「避難情報はありません。」「避難所情報はありません。」と明示されます。名古屋市は発令が無いと枠ごと消え、通常のお知らせと同じ流れに混ざります。住民からは「出ていない」のか「取れていない」のか区別がつきません。

2つ目が本質でした。ここは実装量の問題ではなく、何を書くと決めるかの問題です。

足した4つ

1. 気象庁の警報・注意報(全国)

気象庁の防災情報(bosai)のJSONは鍵が要りません。区域表(area.json)の class20s は1,805件あり、住所文字列に対する最長一致で市区町村コードが引けます。そこから親をたどって府県予報区を決め、警報JSONからその市に出ているものだけを取ります。

政令市は区ではなく市の単位で発表されるので、区名まで一致させないのが正解でした(名古屋市 = 2310000)。コードは数字だけで来るので、変換表を自前で持ちます(20 = 濃霧注意報)。

住所から引く作りにしたので、結果として全国どこでも動きます。東京都港区の住所を入れると「港区に気象警報・注意報は発表されていません」と出ます。

2. 「出ていない」を常設の枠にする

ここが今日の主題です。3つの状態を、はっきり書き分けました。

状態 画面に出す文
出ている 「名古屋市に注意報が出ています/濃霧注意報」
出ていない 「名古屋市に気象警報・注意報は発表されていません」
取得できない 「取得できませんでした。発表されていないという意味ではありません」

3つ目を2つ目に混ぜるのが、この種の道具でいちばん危ない誤りです。配信が落ちているときに「発令はありません」と出したら、それは嘘になります。取得時刻も必ず添えます。

3. 避難所の開設状況(出せないものは、出せないと書く)

港区は「現在の開設状況」を一等地に置いています。こちらも枠を作りましたが、中身は2行に分けました。

2行目を空欄にせず、理由ごと書いています。市の指定避難所の開設状況レイヤは2024年10月29日以降更新されておらず、9月8日に警戒レベル5が出ている最中も全件が未開設のままでした。更新されていない値で「未開設」と表示するのは、空欄より危ないからです。

4. 出た発令の記録(住所から引く)

港区は配信の履歴が時刻つきで1か月ぶん残ります。名古屋市は事後のお知らせに流れて追えません。

こちらは市の配信を3分ごとに取っているので、貯めれば住所から引けます。/history に住所を入れると、その学区に出た避難情報が新しい順に並びます。9月8日の発令もそのまま残っていて、CSVとJSONで取り出せます。町内会の記録、委員会での質問、研究に、加工せず渡せる形にしました。

記録が途切れないよう、サービスの中で3分ごとに取り直しています。「誰かがページを見たときだけ記録する」作りだと、夜中に出た発令を誰も踏まなければ残らないからです。新しい systemd タイマーは足していません(既存のユニットの中で完結させました)。

半日で踏んだこと

Jinja2 で jm.items と書くと、dict の items メソッドに当たります。 500になります。jm['items'] と書きます。テンプレートに渡す dict のキーが items / keys / values のときは毎回これを踏みます。

製品をまたぐと、バグが見える場所が変わります。 避難所の開設状況は別サービス(避難所マップ)が持っていて、そちらを叩いたら常に null でした。原因は、緯度経度だけで問い合わせると相手側の住所ラベルが「35.13,136.94」になり、市名で判定している分岐に入らないことでした。そのサービスを単体で使っているあいだは、誰も気づけない類のバグです。最寄り避難所の住所からも判定するように直しました。

「機械可読な口が無い」は、推測ではなく取得して確かめます。 雨量・水位を足すかどうかを決めるために、3つのサイトを取得して中身のJSON参照を数えました。0件だったので、実装はスクレイプになり規約確認も要ると分かり、優先順位を下げました。代わりに気象庁の危険度分布タイルを先に入れることにしました。やらない判断も、実測してから記録に残します。

設計の芯は、3つの言葉を混ぜないこと

この道具では「該当なし」「未収録」「取得できない」を厳密に分けています。

3つとも画面上は「何も出ていない」ように見えますが、意味は正反対です。混ぜた瞬間に、データが無い場所が安全な場所に化けます。今日足した4つは、どれもこの区別を新しい枠に広げただけとも言えます。機能を足したというより、黙っていた場所に言葉を置きました。

同じ考え方は、防災以外の判定系にもそのまま移せます。在庫、与信、法令の適用判定——「無い」と「調べていない」と「調べられなかった」を分けて書く画面は、作るのに半日、信用を保つのに一生効きます。


この仕組みは、事務所・自治体・会社の名前で公開できるオンプレミス版としてソースコード同梱で出しています。データは国と自治体の公開データだけ、判定は当社のデータセンターで完結し、外部のAPIには出していません。