大雪山 姿見駅から旭岳を望む


Date/Time: 2014:09:23 08:48:09
Camera: PENTAX
Model: PENTAX K-5 II s
Exporsure Time: 1/2500
FNumber: 4.0
Aperture Value: 4.0
Focal Length: 21.0

Close

y2blog » WEBマップシステム Misc.

9

03

2014

WEBマップシステム Misc.

地理院地図システムをネタにWEBマップのTIPSを少し


このサイトで過去何回か国土地理院の電子国土や新しくなった地理院地図について採り上げてきたが、地理院地図 http://portal.cyberjapan.jp/ で用いられているシステムを眺めていたら、役に立ちそうな機能が実装されていたので、Google Mapsなどの他のWEBシステムの参考例なども含めて簡単に紹介しようと思う.



Google Maps API V3をベースに組むとこんな感じ


地図右下の “i” マークをクリックすると現在表示している地図に関する各種情報が表示される.また、右クリックでその地点に関する標高や住所などの情報がポップアップウインドウに表示されるようになっている.




【脱Google Mapsにより実例はありません】


Yahoo! Japan Map API をベースにした場合


【Yahoo! Japan Map API廃止に伴いコンテンツ削除】

※Yahoo! Japan MapのXYZタイルの座標系は、Google Mapsや国土地理院のXYZタイルの座標系とは異なるので注意が必要.各WEBマップシステムで用いられているタイルの仕様については、過去の記事『電子国土V4の背景地図のタイル形式』を参照して欲しい.



緯度・経度情報からその場所の標高値を取得する(標高API)


標高値の取得に関してはGoogle MapsやYahoo! Japan Mapが先行してサービスを行っているので既に利用している人も多いと思うが、国土地理院でも正式に標高APIとして公開を始めたので利用してみると良いだろう.


国土地理院の標高APIの使い方は、Yahoo! Japan Mapと同じように、URLのパラメータとして場所と取得方法(JSON/JSONPのどちらか)を指定して情報を取り出す所謂RESTインタフェースとなっており、WEBブラウザのURL欄に単純に “http://cyberjapandata2.gsi.go.jp/general/dem/scripts/getelevation.php?lon=139.312346&lat=35.322153&outtype=JSON” のように記述するだけでも結果を覗いて見ることができる.


{“elevation”:180.1,”hsrc”:”5m\uff08\u30ec\u30fc\u30b6\uff09″}

のようなJSON形式のデータの内容がWEBブラウザの画面に表示される.ちなみに “hsrc” の値はデコードすると「5m(レーザ)」となり、この標高値がレーザ測量による5mメッシュのDEMから得られたものであることを示している.


同じような仕組みは Google MapsやYahoo! Japan Mapでも用意されているが、日本国内の標高データに関しては国土地理院のデータが木目(メッシュ)の細かさや精度の点で圧倒的に有利だ.


Yahoo! Japan YOLP 標高API : http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/altitude.html

YOLP(地図) 標高APIの出力結果の例 (XML)




1
1
1
200
0

(C) Yahoo Japan Corporation.


1

point
139.31234,35.322153


180.7
218.96511516407
38.265115164067




Yahoo! Japan の標高APIに関してはジオイド高や楕円体高まで含まれており、一度に最大100地点までのデータを取得できるので便利なのだが、全てのサービスにアプリケーションIDを指定しなければ使えないのが難点だ.指定した経路の位置情報から標高プロファイルを簡単に作成できるので、登山道などのアップダウングラフの取得などに活用できるだろう.

・ Google Maps JavaScript API v3 : Elevation Service

Google Maps APIの方はRESTインタフェースでは無くて “ElevationService()” という一つのメソッドとして実装されているが、内部的にはAJAXによる非同期処理なので、処理結果を非同期でコールバックの形で受け取るという使い方はどれも同じだ.



逆ジオコーディング


逆ジオコーディングという言葉はあまり馴染みがないかもしれないが、緯度・経度などの位置情報からその場所の地名(住所)を求めるという意味で使われている.逆ジオコーディングに関しても既にGoogle MapsやYahoo! Japan Mapがサービスを行っており、そのAPIが公開されている.


残念ながらまだ国土地理院の逆ジオコーディングAPIは公開されていないようだが、実は地理院地図のポータルサイトではこの機能が既に実装されている.



Rev. Geocoding on GSIJPortal
地理院地図上でマウスの右クリックで表示されるポップアップウインドウ (地理院地図より)


まだ公開されている情報ではないので、今後この逆ジオコーディング機能がどのように推移するのかは不明だが、とりあえず試験的にこの機能を使ってみた.(ポータルサイトの実装環境から独自に調べたもので、国土地理院が正式に公開している機能ではない.この機能を使うか使わないかは個人の判断で行って欲しい.)



JQueryで実装するとこんな感じ



var addressOfLocation = "";

//==============================================
// reverse geocoding request [ GSI ]
//==============================================
function doGeoReverseGeocodingRequestGSI( lat, lon, callback ){
	
	addressOfLocation = "";
	
	var baseURL = 'http://portal.cyberjapan.jp/GsiJsLibrary/LonLatToLv01.php';
	
	var address = "";
	var url = baseURL;
		url += "?longitude=" + lon + "&latitude=" + lat;

	var parameter = {};
	parameter[ 'request' ] = url;
	
	jQuery( document ).ajaxError( function() {
		console.log(" 'AJAX ERROR' has been occurred. ");
	});

	function getResult( json ) {
		jQuery.proxy( ajaxReadyStateChangeHandler, this )( json, callback );
	}

	// AJAX request [ jQuery ]
	jQuery.ajax({
		type: "GET",
		url: url,
		data: parameter,
		dataType: "json",
		timeout: 30000,
		success: getResult
	});

	function ajaxReadyStateChangeHandler( json, callback ) {
	
		if ( json ) {
			var prefCd   = json.result[0].prefCd ? json.result[0].prefCd : "";
			var prefNm   = json.result[0].prefNm ? json.result[0].prefNm : "";
			var muniCd   = json.result[0].muniCd ? json.result[0].muniCd : "";
			var muniNm   = json.result[0].muniNm ? json.result[0].muniNm : "";
			var muniKana = json.result[0].muniKana ? json.result[0].muniKana : "";
			var conNm    = json.result[0].conNm ? json.result[0].conNm : "";
			var conKana  = json.result[0].conKana ? json.result[0].conKan : "";
			var lv01Nm   = json.result[0].lv01Nm ? json.result[0].lv01Nm : "";
			var lv01Kana = json.result[0].lv01Kana ? json.result[0].lv01Kana : "";
			var lat      = json.result[0].latitude ? json.result[0].latitude : "";
			var lon      = json.result[0].longititude ? json.result[0].longititude : "";
			
			// remove undesired ideographics space (a.k.a. Zenkaku Space) of "muniNm' and "muniKana" fields
			muniNm   = muniNm.replace(/\u3000/g, "");
			muniKana = muniKana.replace(/\u3000/g, "");

			addressOfLocation += prefNm + muniNm + conNm + lv01Nm;
		}

		// initiate callback 
		callback( address );
	}
}



【追記: 4/03 2016】


上記の逆ジオコーディングのサンプルコードは地理院地図の機能が改訂され、アクセス先のURLやAJAXのパラメータが変更されているため動作しない.


新しい地理院地図のソースコードがGitHubで公開されているので、そちらのコードを参照して欲しい.



“gsimaps.js” の294行目

CONFIG.SERVERAPI.GETADDR = “http://mreversegeocoder.gsi.go.jp/reverse-geocoder/LonLatToAddress”;

AJAX廻りのコードは、9994〜10032行目あたりに書かれている.



	getAddressResult : function(json)
	{
		if (json.results){
			var address = "";
			var addObj = json.results;
			var addressData = GSI.MUNI_ARRAY[parseInt(addObj.muniCd,10)+""];
			if (addressData) {
				addressData = addressData.split(",");
				var muniNm = (addressData[1]+addressData[3]).replace(" ","");
				address += muniNm;
			}
			if (addObj.lv01Nm) address += addObj.lv01Nm;

			$("#address").empty();
			$("#address").append(address ? address : "---");
			this.refreshSize();
		}
	},
	getElevationRusult : function (data, dataSrc)
	{
        var outPutHeight = data + "(" + this.vDemAltSRC + ")";

		$( "#elevation" ).html( outPutHeight );
		this.refreshSize();
	},
	execRefresh : function (lon, lat)
	{
		this.ajaxAddress = $.ajax({
			type: "GET",
			url:CONFIG.SERVERAPI.GETADDR,
			data: {
				"lon" : lon,
				"lat" : lat
			},
			dataType: "json",
			timeout: 30000,
			success: L.Util.bind( this.getAddressResult, this ),
			error : function(){}
		});




上記のAJAXリクエストで ” {results: {muniCd: “01217”, lv01Nm: “西野幌”}} ” のような JSON値が得られる.”muniCd” の配列は、”gsimaps.js” の中で定義されているので、muniCdの値とlv01Nmの値を組み合わせると住所が得られる.


上記のAJAXリクエストをWebブラウザ上で試して見る場合は、単純にURL欄に “http://mreversegeocoder.gsi.go.jp/reverse-geocoder/LonLatToAddress?lon=139.229393&lat=35.521259” のように緯度・経度をURLのパラメータとして10進形式で指定してみると、結果を取得することができる.




逆ジオコーディングに関しては、Yahoo Japan Map, Google Maps にも同様な機能があるので、それらを参考にすると良いだろう.

・ Yahoo!リバースジオコーダAPI : http://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/reversegeocoder.html


・ Google Maps Reverse Geocoding (Address Lookup) https://developers.google.com/maps/documentation/javascript/geocoding#ReverseGeocoding


国土地理院の1/25,000地形図の図葉名を求める


ハンドヘルドGPSが普及したこともあり、1/25,000地形図を山に持ち出して使っている人は大部少なくなったようだが、地図の中の地図、1/25,000地形図に代わる物はない.大きな書店の地図コーナーに行けばは1/25,000地形図(紙版)が入手可能だが、自分の欲しい山域がどの図葉名で売られているのか直ぐには判らない.


ポータルサイトのコードを眺めていたら位置情報(緯度・経度)から図葉名を求めるライブラリがあったので、図葉名を求める方法を紹介しておく.


図葉名を求めるJavascriptのライブラリは、”http://portal.cyberjapan.jp/site/mapuse4/js/zuyou.js” に置かれている.

使い方としてはこんな感じだろうか.実際の1/25,000地形図の境界はオーバーラップしている部分があるので、そのような地点の図葉名は複数表示されるようだ.




function getZuyouName25K( lat, lon ) {

    // obtain 1/25,000 TOPO Map name list of current map area
    var zuyou_name = check_zuyou_list( lon, lat );
    var zlist = "";
    for( var key in zuyou_name ) {
        zlist = zlist + zuyou_name[key][0] + " , ";
    }
    zlist = zlist.substring( 0, zlist.length - 2 );
    
    return( zlist );
}

地域メッシュコードを求める


一般のユーザが地域メッシュコードを扱う場面は少ないと思うが、地形図データをダウンロード購入したりする際は、この地域メッシュコードでディレクトリやファイル名が付けられていることがあるので、地域メッシュコード情報を簡単に取得できれば便利だろう.


地域メッシュコードに関しては、総務省のホームページに『地域メッシュ統計の特質・沿革』 “http://www.stat.go.jp/data/mesh/pdf/gaiyo1.pdf” というPDFのドキュメントが置かれているので、このドキュメントを参考にすると良いだろう.地域メッシュコードの算出の方法が詳しく書かれているが、緯度・経度から地域メッシュコードの算出の方法の説明がちょっと分かり難い.


地域メッシュコード自体は、度・分・秒の体系を基に作成されているが、ここで紹介されている算出式の意味が良く分からなかったが、具体的な数値による計算例を追っていってようやく計算式の意味が理解できた.算出式の内容を理解するより、図の説明を基に自分で計算式を求めた方が簡単かもしれない.



サンプルコードはこんな感じ



//==================================================================================================
// calculate JIS X 0410 Mesh Code
//
// Reference : http://www.stat.go.jp/data/mesh/pdf/gaiyo1.pdf
//==================================================================================================
function getMeshcode( lat, lon, order ) {

	var mesh1d = ""; 
	var mesh2d = "";
	var mesh3d = "";

	if ( order < 1 || order > 3 ) return( "" );

	// lat and lon parameter must be decimal degrees
	// ( lat * 60" ) / 40"  => p  remainder => a
	var meshLat1p =  lat * 60.0 / 40.0;
	var meshLat1a =  ( meshLat1p - Math.floor( meshLat1p ) ) * 40.0; // [ minute ]
	meshLat1p = Math.floor( meshLat1p );
	
	var meshLat2q = meshLat1a / 5.0;
	var meshLat2b =  (meshLat2q - Math.floor( meshLat2q ) ) * 5.0; // [ minute ]
	meshLat2q = Math.floor( meshLat2q );

	var meshLat2r = meshLat2b * 60.0 / 30.0;
	var meshLat2c =  (meshLat2r - Math.floor( meshLat2r ) ) * 30; // [ second ]
	meshLat2r = Math.floor( meshLat2r );

	
	var meshLon1u = lon - 100.0;
	var meshLon1f = meshLon1u - Math.floor( meshLon1u ); // [ degree ]
	meshLon1u = Math.floor( meshLon1u );

	var meshLon2v = meshLon1f * 60.0 / 7.5;
	var meshLon2g = (meshLon2v - Math.floor( meshLon2v ) ) * 7.5; // [ minute ]
	meshLon2v = Math.floor( meshLon2v );

	var meshLon2w = meshLon2g * 60.0 / 45.0;
	var meshLon2h = (meshLon2w - Math.floor( meshLon2w ) ) * 45.0; // [ secoond ]
	meshLon2w = Math.floor( meshLon2w );

	mesh1d = String(meshLat1p) + String(meshLon1u);
	mesh2d = mesh1d + String(meshLat2q) + String(meshLon2v);
	mesh3d = mesh2d + String(meshLat2r) + String(meshLon2w);
	
	var  meshcodes = [ "", mesh1d, mesh2d, mesh3d ];
	
	return( meshcodes[ order ] );
	
}

おまけ: 2地点間の緯度・経度からその距離を求める


Hubeny’s distance calculation method という計算方法を使って、2地点間の距離を求める関数.



function getDistanceFromLatLons( lat1, lon1, lat2, lon2 ) {
	
	// Hubeny's distance calculation method
	//
	//  Constants : WGS84 
	//  a = 6,378,137.0 m,   b=6,356,752.314245m, a(1 - e^2) = 6,335,439.327 292 46
	//  e2 = 0.00669437999019758
	//
	var a = 6378137.0;
	var b = 6356752.314245;

	// e2 = ( a * a - b * b) / (a * a); 
	var e2 = 0.00669437999019758;

	// mnum = a * (1 - e2);
	var mnum = 6335439.32729246;

	var deltax = dec2rad( lon2 - lon1 );
	var deltay = dec2rad( lat2 - lat1 );
	var midy   = dec2rad( (lat2 + lat1 ) /2 );

	var sin = Math.sin( midy );
	var w = Math.sqrt( 1 - e2 * sin * sin );
		
	var m = mnum / ( w * w * w );
	var n = a / w;
		
	var distance = Math.sqrt( (deltay * m) * (deltay * m) + 
					( deltax * n * Math.cos(midy) ) * ( deltax * n * Math.cos(midy) ) );
		
	return( distance ); // [ m ]
}


おまけ: 磁気偏角(磁北線)の計算方法


磁北線に関する情報は1/25,000地形図の図葉毎にレジェンドにその値が記されているが、国土地理院では地磁気の実測結果を基に、緯度・経度から磁北線の角度を割り出すことが可能な近似モデルを公表している.簡単な二次近似式なのでプログラムを書くのは容易だろう.詳細については国土地理院のホームページ内にある『地磁気値を求める』を参照して欲しい.