Building Location-Aware Apps for Your Surface RT

Microsoft’s Surface RT lacks a GPS receiver (bummer!), but you can still use WinRT’s location API to build location-aware apps for it. As long as you have a WiFi connection, the location API can determine where you are with a reasonable degree of accuracy – sometimes with astonishing accuracy – using WiFi positioning. Furthermore, you can combine the location API with the Bing Maps control available in the Bing Maps SDK for Windows Store Apps to present location data in a rich and informative way.

To demonstrate, I built a simple app that 1) retrieves the user’s current location, 2) shows that location on a map, and 3) shows the address of any location on the map that the user taps. Addresses are determined by feeding a latitude and longitude into Bing Maps’ reverse-geocoding service. Here’s what the app looked like when I started it up in the basement of my home:

image

And here’s how it looked when I zoomed across the country and tapped Building 37 on the Microsoft campus:

image

What does it take to write an app like this? Remarkably little. You begin by downloading and installing the Bing Maps SDK for Windows Store Apps on your development machine.  Then, in a Visual Studio Windows Store project, you use the Add Reference command to add a reference to Bing Maps and the Microsoft Visual C++ Runtime Package:.

image

Next, you go into Configuration Manager (you’ll find it in the Build menu), set Active Solution Platform to the platform you’re running Visual Studio on, which is probably x64. You can’t, unfortunately, use “Any CPU” as you normally would in a C# project because the Bing Maps SDK relies on unmanaged code:

image

Finally, open the app manifest and check the Location box on the Capabilities page. This is essential because an app that uses the location API must indicate as much in its manifest:

image

Now you’re ready to start adding code. Open MainPage.xaml and replace the empty Grid in the body of the page with this:

<Page.Resources>
    <x:String x:Key="Credentials">Insert key here</x:String>
</Page.Resources>

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}" xmlns:maps="using:Bing.Maps">
    <maps:Map x:Name="BingMap" Credentials="{StaticResource Credentials}" MapType="Aerial" Tapped="OnMapTapped">
        <maps:Map.Children>
            <maps:Pushpin x:Name="Pin" Width="50" Height="50">
                <maps:Pushpin.Template>
                    <ControlTemplate>
                        <Grid>
                            <Ellipse Width="50" Height="50" Fill="Red" />
                            <Ellipse Width="40" Height="40" Fill="White" />
                            <Ellipse Width="30" Height="30" Fill="Red" />
                            <Ellipse Width="20" Height="20" Fill="White" />
                            <Ellipse Width="10" Height="10" Fill="Red" />
                        </Grid>
                    </ControlTemplate>
                </maps:Pushpin.Template>
            </maps:Pushpin>
        </maps:Map.Children>
    </maps:Map>
</Grid>

Before you go any further, replace “Insert key here” in the string resource named “Credentials” with your Bing Maps API key. If you don’t have a Bing Maps API key, you can get one by going to the Bing Maps Account Center and requesting one. It’s fast and it’s free.

The next step is to open MainPage.xaml.cs and replace the OnNavigatedTo method with the following methods:

protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    // Get the current location
    var locator = new Geolocator();
    var position = await locator.GetGeopositionAsync();

    // Center the map on the current location
    var location = new Location(position.Coordinate.Latitude, position.Coordinate.Longitude);
    BingMap.SetView(location, 18.0); // Zoom level == 18

    // Show the current position with a pushpin
    MapLayer.SetPosition(Pin, location);
}

private async void OnMapTapped(object sender, TappedRoutedEventArgs e)
{
    // Get the position of the tap
    var map = (Map)sender;
    var pos = e.GetPosition(map);

    // Convert the position into a map location
    Location location;
    map.TryPixelToLocation(pos, out location);

    // Convert the map location into an address
    var Client = new HttpClient();
    var result = await Client.GetStringAsync(string.Format("http://dev.virtualearth.net/REST/v1/Locations/{0},{1}?o=xml&key={2}", location.Latitude.ToString(CultureInfo.InvariantCulture), location.Longitude.ToString(CultureInfo.InvariantCulture), this.Resources["Credentials"]));

    var doc = XDocument.Parse(result);
    var address = doc.Descendants().Elements().Where(n => n.Name.LocalName == "FormattedAddress").FirstOrDefault();

    if (address != null)
        await new MessageDialog(address.Value).ShowAsync();
    else
        await new MessageDialog("Address not available").ShowAsync();
}

Now test the project on your local machine to make sure you haven’t made any mistakes. If it builds and runs OK, you’re ready for the final step: building an ARM package and deploying it to your Surface.

Due to a bug in the current release of the Bing Maps SDK – a bug that will be fixed soon – a debug build of an app that uses Bing Maps will crash the moment it starts up on an ARM device such as a Surface RT. Therefore, you need to generate an ARM package containing a release build of the app and deploy the package manually. Generating the package is no more difficult than using the Project –> Store –> Create App Packages command to start the Create App Packages wizard and specifying the type of package you want:

image

Once the package is generated, copy it to a USB stick, walk the USB stick over to your Surface and plug it in, and run the PowerShell script named Add-AppDevPackage.ps1, which you’ll find among the package files. Once that’s done, you can start the app from its tile on the start screen.

We deliver solutions that accelerate the value of Azure.

Ready to experience the full power of Microsoft Azure?

Start Today

Blog Home

Stay Connected

Upcoming Events

All Events