地球って丸いけど、平面で表現したい。
位置情報を扱うときに丸い地球を平面に見立てて緯度をY軸、経度をX軸として扱いますよね。
普通に考えると…
- 緯度は赤道が0度で、北極が90度、南極がー90度
- 経度はグリニッジ天文台が0度で日本の方がプラス方向で最大180度、北大西洋を超えてアメリカの方がマイナス方向で最大-180度
…なんですが、地球って丸いので(というか緯度経度はその名の通り角度なので)値はぐるぐる循環します。
経度180度も経度-180度も同じです。
一瞬 x' = x % 180 で良いのかな?って思いますが、うまくいかないです。
緯度と経度を正規化する関数
緯度を-90度から90度まで経度を-180度から180度までに正規化します。
function coordinate(lat,lon) {
var side = ( Math.cos(lat * Math.PI / 180) > 0 ) ? 0 : 180;//裏側(?)なら経度を180度移動
var latitude = Math.asin(Math.sin(lat * Math.PI / 180)) * 180 / Math.PI;
var longitude = Math.atan(Math.tan((lon + side) * Math.PI / 360)) * 360 / Math.PI;
return [latitude,longitude];
}
緯度を正規化する関数
ということで作りました。入力が何度[°]になってても-90度から90度を返します。
// 緯度(Y軸方向)の出力は-90度~90度
function latitude(deg) {
return Math.asin(Math.sin(deg * Math.PI / 180)) * 180 / Math.PI;
}
経度を-180度から180度までに正規化する関数(制限付き)
経度はちょっと複雑で-180度=180度なんですよね。
※コメントに頂いた通り、緯度をぐるっとまわして地球の裏側(?)に行く場合には正しくありません。緯度の範囲に制限があります。
// 経度(X軸方向)の出力は-180度~180度 ※ただし緯度が裏側にいないこと。
function longitude(deg) {
return Math.atan(Math.tan(deg * Math.PI / 360)) * 360 / Math.PI;
}
グラフで表示
緯度経度の入力値を-540度から540度まで指定してますが、出力は-90~90、-180~180になってるのがわかると思います。
参考で剰余modの場合も表示します。
ソースコード
緯度経度の正規化関数も含めてHTMLで確認するソースコードです。
グラフの生成はGoogle Chartを参考にしました。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>循環する緯度経度の正規化</title>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript" src="geo.js"></script>
<script type="text/javascript">
google.load("visualization", "1", {packages: ["corechart"]});
google.setOnLoadCallback(drawChart);
</script>
</head>
<body>
<div id="latitude" style="width: 640px; height: 320px;"></div>
<div id="longitude"style="width: 640px; height: 320px;"></div>
<div id="modulus"style="width: 640px; height: 320px;"></div>
</body>
</html>
// 緯度が90度より大きい時などにも対応
function coordinate(lat,lon) {
var side = ( Math.cos(lat * Math.PI / 180) > 0 ) ? 0 : 180;//裏側(?)なら経度を180度移動
var latitude = Math.asin(Math.sin(lat * Math.PI / 180)) * 180 / Math.PI;
var longitude = Math.atan(Math.tan((lon + side) * Math.PI / 360)) * 360 / Math.PI;
return [latitude,longitude];
}
// 緯度(Y軸方向)の出力は-90度~90度
function latitude(deg) {
return Math.asin(Math.sin(deg * Math.PI / 180)) * 180 / Math.PI;
}
// 経度(X軸方向)の出力は-180度~180度 ※ただし緯度が裏側にいないこと。
function longitude(deg) {
return Math.atan(Math.tan(deg * Math.PI / 360)) * 360 / Math.PI;
}
// 参考 Modulus
function modulus(deg) {
return deg % 180;
}
function drawChart() {
var lat_array = [];
var lon_array = [];
var mod_array = [];
lat_array.push(['latitude-in', 'latitude-out']);
lon_array.push(['longitude-in', 'longitude-out']);
mod_array.push(['mod-in', 'mod-out']);
for (var deg = -540; deg < 540; deg++) {
var clat = coordinate(deg,0);//経度が0度(イギリス)
var clon = coordinate(0,deg);//緯度が0度(赤道)
lat_array.push([deg, clat[0]]);
lon_array.push([deg, clon[1]]);
mod_array.push([deg, modulus(deg)]);
}
chart('latitude', lat_array, 'latitude(緯度)');
chart('longitude', lon_array, 'longitude(経度)');
chart('modulus', mod_array, 'mod(剰余)');
}
function chart(id, data_array, title) {
var data = google.visualization.arrayToDataTable(data_array);
var options = {
title: title,
hAxis: {
title: 'In',
minValue: -540,
maxValue: 540
},
vAxis: {
title: 'Out',
minValue: -180,
maxValue: 180
},
legend: 'none'
};
var c = new google.visualization.ScatterChart(document.getElementById(id));
c.draw(data, options);
}
ツッコミ・コメント上等!
もっといい手があるよ!とか、ココちがくない?ということがあればコメントよろしくお願いします。