循環する緯度経度の値を正規化する。

# 地球って丸いけど、平面で表現したい。

位置情報を扱うときに丸い地球を平面に見立てて緯度をＹ軸、経度をＸ軸として扱いますよね。

普通に考えると…

- 緯度は赤道が０度で、北極が90度、南極がｰ90度
- 経度はグリニッジ天文台が０度で日本の方がプラス方向で最大180度、北大西洋を超えてアメリカの方がマイナス方向で最大-180度

…なんですが、地球って丸いので（というか緯度経度はその名の通り角度なので）値はぐるぐる循環します。
[経度180度](https://www.google.co.jp/maps/@71,180,7z)も[経度-180度](https://www.google.co.jp/maps/@71,-180,7z)も同じです。


一瞬 x' = x % 180 で良いのかな？って思いますが、うまくいかないです。


# 緯度と経度を正規化する関数

緯度を-90度から90度まで経度を-180度から180度までに正規化します。

```js
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度を返します。

```js
// 緯度（Ｙ軸方向）の出力は-90度～90度
function latitude(deg) {
	return Math.asin(Math.sin(deg * Math.PI / 180)) * 180 / Math.PI;
}
```

# 経度を-180度から180度までに正規化する関数（制限付き）

経度はちょっと複雑で-180度＝180度なんですよね。
※コメントに頂いた通り、緯度をぐるっとまわして地球の裏側(?)に行く場合には正しくありません。緯度の範囲に制限があります。

```js
// 経度（Ｘ軸方向）の出力は-180度～180度 ※ただし緯度が裏側にいないこと。
function longitude(deg) {
	return Math.atan(Math.tan(deg * Math.PI / 360)) * 360 / Math.PI;
}
```

# グラフで表示

緯度経度の入力値を-540度から540度まで指定してますが、出力は-90～90、-180～180になってるのがわかると思います。
参考で剰余modの場合も表示します。

![cap.png](https://qiita-image-store.s3.amazonaws.com/0/51116/7c3c1ae3-bca7-b3f5-b574-cbdffc159746.png)

# ソースコード

緯度経度の正規化関数も含めてHTMLで確認するソースコードです。
グラフの生成は[Google Chart](https://google-developers.appspot.com/chart/interactive/docs/gallery/scatterchart)を参考にしました。

```html:index.html
<!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>
```

```js:geo.js

// 緯度が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];
}


// 緯度（Ｙ軸方向）の出力は-90度～90度 
function latitude(deg) {
	return Math.asin(Math.sin(deg * Math.PI / 180)) * 180 / Math.PI;
}

// 経度（Ｘ軸方向）の出力は-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);
}
```

# ツッコミ・コメント上等！

もっといい手があるよ！とか、ココちがくない？ということがあればコメントよろしくお願いします。