事前生成をやめた地形タイル — オンデマンドタイル生成という発想
2026-06-17
株式会社Eukarya で CTO の井上です。このたび、地形データをオンデマンドで配信する地形配信サービス Re:Earth Terrain を正式にリリースしました(あわせて姉妹サービスの Re:Earth Buildings も公開しています)。いずれも OSS で、コードは GitHub(reearth/reearth-terrain・reearth/reearth-buildings)で公開しています。
Re:Earth Terrain は、地形タイルをあらかじめ用意しておくのではなく、 リクエストが届くたびにその場で生成して返す 、という方式の地形配信サービスです。私たちは、Web 上で 3D を含む地理空間情報(GIS)を扱うためのプロダクト群「Re:Earth」を OSS として開発・運営しており、本サービスもその一部です。
本記事では、この「リクエストのたびに生成する」という発想にたどり着いた経緯から、実装の中身までを順に紹介します。
地形タイルは「事前生成しておくもの」だった
Web で地図を表示するとき、地図のデータはふつう タイル という単位に切り分けて配信します。世界全体を、拡大率(ズーム)ごとに格子状の小さな正方形に分け、それぞれに「ズーム z・列 x・行 y」という番地を振ったものがタイルです。ブラウザは、いま画面に映っている範囲とズームに必要なタイルだけを、その番地で取りに行きます。元データは巨大なので丸ごとは送らず、必要な区画だけを小分けにして送る——これがタイルという考え方です。
そのため、配信の前に元データをタイルへ変換しておくのが一般的です。たとえばベクトルデータなら、tippecanoe のようなツールで巨大な GeoJSON を、あらかじめズーム別・区画別のタイル集合に変換しておきます。私たちが普段見ている地図のベースマップ(背景地図)も、たいていは事前生成されたタイルです。
地形(terrain)も、同じくタイルで配信します。地形タイルは、標高データ(DEM)をもとに、地表の凹凸を 3D の起伏として立ち上げるためのデータです。ただし地形のタイル変換は、ベクトルや画像のタイル化よりも重い処理になります。標高データを集め、それを 3D のメッシュ(三角形の集まり)に変換し、全ズーム・全領域ぶんのタイルを生成し、ストレージに並べる。データを追加するたび、基準を差し替えるたびに、この変換を最初からやり直す。地形タイルとは、 配信の前に時間をかけて事前生成しておくもの でした。
私がずっと引っかかっていたのは、この「あらかじめ全部」という部分です。実際に閲覧されるのはごく一部の領域・ズームでしかないのに、なぜすべてを先に生成して抱えておく必要があるのか。 リクエストが届いた瞬間に、そのとき必要なタイルだけを生成して返せばよいのではないか 。この発想を出発点に、地形タイルの生成をすべてリクエスト時に行う方式へ切り替えて作ったのが、今回紹介する Re:Earth Terrain(terrain.reearth.land)です。
事前生成をやめると、運用が大きく変わります。全タイルを先に変換・保管するという重い工程がなくなり、 上流のデータを差し替えれば、次にそのタイルが要求されたときから新しい内容が反映されます 。タイルを作り直すバッチ処理を管理する必要もありません。本記事では、この「リクエストのたびに生成する」方式を、実運用に耐えるサービスとして成立させるために何を作ったかを説明します。
Re:Earth Terrain が返すもの — 用途に応じて高さの基準を選べる地形
仕組みの前に、何を配信しているのかを軽く押さえておきます。Re:Earth Terrain が配信するのは、 地形の標高——地表の凹凸のデータ です。これを、Cesium が読み込む quantized-mesh や、MapLibre などが読み込むラスタ形式(いずれも後述します)で返します。
特徴のひとつは、 高さの「基準」を用途に応じて選べる ことです。3D 地球儀(Cesium や three.js)は地球を WGS84 楕円体という回転楕円体として描き、その表面を高さゼロとします。一方、DEM が持つ標高はふつう海抜(平均海面が基準)です。この2つの基準は場所によって数十メートルずれるため(日本では富士山あたりで約 37m)、海抜のまま地球儀に積むと、地形が浮いたり沈んだりします。
そこで Re:Earth Terrain は、リクエストごとに高さの基準をそろえて返します。どの基準にするかは、URL のパラメータ(data_type)で選びます。
data_type | 返す高さ | 主な用途 |
|---|---|---|
ellipsoid | 楕円体高(DEM + ジオイド起伏) | Cesium などの 3D 地球儀に地形を載せる |
elevation | 正標高(海抜そのまま) | MapLibre、等高線、海面基準の重ね合わせ |
geoid | ジオイド起伏のみ | 座標変換、ジオイドの可視化 |
同じデータから、 基準だけを URL で切り替えられます 。たとえば PLATEAU のような 3D 都市プラットフォームで、地球儀に正しい高さで地形を載せる場面で、そのまま利用できます。
Cesium から使うときのコードは、これだけです。
import * as Cesium from "cesium";
const terrain = await Cesium.CesiumTerrainProvider.fromUrl(
"https://terrain.reearth.land/cesium-mesh/ellipsoid",
{
requestVertexNormals: true, // 陰影用の法線も要求する
requestWaterMask: true, // 水面のマスクも要求する
},
);
const viewer = new Cesium.Viewer("cesium", { terrainProvider: terrain });
viewer.scene.globe.enableLighting = true;
fromUrl が、指定した URL にある layer.json(タイルのメタ情報を記したファイル)を読み込み、タイルの URL の組み立て方・利用可能なズーム範囲・対応している拡張を自動的に把握してくれます。
リクエストのたびにタイルを生成する仕組み
ここからが本題です。前提として、Re:Earth Terrain は Cloudflare というインフラの上で動いています。Cloudflare は、世界中に拠点を持つ CDN(コンテンツ配信網)の事業者です。本記事に関係するのはそのうち2つで、ひとつは Workers(各拠点でプログラムを実行できる仕組み)、もうひとつは R2(オブジェクトストレージ)です。
Re:Earth Terrain は、この Worker ひとつと R2 だけで動作 します。専用のタイルサーバー群も、事前変換のバッチ処理もありません。
タイルが要求されてから返るまでの流れは、次のとおりです。
- リクエストを解釈する。 URL から、タイルの位置(z / x / y)、出力フォーマット、高さの基準を読み取ります。
- 必要な上流データだけを取得する。 Re:Earth Terrain は、必要なデータを自前で抱え込まず、その都度、外部のオープンデータから必要な範囲だけを読み出します。標高データには Mapterhorn(世界の標高をまとめたオープンな DEM データセット)を、水面の判定には Protomaps(OpenStreetMap を元にしたオープンな地図データ。範囲指定で部分的に読み出せる単一ファイル形式 PMTiles で配布されています)を使い、いずれも HTTPS の範囲リクエスト(Range GET)で、そのタイルに必要な部分だけを読み出します。 データ全体を Re:Earth Terrain 側にコピーすることはしません 。例外はジオイドモデルの EGM2008 で、これは全体でも十分小さい(部分読み出しできる GeoTIFF が1ファイル)ので、R2 に置いておき、そこから読み出します。
- 数値処理を WASM で行う。 取得した標高のグリッドを組み立て、必要な基準に合わせてジオイド起伏を足し、メッシュに変換し、指定されたフォーマットに符号化します(この部分の詳細は後述します)。
- キャッシュに格納して返す。 生成したタイルをキャッシュに保存してから返します。次に同じタイルが要求されたときは、生成せずキャッシュから返します。
要点は、 生成済みのタイルをあらかじめ用意しておかない ことです。上流の DEM が更新されれば、次にそのタイルが要求されたときに、新しいデータで自動的に作り直されます。私たちの側でタイルを再生成する作業は発生しません。
数値処理は Rust/WASM、入出力は TypeScript
Worker 本体は TypeScript で書き、計算量の多い数値処理は Rust を WebAssembly(WASM)にコンパイルして担わせています。この2つの役割分担は、 扱うテーマではなく「能力」で分けています 。
- TypeScript 側 は、ランタイムや外部リソースに触れる処理をすべて担当します。HTTP のルーティング、R2 やキャッシュへのアクセス、外部データの取得、外部 GeoTIFF のどの範囲を読むかの計画、ベクトルタイル(MVT)のデコード、後述する ETag の計算などです。
- Rust/WASM 側 は、外部に一切触れない純粋な数値処理だけを担当します。標高のピクセル符号化、画像のエンコード・デコード、メッシュの生成、quantized-mesh 形式へのシリアライズなどです。
TypeScript がバイト列を渡し、バイト列を受け取る。WASM 側は外部リソースの存在を知らない。この境界をはっきりさせることで、数値処理だけを切り出してテストでき、移植もしやすくなります。
出力フォーマット — 同じ標高データから3つの形式へ出し分ける
具体的な生成手順に入る前に、何を出力するのかを整理しておきます。Re:Earth Terrain は、同じ標高データから、利用するライブラリが既に対応している形式へ出し分けます。
- quantized-mesh-1.0 — Cesium が読み込める地形メッシュのタイル形式です。
- Mapbox Terrain-RGB / Mapzen Terrarium — MapLibre・Mapbox GL・deck.gl などが読み込める形式です。標高の値を色(RGB)に符号化したラスタタイルです。
- 水面マスク — Protomaps から得た水面の範囲を、地形メッシュに添付する(Cesium の拡張)か、単体のラスタタイルとして配信します。
メッシュ(quantized-mesh)とラスタ(Terrarium・Mapbox Terrain-RGB)では、途中まで同じ標高グリッドを使い、最後の段で形式が分かれます。次にその生成手順を追います。
ラスタを合成し、メッシュとして立ち上げる
ここが Re:Earth Terrain の中心です。 取得したデータが、どんな順番で 1 枚の地形メッシュになっていくのかを追ってみます。メッシュ生成とエンコードには、私たちが OSS として公開しているライブラリ群 terrain-codec を使っています。
まず、ラスタを合成して 1 枚の標高グリッドにします。
- DEM をデコードする。 Mapterhorn の DEM タイルは、標高を色(RGB)に符号化したラスタです。これをデコードして、各ピクセルが正標高(メートル)を持つグリッドに戻します。要求されたズームのタイルが上流に存在しない地域では、親タイルまでさかのぼって該当範囲を切り出し・拡大することで、データ欠損による不自然な段差を防ぎます。
- ジオイドを読み出す。 同じタイル範囲・同じ画素サイズで、EGM2008 のジオイド起伏をバイリニア補間で読み出します。
- 足し合わせる。 DEM とジオイドを同じ格子にそろえ、ピクセルごとに「正標高 + ジオイド起伏」を足し、楕円体高のグリッドにします(
ellipsoidの場合。elevationは DEM のみ、geoidはジオイド起伏のみ)。
ラスタ形式(Terrarium・Mapbox Terrain-RGB)で配信する場合は、このグリッドをそのまま色に符号化して返します。メッシュ(quantized-mesh)で配信する場合は、ここから terrain-codec に進みます。
次に、標高グリッドをメッシュとして立ち上げます。
- メッシュ用のグリッドを用意する。 メッシュ生成に使う MARTINI は、一辺が 2ⁿ+1 の正方グリッドを必要とします。Re:Earth Terrain では 65×65(=2⁶+1)の標高グリッドを使います。タイルの境界がずれて地球儀に割れ目ができないよう、東辺・南辺には隣のタイルの 1 列ぶんを含めて読み込みます。
- 誤差のピラミッドを作る(RTIN)。 MARTINI は、グリッド上で考えられるすべての直角二等辺三角形について、小さいものから順に「長辺の中点を省いたとき、両端から線形補間した高さと、実際の高さがどれだけずれるか(誤差)」を計算します。大きい三角形は、子三角形の誤差も引き継ぎます。こうして各格子点に「そこを省略すると生じる最大の誤差」が記録されます。これが RTIN(Right-Triangulated Irregular Network)の核です。
- しきい値に従ってメッシュを構築する。 大きな 2 つの三角形から再帰的に分割していき、中点の誤差が許容誤差(
max_error。ズームに応じて決めます)を超える三角形だけを、さらに半分に割ります。結果として、 起伏の大きい場所は細かい三角形に、平坦な場所は粗い三角形になります 。完全に平坦なタイルは、4 隅だけの 2 三角形にまで潰れます。これが、平坦な部分の頂点を無駄に増やさない適応的なメッシュです(アルゴリズムのより詳しい解説は、別記事「リアルタイムに階層的な LOD を構築する RTIN とは」をご覧ください)。 - 各頂点の高さを取り直す。 MARTINI は誤差ピラミッドを作り終えると、高さの値そのものは保持しません。そこで、メッシュに残った各頂点の位置(u, v)から格子座標を復元し、手順 3 の標高グリッドから高さを読み直して、頂点に与えます。
- 量子化する。 各頂点の (u, v, 高さ) を 0〜32767 の整数に量子化します。高さは、そのタイルのメッシュ頂点の最小〜最大の範囲で正規化します(範囲はタイルごとに異なり、平坦なタイルでは範囲がゼロに潰れます)。
- ヘッダ・境界・法線を付けてエンコードする。 地球儀の遠景で「このタイルが地平線の裏に隠れているか」を判定するための水平線オクルージョン点を、メッシュ頂点から計算してヘッダに入れます。さらに、タイルの東西南北の縁にある頂点を列挙し、必要であれば法線(陰影用)と水面マスクを添えて、quantized-mesh-1.0 のバイト列に書き出し、gzip で圧縮します。法線は Cesium の
octvertexnormals拡張として持たせますが、タイル内だけで計算すると、隣のタイルとの境界で陰影が途切れがちです。そこで、手順 4 で読み込んだ halo(隣接タイルの縁取り)から傾きをもとに法線を計算し、隣り合うタイルの陰影が境界でなめらかにつながるようにしています。
手順 4〜9 は、terrain-codec の encode_terrain がひとまとめに行います。標高グリッドを渡すと、MARTINI でのメッシュ化・量子化・ヘッダ構築・法線計算・エンコードまでを通して実行し、.terrain のバイト列を返します。
キャッシュと無効化 — オンデマンドを実運用で成立させる仕組み
リクエストのたびに生成する方式で最初に気になるのは、「毎回その場で作るなら、遅く・高くつくのではないか」という点でしょう。これに対する答えは、 生成は最初の1回だけで、2回目以降はキャッシュから返す 、というものです。これを成立させるキャッシュの設計が、このサービスの中心になります。
Web のキャッシュと ETag
Web には、同じものを何度も取りに行かずに済ませる仕組みが組み込まれています。一度取得したデータは、ブラウザや、ユーザーに近い CDN の拠点にコピーが保持され、次回はそのコピーが使われます。これがキャッシュです。さらに「内容が変わっていないか」を確かめる条件付きリクエストという仕組みもあり、変わっていなければデータ本体を送り直さずに済ませられます。この判定に使われるのが ETag です。
ETag は、レスポンスの内容に対応づけられた短い識別子です。サーバーはタイルを返すときに ETag を付けます。クライアントは、次に同じタイルを要求するとき、If-None-Match ヘッダで前回受け取った ETag を一緒に送ります。サーバー側で内容が変わっていなければ、本体を送らずに「304 Not Modified(変わっていません)」とだけ返せばよく、転送量も処理も節約できます。
Re:Earth Terrain では、この ETag を、タイルの内容を決める要素(タイルセット名・バージョン・出力フォーマット・高さの基準・z / x / y など)から計算しています。つまり ETag は、そのタイルの「内容そのもの」に対応します。If-None-Match の値が一致すれば、キャッシュを参照するよりも・タイルを生成するよりも手前の段階で、すぐに 304 を返します。
サーバー側のキャッシュは2層です。1層目(L1)が Cloudflare の Cache API、2層目(L2)が R2 です。要求されたタイルが L1 にあればそれを、なければ L2 を見て、どちらにもなければ生成します。正確には、 この手前にブラウザのキャッシュもあります 。生成済みタイルには長めの cache-control(後述)を付けて返すので、同じタイルへの再アクセスは、そもそもサーバーまで届かずブラウザ内のキャッシュで完結することも多くあります。
更に、Cache API と Workers を組み合わせられるため、 キャッシュ戦略そのものを自分で設計できます 。Worker はリクエストを受けてからレスポンスを返すまでの全過程を自分のコードで書けるので、「どんなキーで保存し、どの層をどの順で見て、いつ無効化するか」を細かく決められます。とりわけ重要なのがキャッシュキーの設計で、次に述べる無効化の仕組みは、このキーをタイルの内容に応じて組み立てられるからこそ成り立っています。
内容が変わったタイルだけを古くする
オンデマンド方式の難しさは、上流データが更新されたときに、影響を受けたタイルだけを的確に古くする(無効化する)ことです。Re:Earth Terrain では、変化の速度が異なる3つの要因を、それぞれ別の仕組みで扱っています。
- 運用者の都合による全体の更新。 エンコーダを差し替えた、ジオイドモデルを入れ替えた、といった「すべてのタイルを一斉に古くしたい」変更です。これはバージョン番号を上げ、キャッシュキーの先頭(プレフィックス)を切り替えるだけで、古いプレフィックス配下のタイルがまとめて参照されなくなります。使われなくなった古いプレフィックスは、毎日の定期処理(Cron)で削除します。
- 上流データの更新(タイル単位)。 水面データのもとになる Protomaps は頻繁に作り直されますが、Re:Earth Terrain ではこれを週次で取り込んでいます。ここで更新日をキャッシュキーに入れてしまうと、更新のたびにすべての水面つきタイルがいっせいに無効化されてしまいます。そこで、そのタイルが実際に参照している部分(PMTiles 上のバイト範囲)から識別子を計算し、それをキャッシュキーに織り込みます。更新があっても、そのタイルが見ている範囲が変わっていなければ、キャッシュは生き残ります。
- 上流データの更新(地域単位)。 標高データ(Mapterhorn)は地域単位で作り直されます。キャッシュ上のタイルが一定時間(6時間)より古い場合だけ、上流に更新の有無を問い合わせ、実際に変わった地域のタイルだけを無効化します。大きな更新が来ても、全体を作り直すことはしません。
この3つを分けて扱うことで、「コードを変えたら全部」「水面が更新されたら影響したタイルだけ」「標高が更新されたらその地域だけ」を、 それぞれ過不足なく無効化できます 。
レイテンシ — 実測 初回 2.4 秒 / 2 回目 86 ミリ秒を実現
実際の応答時間を、まだリクエストの少ない遠隔地(米国ネバダ州の内陸)のタイルに対して計測した例を示します。日本から計測したもので、応答時間にはクライアントからエッジまでの往復時間を含みます。
| リクエスト | キャッシュ | 応答時間 |
|---|---|---|
| 地形タイル(quantized-mesh、初回・生成あり) | MISS | 約 2.42 秒 |
| 同じ地形タイル(2回目) | HIT(L1) | 約 0.09 秒(約 86 ミリ秒) |
初回はその場で生成するため約 2.4 秒かかります。これは上流データの範囲取得とメッシュ生成を含む値です。
見方を変えると、複数の上流からのデータ取得・デコード・ジオイド合成・メッシュ生成・エンコードという一連の処理を経て約 2.4 秒に収まっているのは、 むしろ速い部類です 。これを支えているのは主に 2 点です。1 つは、 上流データが部分取得できる形にすでになっている こと。DEM は XYZ タイルや PMTiles、水面は PMTiles、ジオイドは COG(部分読み出しできる GeoTIFF)として配布されているため、データ全体をダウンロードするのではなく、そのタイルに必要なバイト範囲だけを Range GET で取れます。もう 1 つは、 デコード・メッシュ化・エンコードといった重い数値処理を Rust/WASM で行っている ことです。この 2 点がなければ、その場生成は実用的な時間には収まりません。
同じタイルの 2 回目は、エッジの L1 キャッシュから約 86 ミリ秒で返りました(この値は日本からエッジまでの往復時間がほぼそのまま表れています)。
生成済みの地形タイルは cache-control: max-age=2592000(30 日)を付けて返します。この 30 日はあくまでエッジ(L1)の鮮度で、これが切れても、前述のとおりタイルは L2 の R2 に保存されているため、再生成は走らず R2 から返ります。再生成が必要になるのは、内容が無効化されたとき(上流データの更新やバージョン更新)だけです。つまり、 生成コストがかかるのは実質的に各タイルの初回だけ で、運用上はほとんどのリクエストがキャッシュ(L1 または R2)から返ります。
なぜ速いのに「安い」まで両立するのか — エッジ計算という前提の変化
ここまで「速さ」を見てきましたが、このサービスのもう一つの軸は「安さ」です。そして両者は、ここ数年のインフラの変化を抜きには語れません。
Web の世界では長らく、「動的な処理はオリジンサーバーで動かし、CDN はその結果を各地にキャッシュするだけ」という役割分担が当たり前でした。地形タイルを動的に配信しようとすれば、どこかのデータセンターにタイルサーバーを立て、その計算資源と、配信したデータ量に応じた転送料金(egress)を払う、という構図になります。地理空間データのように大量のバイトを世界中へ配信するサービスでは、この egress がコストの大きな部分を占めてきました。
ところが、そもそも「計算をどこで動かすか」は、この十数年で段階的に変わってきました。
はじめは、自前のサーバーや VM を特定のデータセンターに立て、自分で運用するのが当たり前でした。次に AWS Lambda や Google Cloud Run のようなサーバーレスが広まり、サーバーの管理そのものからは解放されました。ただしそこでも「どのリージョンで動かすか」は選ぶもので、ユーザーから遠いリージョンに置けば、その往復がそのまま遅延になります。
そして近年、エッジ計算(edge computing)が実用段階に入りました。Cloudflare Workers のようなエッジランタイムでは、コードは特定のリージョンではなく、世界中に分散した拠点のうち、リクエストを受けた場所の近くで動きます。CDN がこれまで静的ファイルをキャッシュしていたのと同じ拠点で、実際のプログラムが動く—— 「どのリージョンに置くか」を意識する必要がほぼなくなり、リージョンという概念が利用者から見えなくなった 、と言ってもよいと思います。Re:Earth Terrain は、この上で Rust を WASM にしたエンジンを走らせています。
コスト構造も大きく変わりました。Cloudflare のオブジェクトストレージ R2 は、 egress(転送料金)がかかりません 。大量のタイルを世界中に配るサービスにとって、これは決定的です。加えて、エッジで動く Worker はリクエストに応じた課金で、24 時間動かし続けるオリジンサーバーを抱える必要がありません。
Re:Earth Terrain は、この変化を最大限に使う形で設計しています。
- 常時稼働のオリジンサーバー群を持たず、計算はリクエストが来たときにユーザーに近いエッジで実行する。
- 世界中のタイルを事前生成して保管せず、実際に要求されたタイルだけを生成して、L1(エッジのキャッシュ)と R2(egress 無料)に置く。
- 配信されるバイトはエッジから返り、転送料金がかからない。
オーダー感も示しておきます(いずれも 2026 年時点の Cloudflare の公開料金より)。Cloudflare Workers の有料プランは月額 5 ドルからで、一定量のリクエストと実行時間が含まれます。R2 のストレージは 1GB あたり月 0.015 ドル程度、そして egress は無料です。生成済みタイルは基本的にキャッシュから返り、生成が走るのは各タイルの初回だけなので、本サービスのような使い方では、 月額はおおむね数ドル〜数十ドルのオーダーに収まります (トラフィックやキャッシュのヒット率によります)。配信量に比例して転送料金が膨らんだり、常時稼働のサーバー費用が積み上がっていく、という構造ではありません。
ユーザーに近いエッジで動くので速く、遊休サーバーも egress もないので安い。 先ほどのレイテンシ(初回 約 2.4 秒、キャッシュ後 約 86 ミリ秒)も、このエッジ上の構成があって初めて成立する数字です。「速いのに安い」は、オンデマンドという発想と、それを載せるエッジ計算という土台が噛み合って初めて実現できました。
同じ発想で建物も — 姉妹サービス:Re:Earth Buildings
「リクエストのたびにエッジで生成する」という考え方は、 地形だけのものではありません 。同じ考え方で動かしているのが Re:Earth Buildings です。Overture Maps(建物の輪郭などを集めたオープンな地図データ)の建物フットプリントから、3D 建物のタイル(3D Tiles 1.1)をリクエスト時に生成します。
建物の地面の高さには Re:Earth Terrain の楕円体高を使い、建物の基部が地形にぴたりと接するよう、形状に高さを焼き込んでいます。地形と建物が同じ高さの基準でそろう、というのが、この2つを一緒に開発している理由です。
建物で難しいのは「高さ」の決め方です。Overture が明示的な高さを持つのは OpenStreetMap 由来の一部だけで、機械学習で生成されたフットプリントは、たいてい高さの情報を持ちません。そこで、5段階の優先順位で高さを決め、どの方法で決めたかを各建物に記録しています。すなわち、(1) 明示的な高さ → (2) 階数 × 3メートル → (3) 建物の分類(class)ごとの目安 → (4) より細かい分類(subtype)ごとの目安 → (5) 床面積からの推定、の順です。最後の段では、都心の細長いビルが平屋のようにならないよう、その地域の建物密度に応じた補正を加えています。
この高さ推定がどの程度妥当かは、PLATEAU (日本のオープンな3D都市モデルデータセット)の実測データと突き合わせて検証・調整しています。オープンな建物データに対して、日本の精緻な 3D 都市モデルを物差しとして当てる、という形です。
おわりに:オープンなデータの上に、オープンなサービスを
地形タイルは長らく「配信の前に、全部まとめて作っておくもの」でした。実際に見られるのはごく一部の領域・ズームでしかないのに、すべてを先に生成して抱えておく——この重さに、私たちはずっと引っかかっていました。
そもそも Re:Earth Terrain を作ったのは、自分たちが必要としていたからです。Re:Earth で 3D の地球儀に地形を載せると、楕円体高と海抜のずれがそのまま地形の浮き沈みになって現れます。PLATEAU のような 3D 都市プラットフォームを正しい高さでそろえるには、用途に応じて基準を選べる地形配信が要る——その実用的な必要が出発点でした。そして「事前生成をやめ、リクエストのたびにエッジで生成する」という方針に切り替えたことで、その重さを手放し、上流の更新がそのまま反映される身軽な配信にできました。
無料で、しかも OSS として公開しているのには、はっきりした理由があります。Re:Earth Terrain も Re:Earth Buildings も、土台にしているデータがオープンだからこそ成り立っています(OpenStreetMap・Mapterhorn・EGM2008・Overture)。世界中の人が積み上げてきたオープンなデータの上に立っている以上、その上に作ったサービスもまたオープンであるべきだ——そう考えて、コードはいずれも MIT ライセンスで公開しました。受け取った分を、また誰かが積み上げられる形で返す。それが、オープンな土台に対する自然な振る舞いだと思っています。
私たちは Re:Earth を、行政・公共領域のためのオープンソースのデータプラットフォームとして開発しています。地形も建物も、その大きな絵の一部です。「事前に全部用意しておく」という前提を外し、オープンな土台の上にオープンなサービスを重ねていく——この考え方が地形・建物の先どこまで広がるのか、引き続き確かめていきます。
リファレンス
Re:Earth Terrain:https://terrain.reearth.land/
Re:Earth Terrain GitHub:https://github.com/reearth/reearth-terrain
Re:Earth Buildings:https://buildings.reearth.land/
Re:Earth Buildings GitHub:https://github.com/reearth/reearth-buildings
Eukaryaでは様々な職種で採用を行っています!OSSにコントリビュートしていただける皆様からの応募をお待ちしております!
Eukarya is hiring for various positions! We are looking forward to your application from everyone who can contribute to OSS!
Eukaryaは、Re:Earthと呼ばれるWebGISのSaaSの開発運営・研究開発を行っています。Web上で3Dを含むGIS(地図アプリの公開、データ管理、データ変換等)に関するあらゆる業務を完結できることを目指しています。ソースコードはほとんどOSSとしてGitHubで公開されています。
➔ Re:Earth / ➔ Eukarya / ➔ note / ➔ GitHub
Eukarya is developing and operating a WebGIS SaaS called Re:Earth. We aim to complete all GIS-related tasks including 3D (such as publishing map applications, data management, and data conversion) on the web. Most of the source code is published on GitHub as OSS.