前回の記事《雪:更快速更流畅的气象数据动画》(中国語)では、予報変数を単一ファイルのコンテナ .xue にまとめ、ブラウザがインデックスを使ってフレームごとに読み込んでいた。 GFS の同じ時次の 2 変数について、フレームごとに着色済みの PMTiles(zoom 0–4)は合計 3,205.87 MB、元の GRIB2 は 137.73 MB、 .xue では約 65 MB まで小さくなった。サイズの問題は解決した。独自コンテナの問題は、自前のデコーダでしか読めないことにある。

2026-09-14、このフォーマットを Pangeo のフォーラムに投稿した。最初の返信は Tom Nicholas からで、質問はこうだった:性能を生んでいるのはこのバイナリフォーマットではなく、可視化に向いた Zarr の chunk 形状を選び、ブラウザ側で WASM デコードすれば足りるのではないか。

答えはその通りである。高速化は二つの要素から来ている。uint8 の単一バイト平面への量子化(圧縮前で 4 分の 1、デコード結果をそのまま WebGL2 の R8 テクスチャとしてアップロードできる)と、再生に合わせて切った chunk である。どちらも標準の Zarr v3 で表現できる。

2026-09-15 から .xue はオンラインでの公開をやめた。すべての時次、ローリングウィンドウの各ラウンド、過去事例はすべて Zarr v3 store のみで公開し、静的な STAC をデータの入口としている。 .xue の元のエンコード・デコードのコードは残してあり、今後エンコード方式を改良する際の参考にする。

現在のパイプラインは、データの流れに沿って次の段階に分かれる:

  1. 生成:GitHub Actions が定期的に上流データを取得し、量子化して Zarr store にエンコードし、公開する;
  2. カタログ:静的な STAC カタログが各データソースの現在の時次を列挙する;
  3. 配信:Cloudflare R2 にデータを置き、CDN がキャッシュし、クライアントは HTTP Range で必要な分を読む;
  4. 描画:ブラウザ内の Rust WebAssembly がデコードし、WebGL2 が投影と着色を行う;
  5. 分析:Python スクリプトが store を直接読み、synoptic-analysis Skill が天気概況レポートを生成する。

データソース

ソース格子と間隔
NOAA GFS0.25°、F120 まで毎時、以降 F240 まで 3 時間ごと
NOAA GFS Sflux約 13 km のガウス格子、時間軸は GFS と同じ
ECMWF IFS オープンデータ0.25°、F144 まで 3 時間ごと、F240 まで 6 時間ごと
ECMWF AIFS Single0.25°、F360 まで 6 時間ごと
ECMWF IFS HRES(Open-Meteo 経由)0.1°、F090 まで毎時、F144 まで 3 時間、F360 まで 6 時間、00Z と 12Z のみ
NOAA HRRR0.03° 米国本土、F18 まで毎時
NOAA GEFS-Aerosols0.25°、F120 まで 3 時間ごと
NCEP CFSv2地上 0.9375°、上空 1°、F6552(39 週)まで 6 時間ごと、00Z と 12Z のみ
NOAA MRMS レーダー0.02° 米国本土、2 分ごと、直近 4 時間
気象庁 高解像度降水ナウキャスト0.005° 日本、5 分ごと、直近 3 時間
CMA レーダー合成図0.0439° 中国、6 分ごと、直近 3 時間
Himawari-90.04°、10 分ごと、直近 6 時間
GOES-19 / GOES-180.04°、10 分ごと、直近 6 時間
Meteosat-120.04°、毎時、直近 24 時間

ほかに地点プロダクトが三つある:高層観測(1 地点 1 回の放球)、空港(METAR と TAF)、熱帯低気圧(嵐ごとの経路と各機関・各モデルの予報)。地点プロダクトは JSON で、地点または嵐ごとに Range で 1 行を取得し、ラスターのフォーマットには入らない。

データソースは二種類に分かれる。予報ソースは初期時刻ごとに公開され、1 時次 1 ディレクトリで、新しい時次が届くと古いものと置き換わる。観測ソース(レーダーと静止衛星)はローリングウィンドウで、数分ごとに 1 ラウンドをビルドし、ウィンドウを前に進め、取得済みのフレームは再ダウンロードしない。最初から最後まで同じ間隔のソースはないため、時間軸は単位と各フレームのオフセットを直接宣言し、等間隔を前提にしない。

ここでいうリアルタイムは、継続的に更新される予報と準リアルタイムの観測を指す:予報は初期時刻の 4–8 時間後に揃い、観測ウィンドウの末尾は上流の公開遅延に応じて、実時間から数分から 30 分ほど遅れる。

ソースごとの違いは取得段階で吸収する。静止衛星は静止衛星投影から緯度経度格子へ、IFS HRES は縮約ガウス格子から、HRRR はランベルト正角円錐図法の格子から、いずれも取得側で一度だけ再投影・再サンプリングする。その後の量子化、グループ化、store の書き出しはすべてのソースで同じ経路を通る。

MRMS を例にとると、プロダクトは 2 分ごとに 1 フレームを出すが、レーダーのボリュームスキャンは 1 周 4–6 分かかるため、新しい低仰角スキャンが届くまで、そのレーダーの範囲内では前のフレームが値ごとそのまま繰り返される。見た目としては、フレームレートは高いのに合成図上のエコーの動きが途切れ途切れになる。エコーの色の塊は全体としては同じ方向に動いているが、細部ではばらばらに動き、飛び飛びに見える。

フォーマットと量子化

アクセスのパターンは、全球予報 1 時次分の連続再生とタイムラインの自由なシークであり、この制約がフォーマットの選択を決めている。

各変数は 1 バイトに量子化する:2 m 気温は −60 から 50 °C を 0.5 °C 刻みで表し、最大誤差は 0.25 °C;降水は対数のコードブックで上限 128 mm/h とし、弱い雨の段階を保つ。量子化後もデータは数値としての意味を保つので、ブラウザは着色して再生するだけでなく、格子点の値を読んだりモデルを比較したりできる。

データはモデルの元の格子のまま置き、再投影も事前着色もしない。投影とカラーパレットは GPU に任せる。時間方向は 6 フレームで 1 グループとし、なめらかな場はグループ内でフレーム間差分をとり、降水は元の値のまま保存する。降水域は気象システムとともに移動するため、フレーム間差分のほうがかえって大きくなるからである。空間方向はタイルに切り、タイルの大きさはソースごとに設定する(GFS と ECMWF は 52 × 48)。ブラウザはビューポートが覆うタイルだけを取得する。

レイアウト

1 つの bundle は 1 つの Zarr group で、その中に変数ごとに uint8 の配列が 1 つずつあり、形状は [フレーム, 緯度, 経度]。ほかに時間・緯度・経度の三つの座標配列がある。内側の chunk は 6 フレーム × 1 タイルで、chunk ごとに独立した Zstandard フレームになっている。

配列ごとに shard は 1 つだけで、時間軸全体と格子全体を覆うため、1 つの配列がバケット上の 1 オブジェクトになる。スカラーの store は、フレーム数によらず 9 オブジェクトで、GFS の 1 時次は約 1,200 オブジェクトである。 shard のインデックスはオブジェクトの末尾にあり、クライアントは HTTP のサフィックス range 1 回でそれを取得し、以降のすべてのフレームとビューポートはそのインデックスで位置を特定する。

公開する codec チェーンは標準の [bytes, zstd] で、どの Zarr クライアントもプラグインなしで読める。 store には bundle のメタデータと CF の scale_factor / add_offset が含まれ、xarray.open_zarr で読むとそのまま物理量になる。

カタログ

各時次で manifest.json を書き、その横に派生した STAC 1.1.0 カタログを置く:ルートの Catalog の下に、データソースごとに 1 つの Collection、時次ごとに 1 つの Item、ローリングウィンドウはラウンドごとに 1 つの Item、地点プロダクトも同じ三層構成になっている。カタログは manifest とソースのレジストリから生成し、タイムスタンプもホスト名も含まないので、どの文書もディスク上の成果物から再生成できる。リンクはすべて相対パスである。

バケットには各ソースの最新時次だけを残すため、各 Collection には現在の時次を指す固定パスの item.json がある。

読む側では既存のツールを使う:pystac がカタログをたどり、xpystac / odc-stac が Item の Zarr アセットを xarray で開く。 group の文書にはインラインの consolidated metadata が入っているので、ディレクトリ一覧を取得できない素の HTTP バケットでも xr.open_zarr(url) でそのまま開ける。

配信

サイトは Cloudflare Pages 上の静的ページで、データは公開 R2 バケットにあり、間に CDN がある。API 層はない。 R2 の送信トラフィックもエッジでのヒットも無料なので、リクエスト数の多寡は主に遅延に影響し、請求額にはほとんど影響しない。

クライアントは必要な chunk を HTTP Range で読み、同じオブジェクト内の隣接する range は 1 回のリクエストにまとめる。全球表示の 1 フレームは時間ブロックごとに 1 リクエスト、ビューポートはタイル行ごとに 1 リクエスト、1 格子点の時系列全体は時間ブロックごとに 1 chunk である。再生位置の前後のフレームは、バイト数で予算を決めたキャッシュに先読みする。

新しい時次を公開する前にまず CDN をウォームアップし、その後でポインタを切り替える。最初の訪問者もエッジキャッシュから配信される。

計算はビルド段階に集中させ、オンラインには静的ファイルしかない。空間集計や時次をまたぐ連結といった計算は、エンコーダがあらかじめ成果物として計算しておく。

描画

ブラウザ内では Rust WebAssembly の worker が必要に応じて chunk をデコードし、 WebGL2 のフラグメントシェーダがピクセルごとに逆 Web Mercator 投影を行ってからカラーパレットを引く。タイルは格子空間の矩形なので、平面へ戻すのは texSubImage2D 1 回で済み、まだ届いていないタイルはシェーダ内でスキップする。隣接する 2 フレームは GPU 上でブレンドされ、再生時の遷移は連続的になる。

等値線は同じシェーダ内で描き、2 回目の描画パスは要らない。風は GPU のパーティクルレイヤーで描く。オプションとして WebCodecs の H.264 経路(?use_h264=true)があり、同じコード値を可逆圧縮の動画で運ぶため、ファイルが小さくなる。

分析

データはオープンな CORS の静的ファイルで、キーは不要であり、ラップされた API を経由せずに読める。リポジトリには synoptic-analysis Skill が含まれており、 Python を実行できる AI アシスタントと組み合わせると、STAC カタログから実データを読み、天気概況を分析し、レポートを書く。例えば次のように依頼できる:

東京の今後 3 日間の天気概況を分析し、GFS と ECMWF の降水と風の予報を比較し、最新の衛星、レーダー、高層観測のデータも合わせて、図、データの時刻、不確実性の説明を含む日本語のレポートを作成してください。

Skill の作業手順は、領域と有効時刻を決め、モデルの場を読み、上層のトラフとリッジ、ジェット気流、地上の気圧系、水蒸気輸送、降水の条件を分析し、観測でモデルの挙動を確かめ、モデル間の違いを比較する、というものである。 1 回の分析で使うデータはまずローカルに保存し、それを計算、作図、検証に使うので、レポート内の数値と図は同じデータに基づく。対象領域、分析の重点、レポートの言語、出力形式はいずれも変更できる。

実際に作成したレポートの例として、台風 Dujuan が東京に影響を及ぼす直前の解析がある:東京都台風気象過程解析(PDF)。作成の経緯は《LLM による複数データソースの気象解析》を参照。

セルフホスティング

独立して運用する場合は、上流のオープンデータを選び、対応する処理パイプラインを動かす。ローカルでは 1 つのコマンドで 1 時次をビルドできる:

1
2
make mvp MODEL=gfs    # ecmwf / aifs / ifshres / sflux / hrrr / gefsaero / cfs も可
make serve

依存関係は Python、GRIB ドライバ付きの GDAL、Node.js で、WASM デコーダのビルドには Rust ツールチェーンが要る。フロントエンドのデータの URL、R2 バケット、パスはいずれも設定できる。別のオブジェクトストレージや社内ネットワークへ移す場合は、公開フロー、ネットワークアクセス、権限設定を変更する必要があるが、フォーマット、カタログ、デコーダは変わらない。

デプロイ後に調整できる範囲には、データ(1 つのモデルといくつかのよく使う変数から始め、必要に応じて広げる)、画面(配色、レイヤー、特定の地域向けの構成)、分析(Skill の分析ロジックとレポート形式)がある。

コードは Apache 2.0 OR MIT のデュアルライセンスで、どちらか一方を選べる。いずれも商用利用、改変、クローズドソースでの再配布を認め、著作権表示とライセンス表示の保持だけを求める。データのライセンスはソースごとに異なる:NOAA のプロダクトはパブリックドメイン、ECMWF のオープンデータと Open-Meteo 経由の IFS HRES は CC BY 4.0 でクレジット表示が必要、ベースマップは © OpenStreetMap contributors。