酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Windows Phone 7でGoogle Map on Bing Map Controlを実現する

Windows Phone 7を使っていて、スマートフォントして有り得ないと思うものがあります。少なくともこのまま行けば、Windows Phone 7は酷評されて日本で日の目を見ません。それは何か?

Bing Mapです。

梅田の付近のこのレベル。まだ日本ではローンチされていないからと言っても、外国の方で日本に旅行で来る人もいるでしょう。このままではWindows Phone 7を使っている外国の方に悪いイメージを与えかねない。

……ってことで、Bing Map Control上でGoogle Mapの画像を使うTipsをご紹介したいと思います。Bing MapもGoogle Mapも再接近時の地図の大きさで言うと、1,073,741,824ピクセルx1,073,741,824ピクセルの超巨大な画像です(ちなみに10億x10億ですね)。

Bing Mapには、Deep Zoomと呼ばれる技術が使われています。Deep Zoomというのは、超高画質の画像を例えば256ピクセルx256ピクセルと言った小さなタイルに分割して、描画に必要な箇所のタイルだけを読み込み、スムーズなズームイン、ズームアウト、スクロール(パン)を実現させることができます。

このタイルを読み込む箇所が、Bing Mapコントロールでは、Microsoft.Phone.Controls.Maps.TileSourceクラスの以下のメソッドになります。

Uri GetUri(int x, int y, int zoomLevel)

タイルを読み込むGetUriメソッドをオーバーライドして、本来のBing Mapの画像ではなく、Google Mapの画像を渡すようにしてしまえば勝ちですね。

やり方としては、まず名前空間を追加します。

    xmlns:mapCtrl="clr-namespace:Microsoft.Phone.Controls.Maps;assembly=Microsoft.Phone.Controls.Maps"
    xmlns:gMapEx="clr-namespace:GoogleMapsSample"
    xmlns:device="clr-namespace:System.Device.Location;assembly=System.Device">

次にBing Map Controlを貼り付けましょう。この辺りは詳しくはサンプルプロジェクトを添付しますので、そちらの方を見てくださってもOKです。CredentialsProviderプロパティは自分のものを使ってくださいね。

ちなみにアプリキーを取得したい方は、第20回 Bing Mapsで遊んでみよう!(1) :Windows Phoneアプリケーション開発入門|gihyo.jp … 技術評論社のBing Maps Keyの取得方法あたりを見てくださいね。

        <mapCtrl:Map Height="610" HorizontalAlignment="Left" Name="map1" VerticalAlignment="Top" Width="456"
                CredentialsProvider="your app key" ZoomLevel="16">
            <mapCtrl:Map.Center>
                <device:GeoCoordinate Altitude="NaN" Course="NaN" HorizontalAccuracy="NaN" Latitude="34.701189" Longitude="135.496016" Speed="NaN" VerticalAccuracy="NaN" />
            </mapCtrl:Map.Center>
            <mapCtrl:Map.Children>
                <mapCtrl:MapTileLayer>
                    <mapCtrl:MapTileLayer.TileSources>
                        <gMapEx:GoogleTileSource TileSourceType="Satellite" />
                    </mapCtrl:MapTileLayer.TileSources>
                </mapCtrl:MapTileLayer>
                <mapCtrl:MapTileLayer>
                    <mapCtrl:MapTileLayer.TileSources>
                        <gMapEx:GoogleTileSource TileSourceType="Street" />
                    </mapCtrl:MapTileLayer.TileSources>
                </mapCtrl:MapTileLayer>
            </mapCtrl:Map.Children>
        </mapCtrl:Map>

XAMLで使ったGoogleTileSourceクラスとTileSourceType型を定義する必要があります。適当にGoogleTileSource.csといったソースを新規作成してコピペしてください。

XAMLで名前空間を指定していますので、名前空間はGoogleMapsSampleを使ってください。必要あれば適切に変更をお願いします。

namespace GoogleMapsSample
{
    public enum GoogleTileSourceType
    {
        Street,
        Hybrid,
        Satellite,
        Physical,
        PhysicalHybrid,
        StreetOverlay,
        WaterOverlay
    }

    public class GoogleTileSource : Microsoft.Phone.Controls.Maps.TileSource
    {
        public GoogleTileSource()
        {
            UriFormat = @"http://mt{0}.google.com/vt/lyrs={1}&z={2}&x={3}&y={4}";
            TileSourceType = GoogleTileSourceType.Street;
        }
        private int _servernr;
        private char _mapMode;

        private int Server
        {
            get
            {
                return _servernr = (_servernr + 1) % 4;
            }
        }

        private GoogleTileSourceType _tileSourceType;
        public GoogleTileSourceType TileSourceType
        {
            get { return _tileSourceType; }
            set
            {
                _tileSourceType = value;
                _mapMode = TypeToMapMode(value);
            }
        }

        public override Uri GetUri(int x, int y, int zoomLevel)
        {
            if (zoomLevel > 0)
            {
                var url = string.Format(UriFormat, Server, _mapMode, zoomLevel, x, y);
                //System.Diagnostics.Debug.WriteLine(url);
                return new Uri(url);
            }
            return null;
        }

        private static char TypeToMapMode(GoogleTileSourceType tileSourceType)
        {
            switch (tileSourceType)
            {
                case GoogleTileSourceType.Hybrid:
                    return 'y';
                case GoogleTileSourceType.Satellite:
                    return 's';
                case GoogleTileSourceType.Street:
                    return 'm';
                case GoogleTileSourceType.Physical:
                    return 't';
                case GoogleTileSourceType.PhysicalHybrid:
                    return 'p';
                case GoogleTileSourceType.StreetOverlay:
                    return 'h';
                case GoogleTileSourceType.WaterOverlay:
                    return 'r';
            } return ' ';
        }
   }
}

さて、これを実行してGoogle MapとBing Mapを並べてみました。

Bing MapとGoogleが同じ256ピクセルx256ピクセルの画像を使っていたので、出来る技ですね。ちなみにYahoo!地図なんかも同じサイズのタイルを使っているので、ひょっとしたら同じことが出来るかもしれないです。

日本人には、Google MapよりもYahoo!地図の方がマッチしているかもしれないので、必要な方はGetUriメソッドを差し替えてそれっぽく書き換えてください。

ちなみにYahoo!地図のURLは、「http://ta.map.yahoo.co.jp/yta/map?v=4.3&r=1&x={0}&y={1}&z={2}」です。お試しあれ。