How to: Use Reactive Extensions to Emulate and Filter Location Data for Windows Phone

This is pre-release documentation for the Windows Phone OS 7.1 development platform. To provide feedback on this documentation, click here. For the Windows Phone OS 7.0 developer documentation, click here.

August 24, 2011

This topic shows how you can use Reactive Extensions for .NET Framework in conjunction with the Microsoft Location Service to filter location data and to emulate location data for development without a physical device. For an introduction to creating Windows Phone applications that use the Location Service, see How to: Get Data from the Location Service for Windows Phone. For an overview of Reactive Extensions, see Reactive Extensions for .NET Framework Overview for Windows Phone. A sample solution using this technique can be downloaded from Code Samples for Windows Phone.

Reactive Extensions is a managed library that provides a simple and elegant interface for dealing with sequences of data. This data sequence can take many forms, such as a stream of data from a file or web service, or a series of events such as user input. Reactive Extensions allows your application to subscribe to a data stream so that it can respond to and be driven by the data. When you subscribe to a data stream, you can also specify one or more operations to manipulate and filter the data stream before it is passed to your application.

The Location Service uses a delegate-based event model for receiving data. This model is familiar to .NET developers and makes it quick and easy to develop event-based applications. One drawback of this model is that your event handler is always called for every event and the events arrive exactly as they were sent out by the Location Service. You will not be able to specify a subset of the events that you are interested in or transform the data before your handler is called. All of this work must be done in your handler and it can get very complicated quickly. By using Reactive Extensions, you can perform specify filters and transformations on the data when you subscribe to events and then your handler only needs to react when the data arrives.

The following section walks you through the process of creating a location-aware application using Reactive Extensions. The application will have the ability to switch between live and emulated location data. If you want to develop an application that uses the Location Service, but don’t have access to a Windows Phone, these instructions will allow you to create your application and then easily switch to live data once you have a device.

To Create the Location Service Emulator Application

  1. Open a new or existing Windows Phone solution in Visual Studio.

  2. From the Project menu in Visual Studio, select Add Reference…. On the .NET tab, select the component name “System.Device”. Repeat this step again for the Reactive Extensions assemblies, “Microsoft.Phone.Reactive” and “System.Observable”.

  3. Add a using directive for the namespaces containing the Location Service API and Reactive Extensions. You also need to add a using directive for the System.Threading namespace because the emulated data will be generated on a separate thread.

    using System.Device.Location;
    using Microsoft.Phone.Reactive;
    using System.Threading;
    
  4. Add a variable of type GeoCoordinateWatcher inside the MainPage class definition. Also, add a Boolean variable useEmulation that will be used to switch between live and emulated location data. You could switch between the two data sources at runtime, but for simplicity, this application will require you to choose the source before compiling.

    public partial class MainPage : PhoneApplicationPage
    {
            GeoCoordinateWatcher watcher;
            bool useEmulation = false;
    
  5. In this example, the application uses a button to start the acquisition of either live or emulated data. The following code shows the click handler for this button. If live data is being used, the application will create an Observable sequence from the Location Service event stream by using the FromEvent<TEventArgs>(Action<EventHandler<TEventArgs>>, Action<EventHandler<TEventArgs>>) method of the Observable class. Once the application has an Observable sequence, it can subscribe to the sequence and receive updates as new data arrives. The behavior of the following code is basically the same as using the PositionChanged event directly, but using Rx in this case allows the application to filter the data stream. This technique will be shown later in this topic.

    If the application has been compiled to use emulation, a new thread on which the data emulation will run is started.

    private void startButton_Click(object sender, RoutedEventArgs e)
    {
        // First, handle the case where emulation is not being used.
        if (!useEmulation)
        {
           // Initialize the GeoCoordinateWatcher.
           watcher = new GeoCoordinateWatcher();
    
           // Reactive Extensions uses Observable sequences to represent data streams.
           // Create an Observable sequence from the event stream using the FromEvent method.
           // This method uses the .NET Generic syntax to specify the type of event args for the event.
           // The parameters to the method are the add and remove handlers of the GeoCoordinateWatcher object.
           IObservable<IEvent<GeoPositionChangedEventArgs<GeoCoordinate>>> positionEventAsObservable = 
               Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(
               ev => watcher.PositionChanged += ev,
               ev => watcher.PositionChanged -= ev);
    
           // Subscribe to the observable data stream. You can use the same event handler as if you 
           // were using the GeoCoordinateWatcher.PositionChanged event directly.
           var positionSubscription = positionEventAsObservable.Subscribe(
               args => watcher_PositionChanged(args.Sender, args.EventArgs));
    
           // Start the GeoCoordinateWatcher to begin receiving data from the Location Service.
           watcher.Start();
        }
        else
        {
           // Start the thread on which emulated location data is generated.
           // The method StartEmulation is defined next.
           Thread emulationThread = new Thread(StartEmulation);
           emulationThread.Start();
        }
    }
    
  6. The StartEmulation method in this application creates an Observable sequence from the EmulationPositionChangedEvents method, which will be defined in the next section. That method returns an IEnumerable object that can be converted to an Observable sequence using the ToObservable<TSource>(IEnumerable<TSource>) method. Once an Observable object is obtained, the application subscribes to the sequence, exactly the way it did with the live data.

    private void StartEmulation()
    {
        // EmulatePositionChangedEvents returns an IEnumerable object.
        // Convert this to an Observable sequence.
        var position = EmulatePositionChangedEvents().ToObservable();
        
        // Subscribe to the Observable sequence.
        // Use null for the sender parameter to the event handler.
        position.Subscribe(evt => watcher_PositionChanged(null, evt));
    }
    
    
  7. Now, the EmulatePositionChangedEvents method should be implemented. This method returns an IEnumerable object containing GeoPositionChangedEventArgs. The application will convert this object to an Observable sequence. In this sample, random values are used for the position data. This is an easy solution, but does not provide a good simulation of actual location data. You can change this to dynamically generate more realistic data, read location data from a file, or generate data from user input.

    static IEnumerable<GeoPositionChangedEventArgs<GeoCoordinate>> EmulatePositionChangedEvents()
    {
        // Create a Random object to create random numbers.
        Random random = new Random();
    
        // Loop infinitely.
        for (; ; )
        {
            // Pause for 100 milliseconds in each loop.
            Thread.Sleep(random.Next(100));
    
            // Generate a random latitude and longitude. You could also load position data from a file or
            // generate the data from user input.
            double latitude = (random.NextDouble() * 180.0) - 90.0; // latitude is between -90 and 90
            double longitude = (random.NextDouble() * 360.0) - 180.0; // longitude is between -180 and 180
    
            // Use yield to return a new instance of the GeoPositionChangedEventArgs class that is exposed
            // through the IEnumerable interface.
            yield return new GeoPositionChangedEventArgs<GeoCoordinate>(
              new GeoPosition<GeoCoordinate>(DateTimeOffset.Now, new GeoCoordinate(latitude, longitude)));
    
        }
    }
    
  8. The final step is to implement the event handler for the location data. Because the event is called from a separate thread, use the Dispatcher to invoke code on the page’s main thread. In this example, a TextBlock displays the location data.

    // Event handler for location data. This invokes code on the
    // page's main thread.
    private void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
    {
       Dispatcher.BeginInvoke(() =>
       {
          textBlock1.Text = e.Position.Location.Latitude.ToString("0.00") + " " +
              e.Position.Location.Longitude.ToString("0.00");
       });
    }
    
    

Now, the application is complete. The application described here provides a platform for developing Location Service applications using the Windows Phone emulator that is included in the Windows Phone SDK. By toggling the value of the useEmulation variable, the application can seamlessly switch between live and emulated data.

The application discussed above is very simple in order to make it smaller and easier to read. Some improvements that would be valuable include robust error handling and more compelling emulated Location data. You could also implement emulation for the GeoPositionStatusChangedEventArgs event using the same technique to enable your application to respond to changes in the Location Service’s status, even when you are running your application on the emulator.

Adding Data Filtering to the Application

In the sample application above, we converted the event stream from the PositionChanged event to an Observable sequence and subscribed to this sequence. This is a few more lines of code than simply adding an event handler delegate for the event. However, it provides the ability for the application to filter the stream of events in a way that is simple and elegant. Here is the code from the sample application that subscribes to event stream. In this case, all PositionChanged events are passed to the application.

IObservable<IEvent<GeoPositionChangedEventArgs<GeoCoordinate>>> positionEventAsObservable = 
    Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(
    ev => watcher.PositionChanged += ev,
    ev => watcher.PositionChanged -= ev); 

var positionSubscription = positionEventAsObservable.Subscribe(
           args => watcher_PositionChanged(args.Sender, args.EventArgs));


Now, by adding a single line of code, we can sample the event stream so that, regardless of how many events emanate from the Location Service, the application is only passed one event per second.

IObservable<IEvent<GeoPositionChangedEventArgs<GeoCoordinate>>> positionEventAsObservable = 
    Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(
    ev => watcher.PositionChanged += ev,
    ev => watcher.PositionChanged -= ev);

//SAMPLE
// Take the Observable sequence of all PositionChanged events and use the Sample
// method to return only one event per second.
var sampledPositionEvents = positionEventAsObservable.Sample<IEvent<GeoPositionChangedEventArgs<GeoCoordinate>>>(TimeSpan.FromMilliseconds(1000));
                
// Subscribe to the sampled Observable instead of the original Observable.
var sampledPositionSubscription = sampledPositionEvents.Subscribe(
args => watcher_PositionChanged(args.Sender, args.EventArgs));

Because operations like this take an Observable sequence and modify it into another Observable sequence, you can chain them together and subscribe to any or all of the sequences. In the following example, the sampled data from the previous example is modified further by using the where operation.

IObservable<IEvent<GeoPositionChangedEventArgs<GeoCoordinate>>> positionEventAsObservable = 
    Observable.FromEvent<GeoPositionChangedEventArgs<GeoCoordinate>>(
    ev => watcher.PositionChanged += ev,
    ev => watcher.PositionChanged -= ev);


// Take the Observable sequence of all PositionChanged events and use the Sample
// method to return only one event per second.
var sampledPositionEvents = positionEventAsObservable.Sample<IEvent<GeoPositionChangedEventArgs<GeoCoordinate>>>(TimeSpan.FromMilliseconds(1000));
                
//RESTRICTION
// Take the sampled Observable sequence and use the restriction operators 
// to only return events where the latitude is positive.
var restrictedPositionEvents = from ev in sampledPositionEvents
    where ev.EventArgs.Position.Location.Latitude > 0
    select ev;

// Subscribe to the restricted AND sampled observable.
var restrictedPositionSubscription = restrictedPositionEvents.Subscribe(
    args => watcher_PositionChanged(args.Sender, args.EventArgs));

All of these operations are set up when you subscribe to the event stream, so your event handler does not need to be tasked with filtering or modifying the data, it can simply react to any data that arrives. Also, these operations can be performed on any Observable sequence, so you could add these same filters to the emulated data stream in the same way.

Show: