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

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

Custom AuthorizationHandler for SignalR Hubs

How to implement IAuthorizationRequirement for SignalR in Asp.Net Core v5.0 Been battling this for a couple of days, and eventually ended up raising an issue on Asp.Net Core gitHub  to find the answer. Wanting to do some custom authorization on a SignalR Hub when the client makes a connection (Hub is created) and when an endpoint (Hub method) is called:  I was assuming I could use the same Policy for both class & method attributes, but it ain't so - not because you can't, because you need the signatures to be different. Method implementation has a resource type of HubInnovationContext: I assumed class implementation would have a resource type of HubConnectionContext - client connects etc... This isn't the case, it's infact of type DefaultHttpContext . For me I don't even need that, it can be removed completely  from the inheritence signature and override implementation. Only other thing to note, and this could be a biggy, is the ordering of the statements in th