Hatena::ブログ(Diary)

酢ろぐ(ch3cooh.jp) RSSフィード


CH3COOH(酢酸)の実験室
Baseball Japan / ○○時計 for WP7 / for WM
Windows Phone 7 開発 Tips

2010-11-29

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

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

Bing Mapです。


f:id:ch3cooh393:20101129221659p:image

梅田の付近のこのレベル。まだ日本ではローンチされていないからと言っても、外国の方で日本に旅行で来る人もいるでしょう。このままでは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プロパティは自分のものを使ってくださいね。

ちなみにアプリキーを取得したい方は、Windows Phoneアプリケーション開発入門:第20回 Bing Mapsで遊んでみよう!(1) |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を並べてみました。

f:id:ch3cooh393:20101129221660p:image

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}」です。お試しあれ。

WP7DEVWP7DEV 2011/12/19 16:28 この「Google Map On Bing Map Control」ですが、
BingMapもGoogleMapにも、利用規約違反にはならないですよね?

WP7DEV WP7DEV 2011/12/19 16:32 GoogleMapで「Google」の文字が表示されてないのが気になったので・・・

Masashi.KMasashi.K 2011/12/19 17:57 地図画像, 衛星画像への直接アクセスは利用規約で禁止されていますよ。
http://code.google.com/intl/ja/apis/maps/faq.html#tos_tiles

WP7DEVWP7DEV 2011/12/19 20:10 Masashi.K さん、ありがとうございます。

やっぱり禁止ですか。
あ〜どうしよう。
ルート検索がBingMapでは使いものにならない〜

ch3cooh393ch3cooh393 2011/12/21 15:33 あ、すみません。コメント頂いていたのに気付きませんでした。Google Maps API Expartの方から指摘を受けて、後日別のところでこの方法でのアクセスは利用規約に反している旨を記載しています。

ところで、Bing Mapsのルート検索はBingMapsDirectionsTaskの事でしょうか?このAPIは国内で使うにはまだ微妙なところがあります。

Bing Maps APIをご存じであれば、ルート検索に関してはこちらを使って頂いた方が良い結果が出るかもしれません。 http://msdn.microsoft.com/en-us/library/ff701717.aspx

また、Bing Mapsの地図画像を使いたくなくて、Google Mapsの地図画像を使いたいということであれば、OpenStreetMapの地図画像を使わせてもらうのもアリだと思いますよ。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/ch3cooh393/20101129/1291036733