f8g

基盤地図情報をcanvasで

最近でもないけど、基盤地図情報が無料でダウンロードできるようになった。
http://fgd.gsi.go.jp/download/
blogで画像を公開するのには申請が必要みたいなので今回は画像なし。

データ

GML版の方が軽くてシンプルなのでそっちを使ってやります。(だた、そこで公開されている閲覧ソフトでは処理できないみたいですが)
一番下のほうにある数値標高モデルをダウンロードすると、ZIPファイルにいくつかXMLが入ってますが、今回は適当にやるので、適当にひとつ選びます。
データは大体こんな感じで書かれてます。(というか仕様知らんので詳しくはそっちを)

    • 多分右下の座標
    • 多分左上の座標
  • ,
    • メッシュのセルの数。番号がlowからhighまで、xとyについて書かれてる。
    • 標高値のデータ。タイプ,値[改行]
    • タイプは、その他: 陸地、データなし: たぶん海(値は-9999.)

処理

それっぽいぼかし画像を作る。(これ使用、最新版が必要)

// データ取得
var xmlhttp = new XMLHttpRequest;

xmlhttp.onreadystatechange = function(){
	if(xmlhttp.readyState == 4)
		onload(xmlhttp);
};

xmlhttp.open("GET", "data.xml", true);
xmlhttp.send(null);

// 画像にする
var onload = function(r){
	var xml   = r.responseXML;
	var text  = r.responseText;
	var black = ImageProcessing.Color.fromHex(0);
	var white = ImageProcessing.Color.fromHex(0xffffff);
	var ip    = new ImageProcessing(document.createElement("canvas"));
	var size  = parseInt(xml.getElementsByTagName("gml:high")[0].firstChild.nodeValue.split(" ")[0], 10) + 1;

	ip.canvas.width  = size;
	ip.canvas.height = size;

	// 標高値を取得
	var alts = [];
	var re = /(その他|データなし),(.+)/g;
	var m;

	while(m = re.exec(text))
		alts.push(m[2]);

	// 0で黒、標高の最大値で白になるようにする
	var rate = 255 / Math.max.apply(null, alts);
	var i = 0;

	// 点を打つ
	ip.lock()
		.each(function(px, x, y){
			var alt = alts[i++];

			if(!alt || alt < 0)
				ip.setPixel(x, y, black);
			else{
				var c = alt * rate;
				ip.setPixel(x, y, new ImageProcessing.Color(c, c, c));
			}
		})
	.update();

	document.body.appendChild(ip.canvas);
};

最大値を求めるために標高値を配列に入れてますが、適当さを重視するなら、正規表現の処理からいきなりsetPixelするのが少し速くてお勧め。

画像処理

画像処理を加えると少しイカした画像になります。

でこぼこっぽく
ip.lock()
	.each(function(px, x, y){
		var alt = alts[i++];

		if(!alt || alt < 0)
			ip.setPixel(x, y, blue);
		else{
			var c = alt * rate;
			ip.setPixel(x, y, new ImageProcessing.Color(c, c, c));
		}
	})
	.filter(ImageProcessing.filter.emboss, 127)
.update();
偽等高線
ip.lock()
	.each(function(px, x, y){
		var alt = alts[i++];

		if(!alt || alt < 0)
			ip.setPixel(x, y, blue);
		else{
			var c = alt * rate;
			c = Math.ceil(c / 32) * 32; // 階調化
			ip.setPixel(x, y, new ImageProcessing.Color(c, c, c));
		}
	})
	// 輪郭抽出
	.filter([
		[0,  1, 0],
		[1, -4, 1],
		[0,  1, 0]
	])
	// 2値化
	.each(function(px, x, y){
		if(px.r == 0)
			ip.setPixel(x, y, black);
		else
			ip.setPixel(x, y, white);
	})
.update();

しまいに

今回はラスタマップのデータを使いましたが、ベクトルマップも公開されてるので、他にも面白いことができそうですが、申請が必要なら流行らないと思う。