Tuesday, April 20, 2010

Windows Phone 7 With Location Services And Bing Maps


Recently Microsoft released the CTP toolkit for Windows Phone 7, available here , and I decided to produce a small application which uses the location services functionality of the phone, as well as the Silverlight Bing Maps user control.

The application is aimed at joggers/runners, and simply allows users to start and end a running session and view their progress on a map. At the end of their session, the relevant start and end points are displayed on the map as pins, and the total time of the workout session along with total distance travelled is also displayed.

Location Services

Location services within Windows Phone 7 provides a location based api, which allows consumers to track their current geocoordinates, as well as the status of their location based device. It is worth noting that location services is not just GPS tracking, as the service can also determine a devices location from wireless networks, assisted gps etc.

One of the challenges of utilising Location Services within the CTP version of the toolkit, is that there is no built in mechanism to emulate a devices current location, or any positional changes of the device. To get around this problem you currently have to create a mock version of the GeoCoordinateWatcher class, as it is this class that is responsible for raising events in relation to a device's status and position. My mock class, MockGeoCoordinateWatcher is based upon Tim Heur's EventListGeoLocationMock class, and I'd encourage you all to read Tim's excellent post.

Before we start receiving positional change events ,we need to create an instance of the MockGeoCoordinateWatcher class and pass it a sample of the geocoordinates that we would like to be raised as our devices position. We then wire up an event handler to the PositionChanged event, which as you can guess by its name is respsonsible for notifying us of changes of position. The _geowatcher instance also needs to be started and stopped in order to start/stop receving events.

When the PositionChanged event is raised it passes a GeoCoordinate class as an event argument, and this class consists of the time that the position changed occurred, along with a location containing the latitidude and longitude coordinates of the position. In this application these geo-coordinates are simply added to a list which is then used at a later stage by the Bing Maps routing service to determine the total distance traveled, as well as being passed to the map to draw the users current progress.
namespace TeamG.Sprinter
{
public partial class MainPage : PhoneApplicationPage
{
IGeoPositionWatcher _geoWatcher;
List> _geoPositions;
MapPolyline _joggingPolyLine;

public MainPage()
{
InitializeComponent();

SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;

_geoWatcher = new MockGeoCoordinateWatcher(MockGeoHelper.CreateMockGeoCoordinateEvents());
_geoWatcher.PositionChanged += new EventHandler>(watcher_PositionChanged);

_geoPositions = new List>();
}

void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => GeoPositionChanged(e));
}

void GeoPositionChanged(GeoPositionChangedEventArgs e)
{
_geoPositions.Add(e.Position);
_joggingPolyLine.Locations.Add(new Location(e.Position.Location.Latitude, e.Position.Location.Longitude));
}

private void StartJogging_Click(object sender, RoutedEventArgs e)
{
_geoWatcher.Start();
}

private void StopJogging_Click(object sender, RoutedEventArgs e)
{
_geoWatcher.Stop();

}
}
}

It is worth noting that on a production device that the GeoCoordinateWatcher class should be used with care, and not kept in a Started state when not required, as this would potentially be a big drain on the device's battery.





Bing Maps


Working with Bing Maps in Silverlight, and thereby Windows Phone 7 is straight forward thanks to the Bing Maps Silverlight User Control. To use the map in our application we first need to reference the relevant assemblies within the project , then reference the MapControl namespace in the page's namespace declaration.





x:Class="TeamG.Sprinter.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"


xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"


xmlns:phoneNavigation="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Navigation"


xmlns:d="http://schemas.microsoft.com/expression/blend/2008"


xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"


xmlns:m="clr-namespace:Microsoft.Maps.MapControl;assembly=Microsoft.Maps.MapControl"


mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"


FontFamily="{StaticResource PhoneFontFamilyNormal}"


FontSize="{StaticResource PhoneFontSizeNormal}"


Foreground="{StaticResource PhoneForegroundBrush}">





Next, we modify our XAML to simply create a map instance, specifiying a Bing Maps key, the required zoom level, and the mode that we wish to view the Map.






Name="JoggingMap"

CredentialsProvider="Youll jave to use your own value"


Mode="Road"


ZoomLevel="16"


Center="53.32058,-2.73706"


Grid.Row="0"/>






Before the lines can be drawn we need to create an instance of the MapPolyLine class, set it's UI properties, then pass it as a UI element to the map.




_joggingPolyLine = new MapPolyline();
_joggingPolyLine.Stroke = new SolidColorBrush(Colors.Blue);
_joggingPolyLine.StrokeThickness = 5;
_joggingPolyLine.Opacity = 0.7;
_joggingPolyLine.Locations = new LocationCollection();
JoggingMap.Children.Add(_joggingPolyLine);

Now everytime that the PositionChanged event is fired, we simply add the geo-coordinates as a new location within the MapPolyLine instance, and this has the effect of drawing a line from the previous coordinate to the new coordinate.




void GeoPositionChanged(GeoPositionChangedEventArgs<?XML:NAMESPACE PREFIX = [default] http://schemas.microsoft.com/winfx/2006/xaml/presentation NS = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" /> e)
{
_geoPositions.Add(e.Position);
_joggingPolyLine.Locations.Add(new Location(e.Position.Location.Latitude, e.Position.Location.Longitude));
}

Once the user has ended their workout session, the application then adds a push pin for both the start and end position of the workout. This is done by simply querying for the first and last geo-coordinates, out of all which have been raised, then creating a push pin for each one.




private void CreateStartEndPushpins()
{
Pushpin startPin = new Pushpin();
Pushpin endPin = new Pushpin();
startPin.Location = new Location(_geoPositions[0].Location.Latitude, _geoPositions[0].Location.Longitude);
endPin.Location = new Location(_geoPositions.Last().Location.Latitude, _geoPositions.Last().Location.Longitude);
JoggingMap.Children.Add(startPin);
JoggingMap.Children.Add(endPin);
}

In a nutshell thats the core of the application completed. The images below show the application running, and you'll notice how the user's progress is tracked as they jog around.























The next phase of the application will use the Bing Maps Route Service web service in order to determine the distance travelled.





A potential stumbling block with this kind of application could be due to its long running nature. From the documentation that Ive currently read, it is unclear whether the phone framework will attempt to pause the application thereby rendering it useless when the phone goes into a locked/inactive state. Ive raised this question to the Windows Phone 7 development team, but Ive yet to receive a response. As soon as I get a response on this then Ill post an update.





Hope this is of some use to you.

4 comments:

  1. Hi,
    That's a very nice article! Do you have any information of how accurate the location service in WP7 is? You wrote it's a hybrid of a few technologies available but I wonder how it works in reality. Can in detect simple moves, within a few meters?
    Cheers,
    Jan.

    ReplyDelete
  2. Hey, i was just wondering if maybe you could send me your project with the source code ? Because i was trying to use a sample of your code but i can't seem to make it work.

    Cheers Jonas

    ReplyDelete
  3. kk i was just wondering what is the right syntax for the List for _gePositions because i can't seem to find it.

    Cheers Jonas

    ReplyDelete