Skip to main content

WP7Contrib: Location Push Model

Anyone who's tried to get your current location of a WP7 device will know this is not as simple as it first appears, the problems really revolve around the frequency at which the location information can be generated by the device (GeoCoordinateWatcher class) and the fact it is generated on the UI thread. Jaime Rodriguez has a very insightful post on the issues, you should read this first if you're not familiar with the issues. For the WP7Contrib we wanted to abstract away the issues and simplify the interface for any developer wanting to get location information.

Following the pattern we used for Network Connectivity we use a push model using the MS Reactive Extensions for .Net. We use an observable sequence which returns the current location (latitude & longitude) in one of three ways - the current location, the location by time threshold (seconds or TimeSpan) and the location by distance threshold (metre).

The interface for the location service is shown below:

public interface ILocationService
{
    IObservable<GeoCoordinate> Location();
    IObservable<GeoCoordinate> LocationByTimeThreshold(int frequency);
    IObservable<GeoCoordinate> LocationByTimeThreshold(TimeSpan frequency);
    IObservable<GeoCoordinate> LocationByDistanceThreshold(int distance);
}

As you can see we have abstracted away the GeoCoordinateWatcher class and provide a clean push model using the IObservable as the return types for all variants.

The implementation of this interface can be found in the LocationService class, in the WP7Contrib.Services assembly.

We decided the implementation would not return a value when the location is unknown (latitude = NaN, longitude = NaN) - if a time out is required for trying to obtain a value then 'Timeout' observable extension should be used, and if the previous value is the same as the current value a value would not be returned - this only applies to the threshold based variants.

Examples of how easy it can be to get location information are shown below, as usual I've implemented this in the 'code behind' for simplicity.

The first example is how to get the current location:

private void currentLocation_Click(object sender, RoutedEventArgs e)
{
    currentSubscriber = locationService.Location()
       .ObserveOnDispatcher()
       .Subscribe(location =>
       {
          this.results.Insert(0, string.Format("'{0}', '{1}'", location.Latitude, location.Longitude));
       });
}

The second example is how to get the location using a distance threshold:

private void startDistance_Click(object sender, RoutedEventArgs e)
{
    if (locationSubscriber != null)
       return;

    var val = Convert.ToInt32(this.threshold.Text);
    locationSubscriber = locationService.LocationByDistanceThreshold(val)
       .ObserveOnDispatcher()
       .Subscribe(location =>
       {
          this.results.Insert(0, string.Format("'{0}', '{1}'", location.Latitude, location.Longitude));
       });
}

And the final example is how to get the location using a time threshold:

private void startTime_Click(object sender, RoutedEventArgs e)
{
    if (locationSubscriber != null)
       return;

    var val = (int)(Convert.ToDouble(this.threshold.Text) * 1000);
    locationSubscriber = locationService.LocationByTimeThreshold(val)
       .ObserveOnDispatcher()
       .Subscribe(location =>
       {
          this.results.Insert(0, string.Format("'{0}', '{1}'", location.Latitude, location.Longitude));
       });
}

These examples are from a quick application called 'RxLocationService' (the code can be found in WP7Contrib on CodePlex in the Spikes directory), screenshot shown below.


As you can see from these examples we've greatly simplifies using the location based information on a WP7 device. The complexity is hidden away in the LocationService class, and below I've included code snippets for each difference variation.

Getting the current location is straight forward - create an instance of a Subject<GeoCoordinate> class, this is returned via the IObservable<GeoCoordinate> interface then create an instance of GeoCoordinateWatcher and hook up an event handler for the PositionChanged event, when the event is fired we update the Subject<GeoCoordinate> with the new value and also importantly signal any observers that the observable sequence has finished by calling 'OnCompleted', this is important because it will shutdown the GeoCoordinateWatcher correctly. Next we start the watcher (which will start asynchronously under the covers) and return the Subject<GeoCoordinate> as an observable sequence where we fitler out an unknown location values.

public IObservable<GeoCoordinate> Location()
{
    var subject = new Subject<GeoCoordinate>();
    var watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default)
       { MovementThreshold = FixedLocationDistance };

    watcher.PositionChanged += (o, args) =>
       {
          subject.OnNext(args.Position.Location);
          subject.OnCompleted();
       };
    watcher.Start();

    return subject.AsObservable()
       .Where(c => !c.IsUnknown)
       .Finally(() =>
       {
          watcher.Stop();
          watcher.Dispose();
       });
}

Next is the code getting the location by a distance threshold - this is very similar to the current location, but the differences are we use a BehaviorSubject<GeoCoordinate> and importantly we don't signal the observable sequence has finished in the event handler. We also use the 'DistinctUntilChanged' extension method to filter out the results so that only distinct values are returned to any observers.

public IObservable<GeoCoordinate> LocationByDistanceThreshold(int distance)
{
    var subject = new BehaviorSubject<GeoCoordinate>(GeoCoordinate.Unknown);
    var watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default)
       { MovementThreshold = distance };
    watcher.PositionChanged += (o, args) =>
       {
          var newLocation = args.Position.Location;
          subject.OnNext(args.Position.Location);
       };
    watcher.Start();

    return subject.Where(c => !c.IsUnknown)
       .DistinctUntilChanged()
       .Finally(() =>
       {
          watcher.Stop();
          watcher.Dispose();
       })
       .AsObservable();
}

And finally the location by a time threshold - this uses the Interval method on the Observable class to trigger getting the location at the required time interval. The CurrentLocationByTime method actual returns the value from the GeoCoordinateWatcher class using the TryStart method.

private IObservable<GeoCoordinate> LocationByTimeImpl(TimeSpan timeSpan)
{
    return Observable.Interval(timeSpan)
       .ObserveOn(Scheduler.ThreadPool)
       .SubscribeOn(Scheduler.ThreadPool)
       .Select(t => this.CurrentLocationByTime(timeSpan))
       .Where(c => !c.IsUnknown)
       .DistinctUntilChanged();
}

private GeoCoordinate CurrentLocationByTime(TimeSpan timeSpan)
{
    var currentLocation = GeoCoordinate.Unknown;
    using (var watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default))
    {
       if (watcher.TryStart(true, timeSpan))
       {
          currentLocation = watcher.Position.Location;
       }

       watcher.Stop();
    }

    return currentLocation;
}

So that pretty much rounds it up, as I said the code can be found in the WP7Contrib CodePlex project in the WP7Contrib.Services project.

Comments

  1. Hi!

    Thanks for a great implementation.
    I do have one question, how can I handle the errors omitted from LocationService when the phone's location service is disabled?

    Line 502 in LocationService.cs causes an error like below:
    Can not access a disposed object.
    Object name: 'GeoCoordinateWatcher'.

    I want to be able to handle this in order to avoid crashing my app.

    Any hints appreciated!
    Torulf (torulf at programplatssen.se)

    ReplyDelete
  2. Typo above, address should read torulf at programplatsen.se

    ReplyDelete
  3. The code has been refactored to remove the GeoCoordinateWatcher.Dispose call - so you shouldn't see this occurring any more. This was released with version 1.5

    Sorry for the late reply.

    ReplyDelete

Post a Comment

Popular posts from this blog

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...

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 ...

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 ...