tl;dr
日本の領海を2次元の多角形で書いて、その多角形の内外判定により日本かどうかを判別します。
やりたかったこと
Snapmapでは投稿された写真がどこで撮影されたかにより、処理を分岐しています。
その過程で、ある座標が日本国内に位置するかどうかを利用制限なくタダで判定したかったのです。
(Googleなどの有料APIを使いたくなかった)
アプローチ
- 日本の領海を2次元の多角形で書く
- この多角形の各点を座標と結びつける
- そうすると、指定した座標が多角形の中にいるのか外にいるのかという判断で、日本の領海内かどうかを判別できる。
注意事項
- 日本が島国という点を生かしたアイデアです。
- 多角形を自分で描くので、国境などの細かいところまでは判定させるのは現実的ではありません。
日本は島国なので、判定したい座標が陸地である限り上記の問題は無いに等しいため、このアプローチが利用できます。 - 以上のことから島国ではない国や、都道府県などに範囲を縮小して(例えば東京都内かどうかの判定など)への応用は難しいです。
中国やロシア、韓国などとトラブルを起こさないようにしましよう
内外判定の実装
Winding Number Algorithmを使用しています。私は詳しく説明できるほど詳しくないので、以下のリンクを参考にしてください。
【第2回】点の多角形に対する内外判定
実装手順
日本の多角形を描く
GoogleMapのマイマップ機能で図形を描けますので、この機能を利用し図形を描きます。
図形を描いたあと、KMLファイルでその図形をDLでき、そのKMLファイルの中に図形の各点の座標が記録されているので、その座標を利用します。
ロジックを作る(ruby)
in? メソッドで内外判定ができます。y=緯度、x=経度、p=多角形を表す配列です。今回のテーマでは、pが日本の領海を表現しています。pは以下のように緯度と経度をいくつも持つ配列です。各点を結ぶ順番は、配列の順番で表現されるので配列の順番は意図した通りに並んでいる必要があります。
前項でDLしたKMLファイルから取得した座標は、線を描く時に設定した点を、打った順番通りに出力してくれるので、基本的にはそのままの順番で問題ありません。
[
[130.3274316, 30.9552693], # 緯度, 経度
[130.3368706, 31.0346679],
[130.1881978, 31.0875757],
...
]
def in?(y, x, p)
last = p.size - 1
last = p[0][0] == p[last][0] && p[0][1] == p[last][1] ? last - 1 : last
j = 0
clockwise = 0
winding_number = 0
last.downto(0) do |i|
yi = p[i][1] - y
yj = p[j][1] - y
if (yi > 0) != (yj > 0)
xi = p[i][0] - x
xj = p[j][0] - x
if (clockwise = sign(yj, yi)) == sign(yj * xi, yi * xj)
winding_number += clockwise
end
end
j = i
end
winding_number.odd?
end
def sign(a, b)
if a > b
1
elsif a < b
-1
else
0
end
end