Part 27 - Working with the Map Control and the Geolocation and GeoPosition Classes

Sign in to queue

Description

Download the source code for this lesson at http://absolutebeginner.codeplex.com/

In this lesson, we're going to talk about the map control and talk about the GPS sensor inside of the device. Besides the specifics of the GPS sensor, the more general principles of working with the Phone's sensors apply here as well. So, while we are examining a specific capability, you can apply the general knowledge to all the capabilities of the Phone and how to work with them. The Phone's API always supplies classes and methods representing the particular sensor and its functionality you want to work with. In the case of the Phone's location features, we'll be able to get the latitude and the longitude in a GeoPosition object that we can then work with. What can we do with the location, the GeoPosition object? For one thing, it works well with the Map Control. We can set current position of the Map Control to a specific location (GeoPosition) or retrieve the current location from the Map control and ask "Where in the world are you displaying?" We can zoom into the map, zoom out of the map, and things of that nature.

We’ll start by opening the New Project dialog (1) creating a new Blank App project, (2) renaming it to LocationAndMaps, and (3) clicking the OK button.

clip_image002[1]

The easiest way to get started working with the Map Control is to use the Toolbox. Usually, I advocate directly typing in the XAML, however as we’ll see in a moment, the Map Control requires a bit of extra work that the drag-and-drop action will take care of for us:

clip_image004[1]

Here I resize the Map Control on the design surface so that we can see the message that it only renders when the app is running:

clip_image006[1]

In addition to the XAML that was generated inside of the Grid, notice the namespace that was added to the Page’s namespace declarations:

clip_image002

This is one reason why I prefer to drag-and-drop to the design surface when working with the MapControl.

Next, I’ll edit the layout of the Grid and the Map.  Ideally, it takes up the first row set to *:

clip_image004

And when I run the app, notice that the Map is simply set to North America (at least, since I am in North America.  I’m not sure what would happen if you tried this from other parts of the world).

clip_image006

The first thing I want to accomplish is the set the MapControl, by default, to a specific location.  I’ll choose an out door mall near the Microsoft campus in Redmond, Washington, as the location of the Phone using the Emulator’s Additional Tools dialog:

clip_image008

Back in my project, in the MainPage.xaml.cs I’ll modify the OnNavigatedTo() event handler method to work with the Geolocator.  This object is your interface to the GPS / location features of the Phone.  Since we’re running in emulation, I would expect to retrieve the location in Redmond, Washington that I set.  Once I have the Geolocator retrieving the location, I attempt to set the MapControl’s view to that location:

clip_image010

A few things I want to point out.  Notice that we’re setting the DesiredAccuracyInMeters to 50.  Due to technological features of the phone and the GPS system, it is not always possible to get an exact location of the phone.  It will try different techniques to get as close as possible.  For example, it will not only use the GPS satellite, but it may also use wifi signals and cell tower signals to attempt to triangulate the location. Depending on how accurate you need this to be and what the resources are near the device at the given time that you're attempting to retrieve the position, it could take longer because it has to account for all of these possibilities, wifi signals that are nearby, cell towers, GPS in order to find the location.   Honestly, I don't understand how all that works. 

However, at some point, we're going to obtain a Geoposition object providing us with a Coordinate.  A Coordinate is made up of positioning information.  The most important for our purposes is a Point.  A Point is simply just a combination of the latitude and longitude and we'll use that information to try and set the view for the map

Whenever you see that key word “try” in a method name, that means it's going to return back to you a true or false: true if the operation was successful, false if it wasn’t.  For our purposes in this pithy example, we’ll ignore the return value.  In other words, if TrySetViewAsync() fails, then it just fails and it will be quiet about it.

In the TrySetViewAsync() we're passing a Point which is a Geocoordinate position which is basically what we get from position.Coordinate.  We also pass in the zoom level.  A setting of zero would zoom to see all of North and South America, and a zoom level of 20 would be where we can see in our own back yards.  It is a double data type so I add the D next to it just is a way of making the value 18 into a double literal.

Before we run the app, we’ll need to add the Location capability to our app.  Open the package.appxmanifest, go to the (1) Capabilities tab, and (2) select the Location capability:

clip_image012

Now we should be able to run the application and then see our map control some where near this Redmond Town Center in Redmond, Washington.  Unfortunately, I don’t know the streets of Redmond, Washington well enough to verify how far the location we’re given in the MapControl is to the location we set in the Emulator’s Location tab, but I’ll assume it is close.

clip_image014

Next, I’ll implement two Button controls to set and retrieve the current position of the map.  I’ll add two more row definitions and setting the height of each to 50:

clip_image016

As a means of further exercising the Geolocation and Map Control, we'll write out the current position of the Map Control to a TextBlock and I’ll programmatically set the position of the Map Control.  Two Button controls will trigger these actions.  So, I’ll add the following XAML inside the Grid:

        <StackPanel Orientation="Horizontal" Grid.Row="1">
            <Button Name="getPositionButton"
                    Content="Get Position"
                    Click="getPositionButton_Click"
                    Margin="0,0,20,0" />
            <Button Name="setPositionButton"
                    Content="Set Position"
                    Click="setPositionButton_Click"
                    Margin="0,0,20,0" />
        </StackPanel>

        <TextBlock Name="positionTextBlock"
                   Grid.Row="2"
                   FontSize="22" />

I’ll create method stubs for the getPositionButton_Click and setPositionButton_Click by putting my mouse cursor on their names in the XAML and pressing F12.

In each of those two methods, I’ll add the following code:

        private void getPositionButton_Click(object sender, RoutedEventArgs e)
        {
          positionTextBlock.Text = String.Format("{0}, {1}",
              MyMap.Center.Position.Latitude,
              MyMap.Center.Position.Longitude);
        }

        private async void setPositionButton_Click(object sender, RoutedEventArgs e)
        {
          var myPosition = new Windows.Devices.Geolocation.BasicGeoposition();
          myPosition.Latitude = 41.7446;
          myPosition.Longitude = -087.7915;

          var myPoint = new Windows.Devices.Geolocation.Geopoint(myPosition);
          if (await MyMap.TrySetViewAsync(myPoint, 10D))
          {
            // Haven't really thought that through!
          }

        }

Hopefully all of this looks familiar, or at least, straight forward. 

In the getPositionButton_Click I’m merely retrieving the Map’s Center position, grabbing the latitude and logitude and printing to a TextBlock.  This demonstrates how to retrieve all or part of the current location OF THE MAP CONTROL which can be useful when building Map-centric apps.

In the setPositionButton_Click, I’m hard-coding the position of the Map.  Again, I’m using TrySetViewAsync() to do the heavy lifting.  However, this time I’m creating a BasicGeoposition object to contain the latitude and longitude and then creating a Geopoint using the BasicGeoposition as a constructor argument. 

When I run the app, I can retrieving the current position into a TextBlock by clicking the “Get Position” button …

clip_image018

… and can set the position of the Map to a specific location near where I grew up by clicking the “Set Position” button:

clip_image020

Next, I’ll add a Slider control to perform zooming.  I’ll add the following XAML to the Grid:

        <Slider Name="mySlider"
            Maximum="20"
                Minimum="10"
                ValueChanged="Slider_ValueChanged"
                Grid.Row="3"
                />

And will use the F12 shortcut to create the Slider_ValueChanged event handler method, to which I add the following code:

        private void Slider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
        {
          if (MyMap != null)
            MyMap.ZoomLevel = e.NewValue;
        }

Simply put, the slider will allow me to move from 10 to 20 (extreme close up). 

Finally, I’ll initiate the Slider’s value at start up by setting its value to the Map Control’s current ZoomLevel in the OnNavigatedTo event handler method:

clip_image022

Now, when I run the app, I can adjust the zoom level:

clip_image024

I can reset the position using the “Set Position” button, and can zoom in dramatically:

clip_image026

Finally, if you want to build apps that include the Map Control, you’ll need to provide a MapServiceToken.  You’ve see the warning message each during run time.  You can obtain a MapServiceToken from the Windows Phone Dev Center after you register your app (which we do not cover in this series).  You would set it like so:

clip_image028

Tags:

C#, Windows Phone

Embed

Download

Download this episode

The Discussion

Add Your 2 Cents