Accessing Location Information
The A-GPS on the phone enables you to
retrieve location information without having to work with the sensor
directly. You can use the Geolocator
class to retrieve location information. This is the WinRT API for accessing geolocation information. When using the Geolocator
class, you can specify the accuracy you’re going to need for your application:
// Normal Accuracy
var locator = new Geolocator();
locator.DesiredAccuracy = PositionAccuracy.High;
The level of accuracy affects not only how
accurate the information is, but also how fast you can get the
information back. With the default level of accuracy, the Geolocator
class will return after it can get a location instead of waiting for high-accuracy information.
When developing your application, you need to
also determine how often you need the geolocation information.
Retrieving geolocation information can be a drain on the battery, so
deciding whether you need a one-time location or need to constantly
monitor the change of location should be part of your application
design. Let’s look at ways to do both.
One-time Geolocation
The Geolocator
class uses the
asynchronous programming features new to Windows Phone 8. To perform a
one-time retrieval of the location of the phone, you can call the Geolocator
class’s GetGeopositionAsync
method. To use this method, you will need to decorate your method with the async
modifier (as shown in the following code) and use the await
keyword to tell the API to run the operation asynchonrously:
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var locator = new Geolocator();
Geoposition location = await _locator.GetGeopositionAsync();
}
By using await
, the method will
wrap the operation in an asynchronous handler so you can simply write
the code as if it were asynchronous. If location information is not
available, the method will return a null reference. After you have the Geoposition
object, you can simply see the location information (or even the civic information like city, state, country, and postal code):
var locator = new Geolocator();
Geoposition location = await _locator.GetGeopositionAsync();
if (location != null)
{
// Get coordinates
double latitude = location.Coordinate.Latitude;
double longitude = location.Coordinate.Longitude;
double? altitude = location.Coordinate.Altitude;
// Get civic location information
string country = location.CivicAddress.Country;
string city = location.CivicAddress.City;
}
You can also check the status of the location by using the Geolocator
class’s LocationStatus
property:
var locator = new Geolocator();
Geoposition location = await _locator.GetGeopositionAsync();
if (location != null)
{
// Get coordinates
double latitude = location.Coordinate.Latitude;
double longitude = location.Coordinate.Longitude;
double? altitude = location.Coordinate.Altitude;
// Get civic location information
string country = location.CivicAddress.Country;
string city = location.CivicAddress.City;
}
else
{
if (locator.LocationStatus == PositionStatus.Disabled)
{
statusMessage.Text = "Location Information has been disabled";
}
}
The LocationStatus
property returns the status of the location information. This can be one of six statuses:
• Ready: Location information is available.
• Initializing: Location information should be available soon. The device is initializing.
• NoData: The device
is working correctly, but no data is available. This could be because
none of the different location information is available (no GPS, no
cell towers, and no Wi-Fi).
• NotInitialized: The location services haven’t been initialized. This usually happens before calling the GetPositionAsync
method the first time.
• NotAvailable: The device doesn’t support location services.
• Disabled: Location
information was disabled for this application. This is usually if the
user was asked and rejected the permission to let your application use
location information or the user has disabled location services on the
phone.
For this one-time use, the Geoposition
result is all you need. The data available from the Geoposition
class includes
• Latitude: This is the current latitude of the phone (location on the surface of the Earth).
• Longitude: This is the current longitude of the phone (location on the surface of the Earth).
• IsUnknown: This says whether the coordinate is invalid or unknown.
• Speed: This is an approximate speed of the user in meters per second.
• Course: This is an approximate heading as related to true north (0–360 degrees).
• Altitude: This is the approximate altitude above sea level.
• HorizontalAccuracy: This is the distance of uncertainty in this location information (in meters).
• VerticalAccuracy: This is the distance of uncertainty in this location information (in meters).
Tracking Geolocation Changes
For applications that require continuous
monitoring of the user’s movements, you can monitor the geolocation
changes as they occur. Like many of the devices on the phone, using the
geolocation functionality for long periods can adversely affect battery
life. To combat this, you will want to be judicious in your use of this
feature.
Continuously monitoring the geolocation of the
phone is similar to a one-time retrieval of location information, but
the method structuring the code is a little different. You should start
by creating a Geolocator
as a class-level variable (to maintain its lifetime) and register for both the StatusChanged
and the PositionChanged
events from the UI thread (for example, in response to an AppBar button click):
private void startButton_Click(object sender, EventArgs e)
{
// Make sure we can only call it after
((ApplicationBarIconButton)sender).IsEnabled = false;
// Wire up for events
_locator.PositionChanged += _locator_PositionChanged;
_locator.StatusChanged += _locator_StatusChanged;
}
Depending on your particular use-case, you also
should specify when to update you with location information. You can
set both time and movement thresholds that will dictate how often to
update you on location changes:
// Set Thresholds
_locator.ReportInterval = 2000; // 2 seconds
_locator.MovementThreshold = 100; // 100 meters
// Wire up for events
_locator.PositionChanged += _locator_PositionChanged;
_locator.StatusChanged += _locator_StatusChanged;
The ReportInterval
property accepts time in milliseconds between calls. The MovementThreshold
specifies how far the user needs to move before you are notified.
In both of the event handlers, you will have to marshal the data to the UI thread (using Dispatcher.BeginInvoke
). For example, in the StatusChanged
event you will need to tell the user what is happening based on the statuses that are returned, like so:
void _locator_StatusChanged(Geolocator sender,
StatusChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
switch (args.Status)
{
// Tell the user the location information is coming
case PositionStatus.Initializing:
statusMessage.Text = "Location Initializing";
break;
// Alert the user that data isn't available currently
case PositionStatus.NoData:
statusMessage.Text =
"No location available...try again later";
break;
// If status is Ready, then PositionChanged fired correctly
case PositionStatus.Ready:
statusMessage.Text = "Receiving Location Information";
break;
// If Disabled, tell the user
case PositionStatus.Disabled:
statusMessage.Text = "Location Information is disabled";
break;
}
});
}
Because these statuses can change although your application is running, you should continue to monitor the PositionStatus
values and keep the user apprised of the validity of this information.
Note that, unlike the one-time retrieval, we do not get the status
information from the watcher when the Ready
status fires. This is because the other event (PositionChanged
) will be fired as the location changes:
void _locator_PositionChanged(Geolocator sender,
PositionChangedEventArgs args)
{
Dispatcher.BeginInvoke(() =>
{
// Just bind the position to the UI so we can data-bind to it
DataContext = args.Position.Coordinate;
});
}
Turning Coordinates into Addresses
Now that you have basic coordinates
of where the phone is on the surface of the Earth, you will want to
turn that into something the user can actually use.