本文へスキップ
半農エンジニアラボENGINEERING × EVERYDAY

FIELD NOTE / 2026

HAIZU開発シリーズ②|画面・サーバー・計算係の役割は?アプリ全体の仕組みを図解

HAIZUのPC画面。Tephra2と簡易時間モデルによる仮定条件下の降灰分布

地図の上で時間バーを動かすと、火山灰の色が変わる。その裏では、ブラウザがすべて計算しているのでしょうか。それとも、操作のたびにサーバーで火山灰の計算をやり直しているのでしょうか。

HAIZUでは、「操作と表示」「要求の受付と結果の準備」「記録の管理」「降灰分布の事前計算」を分担しています。名前だけ並べると難しく見えますが、一つの操作を追いかけると、各技術が何をしているのかが見えてきます。

第1回は、目的や必要な機能、作る範囲を整理しました。第2回では、その設計を実際に動かす仕組みを見ます。React、Go、SQLite、Tephra2という名前を、最初から覚えている必要はありません。「これは何の係なのか」という見方で読んでもらえれば大丈夫です。

ここから読む方へ:紹介・第0回:完成したHAIZUの画面と開発ログ / 第1回:アプリを作る前に、何を決めたのか?

HAIZUは防災学習・参考情報のためのアプリです。実際の噴火や降灰を予測するものではなく、避難や地点の安全性の判断には使えません。本記事ではアプリの構成を説明します。計算モデルの前提と精度の検証は、プログラムが動くこととは別の話として扱います。

全体図で見る「画面・受付・記録・計算」の役割分担

最初に、HAIZUの主な仕組みを図にしました。緑の部分が普段の利用時、オレンジの部分が管理側で事前にデータを用意する工程です。特に、通常の操作とTephra2の事前計算を、別の流れとして見るのがポイントです。

HAIZUの構成図。ブラウザがGo APIへ要求し、APIがSQLiteと格子ファイルを利用。Tephra2は管理側で事前に格子を作成する
図1:HAIZUの役割分担を整理した概略図。背景地図などの配信経路は省略しています。図を押すと拡大できます。Tephra2からの矢印は、事前計算したデータを準備する流れです。

身近な例に寄せるなら、ブラウザは注文や受け取りをする窓口、GoのAPIは注文を確認して仕事を進める担当、SQLiteは受付台帳、ファイルは実際の資料の置き場です。Tephra2は、その資料となる降灰分布を計算する役割を持ちます。

もちろん、料理の注文のように毎回一から同じ作業をするわけではありません。すでに準備されたデータや結果があれば、再利用します。画面からは一つの操作に見えても、裏側では「新しく準備するか」「前の結果を使えるか」を判断しています。

ここでの「サーバー」は、利用者の端末からの依頼を受ける側のことです。一つの物理的なコンピューターの上に、画面を配る仕組みとAPIが同居することもあります。「係が分かれているから、別々の高価なコンピューターが必要」という意味ではありません。

ブラウザの仕事は、操作を受け取り、結果を読みやすく見せること

利用者が触る部分は「フロントエンド」と呼ばれます。HAIZUでは、条件入力や時間操作などの画面をReactで作り、地図の表示にはMapLibre GL JSを使っています。

Reactでは、ボタンや入力欄などを部品として組み合わせ、選んだ条件や表示する時刻が変わったら、その状態に合わせて画面を更新します。「現在は12時間後」「条件変更画面が開いている」といった情報を、画面の部品同士でそろえるための仕組みです。部品や状態という考え方は、Reactの公式入門でも説明されています。

HAIZUのPC画面。条件の説明と地図、噴火から3日後を示す時間操作を表示
キャプチャ1:2026年10月14日の富士山版。下の時刻、左の条件、中央の地図は別々の要素ですが、同じ表示条件を示すようにつなぎます。学習用シナリオの表示例です。

MapLibreは、背景地図を動かしたり拡大したりしながら、降灰の画像を地図の上に重ねる担当です。降灰画像には「地図上のどこに置くか」という座標の情報が必要になります。ただ画像を画面中央へ貼るだけでは、富士山の位置と分布がずれてしまうからです。この重ね方は第3回で詳しく扱います。

また、同じブラウザ側でも、扱っている情報の保存先は一つとは限りません。HAIZUで保存する地点の名前や座標は、このブラウザの保存領域に記録します。一方、サーバー側の計算結果や処理状態は、別の場所で管理します。ブラウザに地点を保存したからといって、別の端末に自動で同期されるわけではありません。

TypeScriptは、画面側のコードで受け渡すデータの形を確認しやすくするために使っています。Viteは開発や公開用ファイルの組み立てを担当します。地図を動かす係、入力を扱う係、開発を支える道具を、ひとまとめに覚えようとしなくて大丈夫です。

GoのAPIは、ブラウザからの依頼を受ける受付になる

「API」は、プログラム同士がやり取りするための窓口です。HAIZUではGoで作ったAPIが、条件を受け取り、使える条件かを確認し、結果の準備や画像・地点値の返却を行います。APIのURLごとに処理を振り分ける部分には、chiというライブラリを使っています。

たとえば、ブラウザは「この条件の結果が欲しい」と依頼します。サーバーは、その要求を受け取ったという情報と、要求を追跡するためのIDを返します。このIDは、受付番号のようなものです。ブラウザはその後、番号を使って「結果は準備できたか」を確認します。

HAIZUでは、この一まとまりの処理を「ジョブ」として管理しています。順番待ち、準備中、完了、失敗といった状態を区別するためです。受付番号が返ってきた時点では、表示に必要な結果がすべてそろっているとは限りません。逆に、同じ条件の結果がすでにあれば、それを使える場合もあります。

こうした手順を挟むのは、画面を複雑にするためではありません。準備できていない画像を先に読みに行く、条件を変更したのに前の結果を新しい結果として見せる、といった取り違えを避けるためです。「依頼を受けた」と「結果が使える」を分けて管理します。

入力の確認も受付の仕事です。画面上に選択肢があっても、サーバーは受け取った値を改めて確認します。通常運用で使う計算済みデータがない条件なら、その場で重い計算を始めるのではなく、対応できないことを返します。ブラウザの見た目だけを整えても、裏側の受付ルールは別に必要になります。

SQLiteは管理台帳、格子データはファイルに分けて保存する

「データベースに保存する」と聞くと、HAIZUのデータが全部そこに入っているように思えるかもしれません。実際には、管理用の記録と、計算結果の数値データを分けています。

SQLiteに記録するのは、匿名利用者の識別、処理の状態、どの結果へアクセスできるかといった管理情報です。SQLiteは、アプリから利用できる、独立したデータベースサーバーを必要としないデータベースエンジンです。特徴はSQLiteの公式説明を参照できます。HAIZUではGoのプログラムからこの記録を読み書きします。

一方、降灰の計算結果の中心となるのは「格子データ」です。地図を小さなマス目に分け、それぞれのマスに堆積量の数字を持たせたもの、と考えると分かりやすくなります。地図に描く色の元になる数字が、マスごとに並んでいます。

現行の実装では、結果ごとの格子をmass.binというファイルに保存します。表示の案内はmanifest.jsonという別のファイルです。「manifest」は少し見慣れない名前ですが、ここでは画像の参照先、表示できる時刻、地図上の配置、条件などをまとめた案内書だと捉えてください。

保存するもの 主な保存先 何のために使うか
処理の状態・匿名利用者・アクセス権 SQLite 要求や結果の利用を管理する
事前計算した条件別の分布 サーバーのファイル 条件に対応する数値の元データを読む
結果の格子と表示の案内 結果ごとのファイル 時刻の画像や地点値を用意する
保存した地点の名前と座標 利用中のブラウザ 同じブラウザで地点を呼び出す

この分け方を知っておくと、運用時にも役立ちます。管理用のデータベースだけを退避しても、格子ファイルのバックアップにはなりません。また、ブラウザに保存した地点とサーバーの結果データでは、消える条件も保存期間も違います。バックアップや整理の話では、「どのデータのことか」をそろえる必要があります。

Tephra2は分布を計算する担当、時間表現はHAIZU側が担当する

Tephra2は、噴火条件を与えたときの火山噴出物の堆積量を計算するツールです。HAIZUでは、その空間的な分布を利用します。計算ツール自体の説明や入力例は、Tephra2の公式リポジトリにあります。

ただし、通常のWeb操作で条件を選ぶたびに、Tephra2を起動して分布を一から作り直す構成ではありません。管理側で条件別の格子データを計算・準備し、通常のAPIはそこから対応するデータを読みます。富士山用の12,480通りという数は、この準備する条件の組み合わせに当たります。

そして、最終的な分布があれば、そのまま途中の時刻が分かるわけでもありません。HAIZUでは、到達の簡易な遅れや噴出の推移を組み合わせ、各時刻の累積量を近似します。Tephra2による空間分布と、HAIZU側の簡易時間モデルは、役割が違います。

ここを曖昧にすると、「Tephra2を組み込んだから、すべての時刻の降灰が検証済みになった」という誤解につながります。時間・高度ごとに変わる現実の風などを、そのまま再現しているわけではありません。モデルの考え方は第5回・第6回で改めて説明する予定です。

開発に利用したCodexは、コードの変更や確認を支援する役割です。通常の降灰表示のために言語モデルへ問い合わせて、分布や時刻の値を生成する仕組みにはしていません。「開発を支える道具」と「アプリの結果を作る計算」を区別しておくと、どの結果の根拠を確認するべきかも整理できます。

条件を変更してから地図に表示されるまでを追いかける

ここまでの係を、一つの操作でつないでみます。例は、条件変更画面で風向きなどを選び、更新を実行する場合です。以下は実計算のデータを使う通常モードの流れで、操作体験用デモとは区別します。

HAIZUの条件設定画面。噴火規模、海抜の噴煙高度、風向きを選択する欄を表示
キャプチャ2:2026年10月14日の条件設定画面。選択した値は、更新を実行するとAPIへの依頼に使われます。選択肢が画面にあることと、結果が準備できたことは別です。
  1. ブラウザが条件を送る。風向きや噴煙高度などの値を、APIが読める形にまとめます。
  2. Go側が条件と利用条件を確認する。入力範囲、データの準備状況、既存結果を利用できるかなどを調べます。
  3. 結果を再利用するか、表示用の結果を準備する。新しい準備が必要なら、処理状態を管理しながら格子と案内書を用意します。
  4. ブラウザが完了を確認し、案内書を受け取る。どの時刻の画像を、地図上のどこへ重ねるかが分かります。
  5. 必要な時刻の画像を取得して、地図へ重ねる。地図の背景と降灰画像が、画面上で一つの表示になります。

やり取りのうち、状態や案内書などにはJSONというデータ形式を使います。名前の付いた項目と値を、プログラムが読める形で渡すものです。ただし、降灰画像まで全部JSONの中に詰め込んでいるわけではありません。画像は画像のURLから、PNG形式で取得します。

たとえば30日間を1時間刻みで見せる場合、0時間を含めると721時刻あります。案内書に721時刻の画像の参照先があっても、その場で721枚の完成画像を受け取っているわけではありません。参照先の一覧と、画像本体の取得は別です。この違いが、通信やファイル作成を増やしすぎない設計につながっています。

通信に失敗した場合や、準備できない条件だった場合も、結果として扱う必要があります。画面側は失敗の案内を受けて説明し、完了していない結果を新しい表示へ切り替えません。正常に動いたときの道筋と、進めなかったときの道筋を両方考えます。

時間バーの操作と、地点を押す操作では頼む内容が違う

一度結果が使えるようになった後は、条件を登録し直さなくても、その結果の中で表示する時刻を変えられます。ブラウザが時間バーで選んだ時刻に対応する画像を取得し、地図上の降灰画像を差し替えます。

現行の通常処理では、Go側が格子ファイルを読み、その時刻の累積量を色へ変換してPNG画像を返します。通常は、その新しいPNGをサーバーのディスクへ何百枚も書き出す方式ではありません。ブラウザは表示中の時刻の前後1枚を明示的に先読みし、移動先の表示を助けます。通信のキャッシュは、これとは別にブラウザが管理します。

一方、地点を押したときに知りたいのは「その地点の数字」です。色付き画像の画素を拾い、色から元の量を逆算するのではありません。選んだ緯度・経度と時刻をAPIへ送り、格子データから対応するセルの値を読んで、数値として返します。

画像は全体の広がりを読み取るため、地点値は気になる場所を確認するために使います。どちらも同じ元データと条件に対応させますが、返す形が違います。また、地点値は格子のセルに対応するため、住所の建物一つひとつを精密に計算した値という意味ではありません。計算範囲外なら、そのことを伝えます。

コードを読み始める人向けに、代表的な窓口も載せておきます。丸暗記する必要はありません。「登録」「状態」「案内書」「画像」「数値」と、依頼が分かれていることを見てください。

依頼の内容 HAIZUのAPI例
条件の登録 POST /api/simulations
処理状態の確認 GET /api/simulations/{id}
表示の案内書 GET /api/simulations/{id}/manifest
時刻ごとの画像 GET /api/simulations/{id}/frames/{frame}.png
地点と時刻の数値 GET /api/simulations/{id}/value(地点と時刻を付けて依頼)

画面を配る仕組みと、デモ・公式情報の経路も区別する

Web版を開いた直後は、まずブラウザが画面を動かすためのファイルを受け取ります。公開用に組み立てたHTMLやJavaScriptなどはnginxが配り、APIへの依頼はGo側へ中継します。その手前ではCaddyがHTTPSの接続などを担当します。HAIZUの通常運用では、画面表示のためのNode.jsサーバーを常駐させる構成にはしていません。

ここも「画面を配る」と「結果の要求に答える」が違う仕事だと分かれば十分です。Dockerは、こうしたプログラムを動かす環境をまとめるために使っています。公開と運用の細かい設定は、第10回で扱います。

ただし、画面が表示できたからといって、実計算の結果が届いているとは限りません。HAIZUの初期表示は、操作体験用のデモです。APIが利用できないときにもデモとして操作できる仕組みがありますが、ダミーの分布には科学的な根拠がありません。条件を更新して取得した実計算の結果と、明確に区別する必要があります。

公式ハザードは、さらに別の情報です。公式の公開資料を事前に地図表示用のGeoJSONへ変換してあり、その地物をブラウザで読み、塗りつぶして表示します。Tephra2の計算で作り直しているものではありません。

HAIZUの公式ハザード表示。公式の降灰可能性マップを紫系で表示し出典を記載
キャプチャ3:2026年10月14日の公式ハザード画面。これはTephra2の結果を色替えした表示ではなく、公開された公式資料を地図用データに変換して重ねたものです。

背景地図も、降灰の計算結果とは別のデータです。国土地理院などの外部配信を利用するため、「画面のプログラムがある」「APIが動く」「背景地図を取得できる」は、それぞれ確認する必要があります。この区別があると、地図だけ出ない、数値だけ取れない、といった問題の調べ方も絞れます。

役割を分けておくと、実装も不具合の確認も進めやすい

ReactやGoなどの名前を覚えることよりも、「何を受け取り、何を返す係か」を説明できることが、全体構成を理解する近道になります。

たとえば、地図の背景は動くのに降灰画像だけ出ない場合は、結果の案内や画像の取得を調べます。画像は出るのに地点値だけ取れないなら、地点の範囲や数値APIの応答を確認します。新しい条件の結果が準備できないなら、条件別データと処理状態に目を向けます。これは不具合の原因を断定する手順ではなく、担当する係を手がかりに調べる範囲を狭める考え方です。

また、機能を直すときには、その境界をまたぐ影響を見ます。地図画像の表示位置を変えるなら、案内書の座標との対応が必要です。計算モデルを変えるなら、既存の格子データやキャッシュとの互換性も考えます。画面だけ、計算だけが正しそうでも、つなぎ目が合っていなければ意図した結果にはなりません。

公開した第3回では、そのつなぎ目の一つである「地図に火山灰の広がりを重ねるには?」を扱っています。背景地図と降灰画像、座標、色と透明度の関係を、図解と実画面で説明しています。

HAIZU開発シリーズ
紹介・第0回:完成したHAIZUの画面と開発ログ
第1回:アプリを作る前に、何を決めたのか?
第2回:画面・サーバー・計算係――アプリ全体の仕組み(この記事)
第3回:地図に火山灰の広がりを重ねるには?座標・色・透明度を図解
第4回:スマホでも地図を見やすくするには?折りたたみと操作画面の設計

HAIZUをブラウザで開くと、実際の操作画面を確認できます。モデルの前提は、使い方とシミュレーションの前提を参照してください。

本記事は2026年10月17日に確認した実装・設定資料に基づきます。キャプチャは2026年10月14日の富士山版を再掲し、構成図は本記事用に作成しました。開発中の複数火山対応や広告連携の詳細は、今回の通常表示の説明とは分けて扱います。

参考情報

kobashinのプロフィール画像

WRITTEN BY

kobashin

個人事業のシステムエンジニア。開発の実務と、AI・身体ケア・家庭菜園の実践を行き来しながら、試してわかったことを記録しています。

運営者情報を読む ↗