はじめに
地理院標高タイル を使ってブラウザ上で直接等高線を描いてみます。
標高データ (二次元配列) に対して CONREC のようなアルゴリズムを用いてベクタデータを生成、描画していくのが一般的だと思うのですが、この記事では二次元配列から直接等高線ラスタ画像を生成する方式を紹介します。
デモ
- デモ https://bl.ocks.org/frogcat/raw/cac7e74534e1c9eb68b95487d0df5465/
- ソース https://gist.github.com/frogcat/cac7e74534e1c9eb68b95487d0df5465
解説
基本
前稿 地理院標高タイルと Leaflet でつくるCS立体図 では標高データを取得して曲率・傾斜を計算して画像を合成していましたが、やっていることはほとんど変わりありません。曲率・傾斜の代わりに等高線を計算しただけです。
等高線
冒頭述べたとおり、通常は二次元配列からベクタデータを生成します。標高データが粗で画素が密な場合にはこのような方法は有効です。ただ、地理院標高タイルはとても密です。たとえばズームレベル14以下で地図を表示した状態であれば、画面上のすべてのピクセルに対して標高値がわかる、というレベルで密なデータが整備されています。
ここで、以下のような a,b,c,d
の4つのピクセルからなるグリッドについて、標高値10 の等高線を引きたいものとします。
- a=20, b=0 の場合、a と b の間に等高線が通る
- a=20, c=0 の場合、a と c の間に等高線が通る
- a=20, d=0 の場合、a と d の間に等高線が通る
ベクトルの場合はここで等高線の経路をさらに詳細に計算することになるのですが、ラスターの場合は単に a を塗りつぶすのみです。上記のデモで実際に等高線の塗りつぶし判定を行っているのは以下のようなルーチンです。
// dem を取得
var dem = xhr.responseText.split(/[,\n]/).map(function(a) {
return Math.floor(parseFloat(a) / 100);
});
var ctx = canvas.getContext("2d");
var img = ctx.createImageData(0x100, 0x100);
for (var i = 0; i <= 0xffff; i++) {
if ((i & 0xff) === 0xff || (i & 0xff00) === 0xff00)
continue;
if (dem[i] !== dem[i + 1] || dem[i] !== dem[i + 0x100] || dem[i] !== dem[i + 0x101]) {
img.data[i * 4 + 0] = 0xff;
img.data[i * 4 + 1] = 0xff;
img.data[i * 4 + 2] = 0xff;
img.data[i * 4 + 3] = dem[i] % 5 === 0 ? 0xff : 0x70;
}
}
ctx.putImageData(img, 0, 0);
右、下、右下のセルの値と自分の値を比較して自分自身を塗りつぶすかどうかを判定する、というものなので難しい計算はしていません。スマホでも動作するレベルの負荷です。等高線の本数が増えても比較的影響が少ないはずです。
まとめ
標高タイルから等高線描画する方法を紹介しました。標高タイルが密に整備されているからこそできるチート、という感もありますが比較的高速に等高線を描画することが可能です。よい使いどころがあれば応用してみてください。