Skip to main content

Geo-location on WP7 - don't trust the first value returned

Rich & I have been working on app which makes heavy use of geo-location information provided by the GeoCoordinateWatcher class on the WP7 platform. As the documentation on MSDN states this class exposes the Windows Phone location services. The GeoCoordinateWatcher class implements the INotifyPropertyChanged interface and all the events generated will be fired on the UI thread allowing you to easily bind the class to the UI, but as we already knew this is not always a good idea - for a detailed reason why this can be a bad idea check out this post by Jaime Rodriguez.

What we also discovered is that you can't always trust the first position event generated by this class.

We have a requirement to get the current location when a user clicks a button, the user could do this at any time when using the application - they could click once, a dozen times or not at all. Every time they click the button we need to get an accurate single value. The accuracy of the value depend on both the actual value and the age of the value. Every value generated by the GeoCoordinateWatcher class is published by via the PositionChanged event and this is made up of 2 components - the location value and the timestamp when the location value was generated, this is exposed as the GeoPosition<T> class.

The PositionChanged event is defined as follows. I've included the StatusChanged event to show all the event signatures for the class:

public class GeoCoordinateWatcher : IDisposable, INotifyPropertyChanged, IGeoPositionWatcher<GeoCoordinate>
    public event EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>> PositionChanged;
    public event EventHandler<GeoPositionStatusChangedEventArgs> StatusChanged;


The point of interest is the type used with the event handler. The GeoPositionChangedEventArgs class has the following definition.The Position property exposing the GeoPosition<T> value, this has the time stamp value.

public class GeoPositionChangedEventArgs<T> : EventArgs
    public GeoPositionChangedEventArgs(GeoPosition<T> position);
    public GeoPosition<T> Position { get; }
public class GeoPosition<T>
    public GeoPosition();
    public GeoPosition(DateTimeOffset timestamp, T position);
    public T Location { get; set; }
    public DateTimeOffset Timestamp { get; set; }

We use the following code to return a single geo-location value when the button is clicked. It uses Rx (reactive extensions) to wrap up the call to the GeoCoordinateWatcher class. We don't just use the 'FromEventPattern' Rx method because we don't want the instance of the GeoCoordinateWatcher running riot and generating a gazillion location results and killing the UI thread!

The highlighted areas show how I am using the time offset to determine the validity of the location published by the GeoCoordinateWatcher class. In this demo instance I am saying - 'if the position timestamp is greater than 20 seconds ago then ignore the value...'. When valid the new location is published using the Rx method 'OnNext' on the returned Subject<T>. We also call the Rx 'OnCompleted' method to make the 'Finally' method execute and shut down the watcher by calling 'watcher.Stop()'.

Note: We also do the 'Start' of the GeoCoordinateWatcher  on a background thread to prevent blocking of the current thread - in this case the UI thread.

This class is the used in the page class as follows:

When run for the first time (on the emulator) I get the following in the output window of visual studio:

When this is run for subsequent times the debug statements in the output window of visual studio depend on how long the interval between clicking the button.

The following shows 2 values because the time interval between clicks is greater than 20 seconds. The first value generated by the instance of the GeoCoordinateWatcher class is ignored.

To show this is not an attribute of the currently executing application session, I've added an explicit finalise to the main page as well as a debug statement in the constructor. This shows that once the location services are initialised on the phone and the current location is requested then the last value is always stored by the device and published as the first event when the GeoCoordinateWatcher is started.

The above screen shot shows 3 distinct activations of the application after tomb-stoning the device twice.

As you can see the first run shows the last geo-location generated by the device was 4 hours prior, then we tomb-stoned the app for 30 seconds, then re-activated the app with a back press. Again because the previous geo-location was generated greater than 20 seconds ago it is ignored. Finally the last activation after tomb stoning was not greater than 20 seconds so it was not ignored.

This code has been incorporated into the WP7Contrib as the LocationService. It has method for getting the geo-location using time & distance thresholds as well. It also has provides the GeoCoordinateWatcher  Status event as an observable method.

The code is available in this change-set or will be part of the 1.4 release due at the end of the month.


Popular posts from this blog

WPF tips & tricks: Dispatcher thread performance

Not blogged for an age, and I received an email last week which provoked me back to life. It was a job spec for a WPF contract where they want help sorting out the performance of their app especially around grids and tabular data. I thought I'd shared some tips & tricks I've picked up along the way, these aren't probably going to solve any issues you might be having directly, but they might point you in the right direction when trying to find and resolve performance issues with a WPF app. First off, performance is something you shouldn't try and improve without evidence, and this means having evidence proving you've improved the performance - before & after metrics for example. Without this you're basically pissing into the wind, which can be fun from a developer point of view but bad for a project :) So, what do I mean by ' Dispatcher thread performance '? The 'dispatcher thread' or the 'UI thread' is probably the most

Showing a message box from a ViewModel in MVVM

I was doing a code review with a client last week for a WPF app using MVVM and they asked ' How can I show a message from the ViewModel? '. What follows is how I would (and have) solved the problem in the past. When I hear the words ' show a message... ' I instantly think you mean show a transient modal message box that requires the user input before continuing ' with something else ' - once the user has interacted with the message box it will disappear. The following solution only applies to this scenario. The first solution is the easiest but is very wrong from a separation perspective. It violates the ideas behind the Model-View-Controller pattern because it places View concerns inside the ViewModel - the ViewModel now knows about the type of the View and specifically it knows how to show a message box window: The second approach addresses this concern by introducing the idea of messaging\events between the ViewModel and the View. In the example below

Implementing a busy indicator using a visual overlay in MVVM

This is a technique we use at work to lock the UI whilst some long running process is happening - preventing the user clicking on stuff whilst it's retrieving or rendering data. Now we could have done this by launching a child dialog window but that feels rather out of date and clumsy, we wanted a more modern pattern similar to the way <div> overlays are done on the web. Imagine we have the following simple WPF app and when 'Click' is pressed a busy waiting overlay is shown for the duration entered into the text box. What I'm interested in here is not the actual UI element of the busy indicator but how I go about getting this to show & hide from when using MVVM. The actual UI elements are the standard Busy Indicator coming from the WPF Toolkit : The XAML behind this window is very simple, the important part is the ViewHost. As you can see the ViewHost uses a ContentPresenter element which is bound to the view model, IMainViewModel, it contains 3 child v