ハザードマップに「いま」を足した——気象警報・避難所の開設状況・発令の記録を住所ひとつで
住所を入れると洪水と内水の浸水想定を返す道具(Kurage 洪水・内水ハザードマップ)に、今日4つ機能を足しました。きっかけは新しい要望ではなく、他の自治体の画面と自分の画面を並べて実測したことです。差は機能の数ではありませんでした。
- デモ(いま、どうするか): https://kurage.exbridge.jp/kflood.php/now?ref=vwork-kflood-now
- 今日つくった記録ページ: https://kurage.exbridge.jp/kflood.php/history?ref=vwork-kflood-now
- オンプレミス版: https://kappstore.exbridge.jp/app.php?id=41a09acc163dcb7d&ref=vwork-kflood-now
比べたのは、東京都港区と名古屋市の防災ポータル
港区の防災ポータルは Salesforce の Experience Cloud で建てた運用画面でした。名古屋市の防災ポータルは市の共通CMSの1ページで、実体はリンク集です。ここまでは想像どおりでしたが、中身を取得して読んでみると、予想と違う結論になりました。
名古屋市もデータは持っていました。 雨量も水位も気象警報も、名古屋市水防情報システム(NICOS)、上下水道局の雨水情報、監視カメラの各システムにあります。ただし全部が別のサイトで、しかもどれも機械可読な口がありません(NICOS・愛知県 川の防災情報・上下水道局の3つとも、ページの中にJSONやCSVへの参照が0件でした)。
差は2つでした。
- 1つの住所について「いま」を束ねる層があるかどうか
- 出ていないときに、そう書くかどうか
港区は空でも枠があります。「避難情報はありません。」「避難所情報はありません。」と明示されます。名古屋市は発令が無いと枠ごと消え、通常のお知らせと同じ流れに混ざります。住民からは「出ていない」のか「取れていない」のか区別がつきません。
2つ目が本質でした。ここは実装量の問題ではなく、何を書くと決めるかの問題です。
足した4つ
1. 気象庁の警報・注意報(全国)
気象庁の防災情報(bosai)のJSONは鍵が要りません。区域表(area.json)の class20s は1,805件あり、住所文字列に対する最長一致で市区町村コードが引けます。そこから親をたどって府県予報区を決め、警報JSONからその市に出ているものだけを取ります。
政令市は区ではなく市の単位で発表されるので、区名まで一致させないのが正解でした(名古屋市 = 2310000)。コードは数字だけで来るので、変換表を自前で持ちます(20 = 濃霧注意報)。
住所から引く作りにしたので、結果として全国どこでも動きます。東京都港区の住所を入れると「港区に気象警報・注意報は発表されていません」と出ます。
2. 「出ていない」を常設の枠にする
ここが今日の主題です。3つの状態を、はっきり書き分けました。
| 状態 | 画面に出す文 |
|---|---|
| 出ている | 「名古屋市に注意報が出ています/濃霧注意報」 |
| 出ていない | 「名古屋市に気象警報・注意報は発表されていません」 |
| 取得できない | 「取得できませんでした。発表されていないという意味ではありません」 |
3つ目を2つ目に混ぜるのが、この種の道具でいちばん危ない誤りです。配信が落ちているときに「発令はありません」と出したら、それは嘘になります。取得時刻も必ず添えます。
3. 避難所の開設状況(出せないものは、出せないと書く)
港区は「現在の開設状況」を一等地に置いています。こちらも枠を作りましたが、中身は2行に分けました。
- 帰宅困難者向けの退避施設: 110施設中 0施設が開設(時点つき)
- 指定避難所・指定緊急避難場所: 開設状況は出せません
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には出していません。