Edited at
FOSS4GDay 19

Mapbox GL JSで色別標高図

Mapbox GL JSで、地理院地図の「自分で作る色別標高図」を実装してみる試みです。


自分で作る色別標高図とは

今年、地理院地図に追加された、自由に色を設定して色別標高図を作ることができるすごい機能です。

Leaflet.jsのTileLayerを拡張して、canvas上で標高に応じた色をつけるように実装されています。

同じことをMapbox GL JSで実現することを考えます。

ですが、現時点では色別標高図を作る機能はないため、自分で実装してみます。


地理院標高タイル

まず、地理院標高タイルをMapbox GL JSで扱えるようにします。

これは以前、Mapbox GL JSで地理院標高タイルという記事を書きました。


Relief Layer

hillshadeのように、色別標高図をRelief Layerとして追加できるようにします。


WebGLで標高に応じた色をつける


relief.fragment.glsl

#ifdef GL_ES

precision highp float;
#endif

uniform sampler2D u_image;
uniform sampler2D u_table;
varying vec2 v_pos;

uniform float u_color_len;
uniform float u_opacity;

float getElevation(sampler2D u_tex, vec2 coord, float bias) {
// Convert encoded elevation value to meters
vec4 data = texture2D(u_tex, coord) * 255.0;
return (data.r + data.g * 256.0 + data.b * 256.0 * 256.0) / 4.0;
}

float getIndex(float el) {
float prev;
for (float i = 0.0; i < 128.0; i++){
if (i >= u_color_len) return u_color_len;
float v = getElevation(u_table, vec2((i + 0.5) / u_color_len, 1), 0.0);
if (v >= el){
if (i == 0.0) return i;
return i + (el - prev) / (v - prev);
}
prev = v;
}
}

void main() {
float v = getElevation(u_image, v_pos, 0.0);
float i = getIndex(v);

vec4 color = texture2D(u_table, vec2(i / u_color_len, 0), 0.0);
gl_FragColor = v > 0.0 ? vec4(color.rgb, u_opacity) : vec4(0.0, 0.0, 0.0, 0.0);
}


ざっくりと説明すると、

1. WebGLで標高値getElevationに応じた階級getIndexを計算する

2. 設定色のTextureから、階級に応じた色を取得して返す

という感じです。

区分の標高値と設定色をTexture u_tableとして渡すところがポイントでしょうか。

このフラグメントシェーダーを実行するために、いろいろとコードを書きます。

https://github.com/tattii/mapbox-gl-js/pull/2

参考: Data-driven Raster Layer with Mapbox GL


Example



https://tattii.github.io/terrain-japan/relief/#9.06/35.511/139.1993

map.addLayer({

"id": "relief",
"source": "gsi-dem",
"type": "relief",
"paint": {
"relief-opacity": 0.7,
"relief-gradation": true,
"relief-colors": [
0, "#2db4b4",
100, "#71b42d",
300, "#b4a72d",
1000, "#b4562d",
2000, "#b4491b",
4000, "#b43d09",
null, "#b43d09"
]
}
}, 'waterway-river-canal-shadow');

relief-colorsで標高と色を指定します。この設定例で、自分で作る色別標高図の初期状態と同じです。


WIP: 自由に色を設定する

簡易的に、dat.guiを使ってリアルタイムに色の編集をできるようにしました。



https://tattii.github.io/terrain-japan/relief/color.html#8.85/35.352/139.1404

標高の編集や区分数を増やすことができるようなアプリケーションとして作成中です...。


Mapbox GL JSで実装してみて

描画が速いため、リアルタイムに色の編集をできるのが良いところです。

また、Mapbox GL JSの陰影起伏hillshadeは重ねても暗くなりません。

一方、Mapbox GL JSをforkして独自の拡張をしているため、本体のバージョンアップについていくのは大変です。


まとめ

WebGLは難しいです。綺麗な色別標高図を作るのも難しいです。

地理院地図にある、URLでの共有や断面図といった機能も実装してみたいです。