Google Maps for Windows Phone 7 using the Bing Maps Control

3 minute read

GoogleMapsAs I write this, we are less than 25 hours away from the moment the first Windows Phone 7 devices are released into the wild – in the Netherlands, that is. The marketplace is filling up nicely already. Some things are pretty obviously missing. And you wonder why, because they would be quite easy to make.

I hereby dare Google to publish a Google Maps application in the Windows Phone 7 Marketplace. Why? Because if they don’t do it, someone else probably will, and probably pretty fast, too. See the screenshot to the left. I assure you – this is no fake. What you see here is the Google Maps satellite layer with the Street layer on top of it, with a 40% opacity, showing a piece of the city center of Amersfoort, Netherlands. It took me about 90 lines of code and 18 lines of XAML.

Setting up a basic Bing Map control is easy as cake. You find the grid “Contentpanel”, and you plonk a Map in it, preferably using Blend:

 

 

 

 

 

 

 

<Microsoft_Phone_Controls_Maps:Map 
 x:Name="map"  
 CredentialsProvider="your_credentials" Mode="Road">
</Microsoft_Phone_Controls_Maps:Map>

You can change “Road” into “Aerial” and then you have Bing Satellite imagery. What’s less known is that you can actually attach your own tile layers to it. A tile is an image of 256x256 defined by it’s X and Y position in the grid forming the map of the world on a specific zoom level. All you have to to is write a little class that descends from Microsoft.Phone.Controls.Maps.TileSource in which you only have to override the following method:

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

in which you tell which x/y/zoomlevel combination translates to which URI. For Google Maps, this turns out to be pretty easy. All information needed for can be found in the Deep Earth source code – a wee bit adapted for for the Bing Map Control. First, I made an enum that defines all the layers that Google provides

namespace LocalJoost.TileSource
{
  public enum GoogleTileSourceType
  {
    Street,
    Hybrid,
    Satellite,
    Physical,
    PhysicalHybrid,
    StreetOverlay,
    WaterOverlay
  }
}

Then, the actual tile calculating class:

using System;

namespace LocalJoost.TileSource
{
  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);
          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 ' ';
    }
  }
}

As you can see, this is not quite rocket science. To make the Bing Maps control use this, you need to expand your XAML a little. First, you have to add two namespaces to your MainPage.Xaml:

xmlns:MSPCMCore="clr-namespace:Microsoft.Phone.Controls.Maps.Core;assembly=Microsoft.Phone.Controls.Maps" 
xmlns:LJTileSources="clr-namespace:LocalJoost.TileSource;assembly=LocalJoost.TileSource" 

Then, you have to use a third mode for the Bing Map – Mercator. This basically only tells the map to operate in Mercator mode, but not to attach any default imagery. And here we go:

<Microsoft_Phone_Controls_Maps:Map x:Name="map" 
   CredentialsProvider="your credentials" >
  <Microsoft_Phone_Controls_Maps:Map.Mode>
    <MSPCMCore:MercatorMode></MSPCMCore:MercatorMode>
  </Microsoft_Phone_Controls_Maps:Map.Mode>
  <Microsoft_Phone_Controls_Maps:Map.Children>
    <Microsoft_Phone_Controls_Maps:MapTileLayer>
      <Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources>
        <LJTileSources:GoogleTileSource TileSourceType="Satellite"/>  
      </Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources>
    </Microsoft_Phone_Controls_Maps:MapTileLayer>
  </Microsoft_Phone_Controls_Maps:Map.Children>
</Microsoft_Phone_Controls_Maps:Map>
Presto. Google Maps for Windows Phone 7. And oh, if you want to project another layer on top of it, for instance Google Streets, like I did, you just add more tilelayers:
<Microsoft_Phone_Controls_Maps:Map x:Name="map" 
   CredentialsProvider="your credentials" >
<Microsoft_Phone_Controls_Maps:Map.Mode>
  <MSPCMCore:MercatorMode></MSPCMCore:MercatorMode>
</Microsoft_Phone_Controls_Maps:Map.Mode>
<Microsoft_Phone_Controls_Maps:Map.Children>
  <Microsoft_Phone_Controls_Maps:MapTileLayer>
    <Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources>
      <LJTileSources:GoogleTileSource TileSourceType="Satellite"/>
    </Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources>
  </Microsoft_Phone_Controls_Maps:MapTileLayer>
  <Microsoft_Phone_Controls_Maps:MapTileLayer>
    <Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources>
      <LJTileSources:GoogleTileSource TileSourceType="Street"/>
    </Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources>
  </Microsoft_Phone_Controls_Maps:MapTileLayer>
</Microsoft_Phone_Controls_Maps:Map.Children>
</Microsoft_Phone_Controls_Maps:Map>

The Bing Maps control makes you able to zoom and pan trough all this. Okay, Google Maps does a little more than this, but this shows pretty well how darn easy it is to make a good looking, pretty fully functional mapping application showing totally different imagery.

Conclusion: using the Bing Maps Control for showing raster maps is so bloody easy that I almost start to wonder if it’s still justified to feel proud ‘being a GIS guy’. Almost ;-)