Windows Phone 7 Silverlight: Location Services

0
78
10 min read

 

(For more resources on this subject, see here.)

 

Introduction

One of the most powerful features of smartphones today is location awareness. Windows Phone 7 is no exception. The wide consumerization of GPS around 10 years ago brought handheld GPS receivers for consumers on the go, but few individuals could justify the expense or pocket space. Now that smartphones have GPS built in, developers have built incredibly powerful applications that are location-aware. For example, apps that help users track their jogging route, get real-time navigation assistance while driving, and map/analyze their golf game.

In this article, we will take a deep dive into the location API for Windows Phone 7 by building an application to help navigate during travel and another to map the user’s location.

Tracking latitude and longitude

In this recipe, we will implement the most fundamental use of location services, tracking latitude and longitude. Our sample application will be a navigation helper which displays all the available location information. We will also review the different ways in which the phone gets its location information and their attributes.

Getting ready

We will be working in Visual Studio for this tutorial, so start by opening Studio and creating a new Windows Phone 7 application using the Windows Phone Application project template.

All the location/GPS-related methods and classes are found in the System.Deviceassembly, so add this reference next:

Windows Phone 7 Silverlight: Location Services

We will need some UI to start tracking and displaying the data, so go to the MainPage.xaml file, if it’s not already open. Change the ContentPanel from a Grid to a StackPanel, then add a button to the designer, and set its Content property to Start Tracking. Next add four TextBlocks. Two of these will be Latitude and Longitude labels. We will use the others to display the latitude/longitude coordinates, so set their x:Name properties to txtLatitudeand txtLongitude respectively. You can also set the application and page titles if you like. The resulting page should look similar to the following screenshot:

Windows Phone 7 Silverlight: Location Services

How to do it…

The core class used for tracking location is the GeoCoordinateWatcher. We subscribe to the events on the watcher to be noticed when changes occur:

  1. Double-click on your button in the designer to go to the click event handler for the button in the code behind file. This is where we will start watching for location changes.
  2. Create a GeoCoordinateWatcher field variable named _watcher. Set this field variable inside your click event handler to a new GeoCoordinateWatcher. Next add a handler to the PositionChanged event named _watcher_PositionChanged. Then start watching for position changes by calling the Start method.
  3. Next add a handler to the PositionChanged event named _watcher_PositionChanged. Then start watching for position changes by calling the Start method.
  4. In order to use the position information, create the void handler method with parameters named sender of type object and e of type GeoPositionChangedEventArgs<GeoCoordinate>. Inside this method set the Text properties on the txtLatitude and txtLongitude text boxes to the coordinate values e.Position.Location.Latitude and e.Position.Location.Longitude respectively.

Latitude and longitude as strings:
Latitude and longitude are of type double and can be converted to strings using the ToString method for display.

You should end up with a class that is similar to the following block of code:

public partial class MainPage : PhoneApplicationPage
{
private IGeoPositionWatcher<GeoCoordinate> _watcher;
public MainPage()
{
InitializeComponent();
}
private void butTrack_Click(object sender, RoutedEventArgs e)
{
_watcher = new GeoCoordinateWatcher();
_watcher.PositionChanged += _watcher_PositionChanged;
_watcher.Start();
}
void _watcher_PositionChanged(object sender,
GeoPositionChangedEventArgs<GeoCoordinate> e)
{
txtLatitude.Text = e.Position.Location.Latitude.ToString();
txtLongitude.Text = e.Position.Location.Longitude.
ToString();
}
}

That’s it. You can now deploy this app to your phone, start tracking, and see the latitude and longitude changes on your screen.

How it works…

The watcher starts a new background thread to watch for position changes. Each change is passed to your event handler(s) for processing.

Window Phone 7 provides location services through the following three sources:

  • GPS: Satellite based
  • Wi-Fi: Known wireless network positions
  • Cellular: Cellular tower triangulation

Each of these position providers has their strengths and weaknesses, but the combination of the three covers nearly any possible use case:

  • GPS is the most accurate, but you must have an unobstructed view of the sky
  • Wi-Fi can be accurate depending on how close you are to the access point, but you must be in the range of a known wireless network
  • Cellular is the least accurate and only needs cell signal

So if you’re in an urban area with tall buildings, GPS may be intermittent but Wi-Fi networks and cellular coverage should be plentiful. If you are in a rural area, GPS should work well and cellular triangulation might help where available.

Tracking altitude, speed, and course

In this section, we will discuss the different types of location information that are provided by the GeoCoordinateWatcher and how they might be used. A quick look at the Object Browser shows us that the GeoCoordinate object has several interesting properties:

Windows Phone 7 Silverlight: Location Services

In addition to Latitude and Longitude, there is Altitude, Speed, and Course, among others. Altitude and Speed are pretty self-explanatory, but Course might not be as obvious. Course is your heading or the direction you are going, given two points. The following table shows each property and its unit of measurement:

Windows Phone 7 Silverlight: Location Services

Horizontal and Vertical Accuracy specifies the accuracy of Latitude/Longitude and Altitude, respectively, in meters. For example, this means your actual Latitude position is between the reported Latitude minus the accuracy value and the reported Latitude plus the accuracy value. The smaller the accuracy value, the more accurate but the longer it may take to get a position.

Getting ready

Add three more sets of TextBlock controls under the longitude control for each of the following properties: Altitude, Speed, and Course. Set the speed label TextBlock Text property to Speed (mph). Name the TextBlock controls as you did for latitude/longitude so we can assign their Text properties from the code behind. The page should look similar to the following screenshot:

Windows Phone 7 Silverlight: Location Services

How to do it…

Perform the following steps to add altitude, speed, and course to the application:

  1. Open the code behind file for the page, and in the positionChanged handler, set Altitude in the same way as we did for latitude/longitude before; simply set the Text property of the txtAltitude TextBlock to the Altitude property as a string.
  2. For the Speed property, convert from meters per second to miles per hour. One meter/sec equals 2.2369363 miles per hour, so we can multiply the Speed property by 2.2369363 to get miles per hour.
  3. Display Course so that the normal users can understand it, using the name of the direction (that is, North, South, East, West). The Course value is a degree value from 0 to 360, where 0/360 is north and the degrees go clock-wise with a compass.
  4. Create a series of if statements that will provide the correct heading. Between 316 and 45 will be North, 46 and 135 will be East, 136 and 225 will be South, and between 226 and 315 will be West. Our _watcher_PositionChanged method is now as follows:

    void _watcher_PositionChanged(object sender, GeoPositionChangedEve
    ntArgs<GeoCoordinate> e)
    {
    txtLatitude.Text = e.Position.Location.Latitude.
    ToString()
    txtLongitude.Text = e.Position.Location.Longitude.
    ToString();
    txtAltitude.Text = e.Position.Location.Altitude.
    ToString();
    txtSpeed.Text = (e.Position.Location.Speed *
    2.2369363).
    ToString();
    double course = e.Position.Location.Course;
    string heading = string.Empty;
    if (course >= 46 && course <= 135)
    heading = "East";
    if (course >= 136 && course <= 225)
    heading = "South";
    if (course >= 226 && course <= 315)
    heading = "West";
    else
    heading = "North";
    txtCourse.Text = heading;
    }

How it works…

If you deploy the application to your phone now, you will see Speed display NaN (Not a Number) , Altitude display zero, and Course is blank. This is because Altitude, Speed, and Course are only available when you specify that you want high accuracy location information. We do this by instantiating the GeoCoordinateWatcher with a GeoPositionAccuracy type of GeoPositionAccuracy.High in the constructor. By default, the accuracy is set to GeoPositionAccuracy.Default, which only uses cellular triangulation and is not accurate enough to calculate speed, altitude, or course. GeoPositionAccuracy.High uses GPS and Wi-Fi, when available, which provides more accurate positions. Although it is more accurate, it also uses more power and can take longer to get your position. This is why High is not the default. It is strongly recommended that you only use the higher accuracy when it is absolutely needed.

In this case, we need the Altitude, Speed, and Course, so it is necessary. Set the accuracy level to high in the GeoCoordinateWatcher constructor , like so:

_watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);

If you redeploy the application to the phone, you may notice it still shows NaN for Speed. This may be because you are indoors and have an obstructed view of the sky or it may just take a few moments to get a good signal. Once you have a good GPS signal, you should see valid Speed, Altitude, and Course values. The best way to test this application is in the passenger seat of a driving vehicle so you can compare the vehicles, speedometer to the speed in the application.

There may be times, as well, when you lose GPS signal. When this occurs, the latitude and longitude values will also be set to NaN. In such cases, you may want to give the user a friendlier explanation of the problem. You can simply check the IsUnknown property in the position changed event and provide a better message. For example:

void _watcher_PositionChanged(object sender, GeoPositionChanged
EventArgs<GeoCoordinate> e)
{
if (e.Position.Location.IsUnknown)
{
txtLatitude.Text = "Finding your position.
Please wait ...";
txtLongitude.Text = "";
txtAltitude.Text = "";
txtSpeed.Text = "";
txtCourse.Text = "";
return;
}
txtLatitude.Text = e.Position.Location.Latitude.
ToString();
txtLongitude.Text = e.Position.Location.Longitude.
ToString();
txtAltitude.Text = e.Position.Location.Altitude.
ToString();
txtSpeed.Text = (e.Position.Location.Speed * 2.2369363).
ToString();
double course = e.Position.Location.Course;
string heading = string.Empty;
if (course >= 46 && course <= 135)
heading = "East";
if (course >= 136 && course <= 225)
heading = "South";
if (course >= 226 && course <= 315)
heading = "West";
else
heading = "North";
txtCourse.Text = heading;
}

The last property we will cover in this recipe is the Permission property on the GeoPositionWatcher. Before submitting your app to the marketplace, you must define which phone capabilities your app requires. One of those capabilities is location. Before a user installs an application, he/she is informed of the capabilities the app requires and must accept them to install. Even though the user has given the app permission to use location services of the phone, the user can still turn off location services for all apps from the settings menu. The Permission property will help us check for this and tell the user why the app isn’t working.

There is a slight trick though; the Permission property will be set to Granted when the watcher is first created, even if Location services are disabled in the Settings menu. It will be reset to Denied after the Start method is called. So we must check for a Denied permission value after calling the Start method. For instance:

private void butTrack_Click(object sender, RoutedEventArgs e)
{
_watcher = newGeoCoordinateWatcher(GeoPositionAccuracy.High);
_watcher.PositionChanged += _watcher_PositionChanged;
_watcher.StatusChanged += _watcher_StatusChanged;
_watcher.Start();
if (_watcher.Permission == GeoPositionPermission.Denied)
tbLatitude.Text = "Please enable location services and retry";
}

We can test this by turning off location services. From the start screen, flick left to the App list, tap Settings, and then tap location. Swipe the switch left to the Off position. Redeploy the application to your phone, click the Start Tracking button, and you will see our new message.

As mentioned previously, the user must accept the capabilities of the application before installing it. There may be future updates to the phone which allow the user to change the allowed capabilities of individual apps from the settings menu as well. The Permission property would also be useful in this scenario.

There’s more…

You may have also noticed the CivicAddressResolver and CivicAddress classes in the System.Device.Location namespace . As its name implies, the CivicAddressResolver returns an address from a GeoCoordinate. Unfortunately, this is not yet implemented for Windows Phone. You can instantiate them and attempt to use them, but the returned CivicAddress will always be unknown. Hopefully, this will be implemented in the future updates of the operating system.

LEAVE A REPLY

Please enter your comment!
Please enter your name here