Google Mapsの地図をC#で使いたかったので、Google Mapsの仕組みを調べてみました。
Google Maps
Google Mapsは、JavaScriptとCSS(Ajax)を有効活用したウェブブラウザで動作するSaaS型のアプリケーションです。
このGoogle Mapsでは、ウェブサイト向けにGoogle Maps APIが提供されています。しかし、プログラムでは、うまく使用できず、もし使うとするなら、Static Map APIくらいしか利用できません。Static Map APIでは、Google側で自動的に加工され、著作権表示、スケール表示が自動的に付加され、邪魔になります。
そこで、Google Mapsが使用している生のデータを利用することにして、ダイレクトにGoogle Mapsのデータを扱うことにしました。
タイルについて
Google Mapsでは、地図を複数の画像に分けて、それをPCで合成させて、1枚の地図を表示しています。この分割された地図をタイルとします。
タイルは、256ピクセル×256ピクセルのPNG形式の画像になっています。
このタイルは、x座標、y座標、z座標に分けて管理されており、それぞれ、経度、緯度、スケールに対応付けられています。
タイルの取得先のURLは、以下のように構成されています。
http://mt0.google.com/vt/lyrs=m@134&hl=ja&x=[タイルのx座標]&y=[タイルのy座標]&z=[タイルのz座標]
まず、アクセス対象のサーバは、mt0.google.com、もしくは、mt1.google.comです。これは、Javascriptでランダムで振り分けられていました。
hlは、Googleでよく使われる変数で言語設定の変数です。日本語は、jaです。
x、y、zは、タイル座標です。
lyrs=m@134は、よくわかりません(笑)。これが無くても動くので、何に使われているかわかりません。バージョン情報に使われているということが他サイトで言われていますが、よくわかりません。
スケールは、1-22まであり、+1されると、縦と横の密度が2分の1になり、4倍に拡大されるようになっています。
緯度・経度・スケールからタイル座標への変換
タイル座標に変換しないと使えないので、変換します。これは、Google MapsのJavaScriptを解析して求めました。
C#で動作するように変換しています。クラスの記述は省略しています。
緯度・経度の表記
緯度や経度の表記方法ですが、単位は、「度」を使用し、10進数実数で表記します。
つまり、「度」と「分」を用いている場合は、「分」の部分を「度」に変換する必要があります。
経度からx座標への変換
int LongitudeToX(double longitude, int zoom)
{
double centerPoint = Math.Pow(2, zoom + 7);
double totalPixels = centerPoint * 2;
double pixelsPerLngDegree = totalPixels / 360;
return (int)(centerPoint + longitude * pixelsPerLngDegree + 0.5) / 256,
}
その経度が含まれるタイルの座標がわかります。
経度からy座標への変換
int LatitudeToY(double latitude, int zoom)
{
double centerPoint = Math.Pow(2, zoom + 7);
double totalPixels = centerPoint * 2;
double pixelsPerLngRadian = totalPixels / (2 * Math.PI);
double siny = Math.Min(Math.Max(Math.Sin(latitude * (Math.PI / 180)), -0.9999), 0.9999);
return (int)(centerPoint - 0.5 * Math.Log((1 + siny) / (1 - siny)) * pixelsPerLngRadian + 0.5) / 256,
}
その緯度が含まれるタイルの座標がわかります。
x座標から経度への変換
double XToLongitude(int x, int zoom)
{
return x / Math.Pow(2, zoom) * 360 - 180;
}
左上の経度が求まります。
y座標から緯度への変換
double YToLatitude(int y, int zoom)
{
double tile_p = Math.Pow(Math.E, ((0.5 - (y / Math.Pow(2, zoom))) * 4 * Math.PI));
return Math.Asin((tile_p - 1) / (tile_p + 1)) * 180 / Math.PI;
}
左上の緯度が求まります。
1タイルの経度当たりのピクセル数
double LongitudePerPixel(int zoom)
{
return 360 / Math.Pow(2, zoom + 8);
}
1タイルの緯度当たりのピクセル数
double LatitudePerPixel(int y, int zoom)
{
double tile_p = Math.Pow(Math.E, ((0.5 - (y / Math.Pow(2, zoom))) * 4 * Math.PI));
double tile_q = Math.Pow(Math.E, ((0.5 - ((y + 1) / Math.Pow(2, zoom))) * 4 * Math.PI));
return (Math.Asin((tile_p - 1) / (tile_p + 1)) - Math.Asin((tile_q - 1) / (tile_q + 1))) * 180 / Math.PI / 256;
}
※arcsinの計算方法がわからなかったため、これだけタイルのy座標が必要です。
最後に
式ばっかりで、さらに説明グダグダで意味不明になってしまって申し訳ないです。
プログラムでGoogle Mapsを使いたい人に、この使い方をお勧めしておきます。
ただし、動作に関しては保証しないので、自己責任でお願いします。
コメントする