これは FOSS4G Advent Calendar 2025 の一日目の記事です。
はじめに
過去に国土地理院の 標高タイル を Leaflet の地形表現に応用したり、maplibre-gl-js 用の terrain を生成してみたり、といった試行をしてきました。
ただ、新しい地図ライブラリへの移植や、既存の地図ライブラリのバージョンアップへの追従はなかなか大変です。
もし ServiceWorker で地形表現用の傾斜・曲率等の PNG タイルを動的に生成できれば、地図ライブラリに依存しない形で実装を維持できるのでは?
ということで、やってみました。
デザイン
通常の dem_png はこのような URL でアクセスしますが、
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png
次のようにクエリーパラメータを付与すると hillshade (陰影起伏図) が返ってくるとしたら?
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=hillshade
もちろん、地理院タイルのサーバーサイドでこのような処理をしてくれるなんてことはありませんが、ServiceWorker を使えばローカルでこのような処理を実現することが可能です。
このようなバリエーションを生成する ServiceWorker が実装できました。
左上から順番に次のような URL を使用することで効果が得られます。
# elevation : 標高値に応じたグラデーション
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=elevation
# hillshade : 任意光源からの陰影
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=hillshade
# slope : 傾斜量
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=slope
# curvature : 曲率
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=curvature
# contour : 等高線
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=contour
# mapbox : mapbox terrain PNG
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=mapbox
また、たとえば以下のようにパラメータを追加することでスタイリングを変更できます。
# elevation : 標高値に応じて緑→黄→赤のグラデーション、無効値は青に
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=elevation&colors=0a0-aa0-a00&fallback=00a
# hillshade : 光源を北西にして、影を強く
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=hillshade&dir=315&high=60
# curvature : 凹を青、凸を赤に/標高値を 20倍 して強調
https://cyberjapandata.gsi.go.jp/xyz/dem_png/9/453/202.png?type=curvature&colors=00f-fff-f00&gain=20
実際に使用できるパラメータも含め、仕様や使い方は以下の GitHub レポジトリにて取りまとめています。ライセンスは MIT です。
dempng-worker を使用したレイヤーを使う際には 「地理院タイルを dempng-worker で加工」 といったように、ソースデータを加工したことが明確になるようにクレジットを設定してください。
dempng-worker の出力する傾斜量や曲率の値はウェブブラウザ上での可視化を目的とした簡易的な計算によるもので、厳密なものではありません。厳密な数値を必要とする方は別のソリューションをご検討ください。
デモ&ギャラリー
以下、スクリーンショットにはデモへのリンクを設定しています
陰影段彩等高線図
上は Leaflet / 下は maplibre-gl-js。地図タイルを追加できるような Web 地図ライブラリであれば基本的になんでも使用可能なはずです。
赤色立体地図風
赤色立体地図公式サイト の情報を見ながら curvature と slope をオーバーレイ。パラメータを細かく詰めていけば、本家に近づけるかも?
CS立体図風
CS 立体図で見るリアス式海岸。curvature (凹青~凸赤) と slope (透明黒~不透明黒) のオーバーレイ。このデモに限り dem1a_png を使っているので、1m DEM 相当の高解像度版です。
ちなみに、slope は最大で z=17 のDEM、 curvature は最大で z=15 のDEMから生成するようにスタイルを書いているので、curvature には疑似的な平滑化処理っぽい効果が発生しています。
ミニチュア風
地理院タイル(淡色地図) に hillshade をオーバーレイ。色指定は透明黒→不透明黒のアルファグラデーション。ミニチュア風の効果がかかっているように見えて個人的に好き。
2017年 frogcat スタイル
こちらの記事のスタイルの再現を試みたもの。
都市部用に調整した CS 立体図
中目黒から渋谷方向
国分寺崖線の二子玉川あたり
いずれも起伏の激しい山岳用のパラメータだとほぼフラットにしか見えなくなってしまうのですが、高さを強調したり/着色するレンジを絞り込んだりするチューニングによって凹凸がはっきりと確認できるようになっています。
クエリーパラメータの調整だけで適用先を開拓できるのはこの手法のメリットです。
技術的メモ
画像生成技術
今回の実装では WebGL は使用していません。取得した PNG を OffscreenCanvas に描きこみ、 ImageData を取得、 RGBA 配列に対する演算結果を Canvas に描き戻し、Blob (PNG) を生成するという方式です。
WebGL による高速化の余地はあるかもしれませんが、以下は懸念材料です。
- 生成する画像が 256x256 と比較的小さく、 WebGL / JS でさほど性能差が出ないかも
- curvature, slope, hillshade 等複数の種別対応して、複数の WebGL Context が必要に?
- タイル画像を次々と WebGL に渡すことになるので転送コスト大きいかも
タイルの継ぎ目問題
elevation や mapbox のように、1ピクセルの値だけで色変換ができる場合はよいのですが、curvature や hillshade などは隣接ピクセルの値を畳み込み演算する処理が発生します。単純に1タイルだけ持ってくると、タイルの周辺部分は隣接ピクセルがないために値が計算できなかったり、補完した場合でもなんらか不自然になります。
これを回避するためには、中心1タイルに加えて周囲の8タイルを取得してやればよいのですが、今回は実際にそのように実装しています。なので継ぎ目が気になることはないと思います。
性能問題
ServiceWorker の実装では、地理院タイルを取得してくる部分は以下のような簡単なキャッシュを導入しています。
すでに当該 URL への HTTP アクセスが発生している間であれば、その Promise が共有される仕組みです。このキャッシュは HTTP アクセスが完了すると削除されますが、以降はブラウザのディスクキャッシュ等の利用が期待できます。
「1タイルに対して周囲8タイルも取得しないといけない」ような場合、まじめにすべて fetch すると HTTP アクセスがボトルネックとなってしまいますが、このような処理を挟むことと(とりあえず手元では)それなりの性能で動作しているように感じられます。
また、今回のようなレイヤー生成を、たとえば maplibre-gl-js の addProtocol で実装することもできるわけですが、これはブラウザのメインスレッドで動作するためボトルネックになりがちです。ServiceWorker はブラウザのメインスレッドとは異なるバックグラウンドスレッドで動作するという特徴があり、ブラウザのメインの処理を圧迫しません。この点でもこの方式の優位性があると思っています。
MapLibre Style Spec
実際にデモで使用している MapLibre Style Spec の一例、こちらは dem1a_png を使った CS立体図のものです。
こちらは当然 maplibre-gl-js のために用意しているのですが、maplibre-gl-js 以外では使えない、ということはありません。今回のデモはすべて Leaflet でも見られるようにしているのですが、style json をパースして L.TileLayer を add するようなことをしています。
Maplibre Style Spec を他の地図ライブラリに読み込ませるプラグイン、みたいなものがあれば、スタイル定義の流用が可能になるかもしれませんね。
おわりに
ServiceWorker を使った、地図ライブラリに依存しない標高PNGの動的加工の手法を紹介しました。
今年3月の 1mメッシュ(標高)の提供範囲を一挙に拡大 によって、来年は dem1a_png も提供範囲が拡大するのではないでしょうか?しかし、z=17 の地図タイルを個人的に取得・変換・維持していくのはなかなかハードルが高いです。今回紹介した手法が、そのような問題に対する処方箋の一つになることを期待しています。
あとは単純に興味本位ですが、いろいろと面白い表現ができそうな予感がしているので、スタイリングを容易に試せるスタジオ的な GUI も用意できればいいなと思っています。










