1. Problem
You want an application (or
game) that gives information about the area where your user is located.
(For a game, you could choose to let the user play in the city where the
user is located, against other players around there.)
2. Solution
To solve this problem, you'll use
a control that uses Bing Maps tailored for WP7., but much more
important of how you will show data, is how you will get them, thanks to
the GeoCoordinateWatcher class contained in the namespace System.Device.Location.
3. How It Works
The GeoCoordinateWatcher class provides location data based on coordinates of latitude and longitude. Like other sensors, the cycle is defined by the Start method (which enables you to access to the Position property and enable the PositionChanged event) and Stop (which prevents GeoCoordinateWatcher from providing events and data on the position).
4. The Code
As you can see in Figure 1,
the constructor has an overload that enables you to specify the degree
of accuracy that you want in determining the user's location (the
property DesiredAccuracy is read-only). If you set the accuracy to High,
you will have a more precise location of the user, but this requires
more resources that will easily discharge the battery. Therefore, use
this setting only if needed, because you don't want to risk the user
rejecting your application because it is too resource-intensive.
There are two important events that you must take into consideration:
PositionChanged
StatusChanged
...
Then we start to see the private method that initialize the GeoCoordinateWatcher and subscribes the two events with our handlers
private void InitWatcher()
{
geoWatcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
geoWatcher.PositionChanged += new
EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoWatcher_PositionChanged);
geoWatcher.StatusChanged += new
EventHandler<GeoPositionStatusChangedEventArgs>(geoWatcher_StatusChanged);
}
...
The respective handler is as follows:
...
void geoWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
//Handle here the change of status
}
void geoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//Handle here the change of position
}
...
Let us explain the purpose of these thow event-handlers:
geoWatcher_StatusChanged takes care of notifying that the status of GeoCoordinateWatcher has changed.
geoWatcher_PositionChanged is the most important. The event handler is called when the PositionChanged event occurs, and communicates via GeoCoordinate the new location.
The GeoCoordinate class has several interesting fields that provide important information as you can see in figure 2, such as altitude, latitude, and longitude (identifying the position) that will be set to NAN until the Watcher status will not become ready. Another interesting property is the speed (which would be useful if you wanted to create a navigation system).
All you need to do to resolve your problem quickly is to write this code:
...
void geoWatcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//BingMap is the Map control provided for the scope
//14d is the level of zoom, this is good enough to have
bingMap.SetView(e.Position.Location,14d);
}
It's really too simple, we
know. Microsoft loves to allow developers to create applications as
quickly as possible and has therefore integrated into its operating
system some base components to help developers focus on creating other
aspects of their applications. Of course we think it's important that
you know that the use of Bing Maps implies that you are registered on
Bing Maps service portal.
What if you don't want to use Bing Maps to show the user's position on a map? You can use Microsoft Research Maps (available at http://msrmaps.com/default.aspx). In this article, you will not see how to interact with this service.
Instead, your focus will be directed to the data, both at how to get it
from Location Services, and at how to use it to find the user's exact
spot.
After you add a reference to the service available from http://msrmaps.com/TerraService2.asmx (we will explain the configuration later), you can begin to look at the code.
The structure of the UI will look like this:
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Location Service"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="LocationStatusTextBox" Text="..."
Style="{StaticResource PhoneTextNormalStyle}"/>
</StackPanel>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" >
</Grid>
</Grid>
There's something obviously strange in this interface at first glance. The Grid ContentPanel is empty, but only because inside the code you will create a kind of mosaic. In the code-behind, you will need a series of using statements:
...
using System.Device.Location;
using System.Windows.Media.Imaging;
using System.IO;
...
You will also need a private
method that enables you to initialize the various services. In addition,
you will need a proxy (and this will be the name of the object that we
will use to interact with service but we will not see here the use of
web services) for TerraService and a GeoCoordinateWatcher:
TerraService proxy = null;
GeoCoordinateWatcher geoWatcher = null;
// Constructor
public MainPage()
{
InitializeComponent();
InizializeServices();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
private void InizializeServices()
{
proxy = new TerraService();
geoWatcher = new GeoCoordinateWatcher();
}
In the event that fires when
the page has finished loading, you will write the code to subscribe to
events that are raised at the completion of an asynchronous request.
This is because the service always works asynchronously, so you don't
stop the UI too long while you wait for a response, and then your code
will be notified that a request has been answered. This mode of
operation is not new to anyone who has already worked with Silverlight
on the web side. Once again, this makes us appreciate how easy it is for
developers to recycle their skills between technologies based on the
.NET technologies stack.
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
...
//Subscribe here webservice events
...
geoWatcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(geoWatcher_PositionChanged);
geoWatcher.StatusChanged += new
EventHandler<GeoPositionStatusChangedEventArgs>(geoWatcher_StatusChanged);
//Start the geoWatcher
geoWatcher.Start();
}
When the StatusChanged event of GeoCoordinateWatcher
fires, you will change the text of the text box destined for this use,
so you can have onscreen feedback that the location service is
effectively launched and operational:
void geoWatcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
this.LocationStatusTextBox.Text = e.Status.ToString();
}
The workflow of your application is as follows:
firing the event geoWatcher_PositionChanged will make a request by calling the method GetAreaFromPt in the proxy,
whether successful or unsuccessful, will fire proxy_GetAreaFromPtCompleted event,
which in the case of a correct answer will call the GetTile method of the proxy, with the consequent trigger of the event proxy _ GetTileCompleted.
Then we start from the first step, implementing geoWatcher_PositionChanged
void geoWatcher_PositionChanged(object sender,
GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (!e.Position.Location.IsUnknown)
{
proxy.GetAreaFromPt (
//the ActualPosition
new TerraService.LonLatPt()
{
Lat = e.Position.Location.Latitude,
Lon = e.Position.Location.Longitude
},
//the type of map
1,
//the scale of image received
TerraService.Scale.Scale2km,
(int)ContentPanel.ActualWidth,
(int)ContentPanel.ActualHeight);
}
}
The purpose of this recipe
was to show you how the Location Service, based on a universal standard
for localization, enables you to access (without too much trouble)
information about the device's position in the world.
5. Usage
From Visual Studio 2010,
press Ctrl+F5. Wait for the application to start on the phone and then
wait that the position is available then look that the position on the
map changes relating your actual position.