19 October 2010

Google Maps for Windows Phone 7 using the Bing Maps Control

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 ;-)

19 comments:

peSHIr said...

*Great* post.

So... Bing Maps control as a ready map slippy map, huh? (Or I possibly misread this article; I'm hacking a bit of WP7 myself while watching some TV...)

If so it should be much easier than I thought to be able to use Open Street Map tiles in a WP7 application: just the right x/y/zoom to tile URL map.

Cool!

nzben said...

Interesting solution. What does the Google Maps API terms of service say about using it in this manner? :)

Loc#alJoost said...

@nzben I am pretty sure the Google TOS does not allow this. But now it's so easy now use their data in a Silverlight/Windows Phone 7 app they better start doing something or else someone ELSE will make Google Maps for Windows Phone 7. Possibly me ;-)

Loc#alJoost said...

@peSHir using OpenStreetMaps is just as easy. Maybe I'll get around to post code for that as well, have it around somewhere, just needs a little adaptation for the Bing Map Control. I thought using Google Maps a bit more spectacular ;-)

peSHIr said...

Great that you are mentioneed on wpcentral.com.
Signed up for an account there to comment on the article by Daniel Rubino (well, his footnote), but I still can't seem to log on. So I'll just dump that here for now if you don't mind?

marypcb said...

I'm curious; is there anything in the Bing maps control that gives T&Cs fro what map data you can use it to consume?

Frank Steggink said...

Here is another requestto use OpenStreetMap in the Bing Maps control :) Especially since Bing is using OpenStreetMap itself.

Naveed said...

can you please tell me how could i place a layer having routes from Google map

Loc#alJoost said...

@naveed First of all, the control I am using is BING maps, it only loads data from Google - courtesy of the flexibility Microsoft built into the control. Second - you could easily add a second raster layer with route data - if you have the backend power to generate something like that. If you have not, you will have to go into vector layers. I am still looking into that one myself. The hard part is getting the data. If you have an API somewhere that can give you a string of lat/lon waypoints as return value, then showing a route on top of the map is pretty easy. I will blog about that some day.

peSHIr said...

And another use of the Bing Maps controls: Google Sky tile source example at http://www.scottlogic.co.uk/blog/colin/2011/02/google-sky-on-windows-phone-7

meir said...

i understand how 2 use the map but how can i request to begin in a certin coordinate?
when i ask for it it keeps sending me to the same location with no regard to what coordinates i give it

meir said...

when i use the line:
UriFormat = @"http://maps.google.com/maps/api/staticmap?center={0:0.000000},{1:0.000000}&zoom={2}&size=1000x1000&sensor=false";

instead of using the original u gave with your code for some reason instead of giving me the map i need it shows me 4 copys of the same map i requested
any idea of how i can solve this?

Loc#alJoost said...

@meir Like you said yourself - you use the static Google API. I don't do that, and I have no idea how that static api works.

Ross said...

Great work! You saved me a lot of time. :)

fithri selva said...

nice info..
by the way, can we add pushpin like in bing maps? can you give example..

HybridDriver said...

How do I modify this to return my own map tiles located in local storage? thanks.

Joost van Schaik said...

@hybriddriver I wish I new. I know it can be done.

rbrundritt said...

This is against the Bing Maps and Google Maps terms of use. Any application found to use Google Maps in a Bing Maps control will be removed from the Windows Phone Marketplace.

Joost van Schaik said...

@rbrundritt Correct! This is ment to be a challenge to Google. They did not respond. In the mean time, you can find various apps in the Windows Phone Marketplace showing Google Maps tiles. Now I wonder where they find the code to do that ;-)