Partager via


GPS Intermediate Driver – Event Driven Usage

In one of my previous blog entries I wrote about how to use the GPS Intermediate Driver (GPSID) to easily retrieve location information in an unified way. So this time I am not going to tell you where you will find a managed wrapper around GPSID, nor am I going to tell you what the advantages are by making use of the GPS Intermediate Driver. If you want to read more about GPSID and the managed wrapper around it that ships with the Windows Mobile 5.0 and Windows Mobile 6 SDK’s, you can always visit my previous blog entry about this topic. In that blog entry I showed you how you can call the GetPosition method to retrieve location information from the GPS radio.

In this blog entry I will show you how you can retrieve location information in an event driven way. The managed wrapper around GPSID defines a LocationChanged event that will be fired, each time GPS location information changes. Any application can subscribe to this event by providing an event handler. Each time the GPS location information changes, the event handler of the application will be called to allow the application to act on location changes. Hooking up to the LocationChanged event makes your application a bit more complex, but at least you always are assured that you have the latest location information available inside your own application.

So let me just give you a guided tour around how to create an application that will make use of the LocationChanged event by showing you a sample application as well as the relevant code that I created for this particular application. The application creates a little electronic compass, showing you the direction in which you are traveling, as well as latitude and longitude information and the current speed with which I am travelling. 

In order to continuously update my compass and of course also the latitude, longitude, speed and altitude information, I decided to make use of the LocationChanged event that is exposed through the managed wrapper around GPSID.

In the constructor of my MainForm I am simply creating a new instance of type GPS as the following code snippet shows:

public MainForm()

{

    InitializeComponent();

    gps = new Gps();

}

 

Each time the user clicks the left hand soft key on the Windows Mobile device I am either going to enable or disable updating location information for my compass application, as is shown in the following code snippet:

private void menuGPS_Click(object sender, EventArgs e)

{

    if (!gps.Opened)

    {

        gps.LocationChanged += new

                LocationChangedEventHandler(gps_LocationChanged);

        gps.Open();

    }

    else

    {

        gps.LocationChanged -= gps_LocationChanged;

        gps.Close();

        lblLatitude.Text = "";

        lblLongitude.Text = "";

        lblSpeed.Text = "";

        lblAltitude.Text = "";

    }

    menuGPS.Text = gps.Opened ? "Disable GPS" : "Enable GPS";

}

 

Besides updating my user interface in the above shown code snippet, there are two very important lines of code. The statement on line 5 of the code snippet adds my event handler to the LocationChanged event of my Gps object. The way to do so is creating a new instance of type LocationChangedEventHandler and passing it the name of a method. This method must accept the correct number and right types of parameters, which I will explain to you in a few seconds. After I have added my event handler to the LocationChanged event, each time the GPS location changes, my event handler will be called from within GPSID.

Now if I am no longer interested in receiving GPS location information, I need to unsubscribe from the LocationChanged event. This is what I am also doing in the above code snippet, on line 11. It is also important to call the Close method on my Gps object afterwards, because the GPS radio will be disabled after calling this method if my application was the only one making use of the GPS hardware. In this way I simply save battery power.

So let’s take a look now at the actual event hander that is called each time the GPS location changes.

void gps_LocationChanged(object sender, LocationChangedEventArgs args)

{

    GpsDataUpdater ctrlUpdater = UpdateGPSData;

    CompassUpdater compassUpdater = UpdateCompass;

    BeginInvoke(ctrlUpdater, args.Position);

    if (args.Position.HeadingValid)

    {

        BeginInvoke(compassUpdater, args.Position.Heading);

    }

}

 

My event handler receives location information through the LocationChangedEventArgs that contains an object of type GpsPosition. This object contains all relevant location information, not only longitude and latitude data, but a whole range of other information as well. In my sample application I will only display lat/lon information, the speed with which I am traveling, the altitude and the direction in which I am traveling.

You probably noticed in the above code snippet that I am not updating user interface controls directly inside this event hander. Instead I am calling Control.BeginInvoke passing it a relevant delegate. The reason for this is that GPSID sends us location information on another thread, meaning that my event handler will execute on another thread than the one that created my user interface controls. Since it is only allowed to update UI controls on the thread that created the controls, I need to make use of either the synchronous or the asynchronous Invoke methods that will update UI controls on the thread that created them for the thread that wants to show those updates. If you want to know more about the way to update UI controls in a multithreaded application, you can take a look at this How-Do-I video.

Finally let me show you how I am actually updating my UI controls inside this application.

private delegate void GpsDataUpdater(GpsPosition p);

private void UpdateGPSData(GpsPosition p)

{

    if (p.LatitudeValid)

        lblLatitude.Text = p.Latitude.ToString();

    if (p.LongitudeValid)

        lblLongitude.Text = p.Longitude.ToString();

    if (p.SpeedValid)

        lblSpeed.Text = p.Speed.ToString() + " MPH";

    if (p.SeaLevelAltitudeValid)

        lblAltitude.Text = p.SeaLevelAltitude.ToString() + " ft";

}

 

The first statement in this code snippet declares a delegate, the one that we used inside the LocationChangedEventHandler. The method UpdateGPSData has the same signature as the GpsDataUpdater delegate, expecting a parameter of type GpsPosition. The GpsPosition object contains all relevant GPS data, including a number of properties that indicate if particular items are valid for this instance of type GpsPosition. As you can see in UpdateGPSData, I am simply setting the Text properties of my UI Controls to a relevant member of GpsPosition, as long as that particular data member contains valid information.

So know you know about two different ways to use location information inside your own application by making use of GPSID. There is still a question to be answered though. How can we test applications that are location aware? That will be the topic of an upcoming blog entry.

Happy coding!

Constanze

Comments

  • Anonymous
    September 26, 2008
    cool - thanks for the quick, simple introduction.  How do I create and use the compass control on WM5/6 ?

  • Anonymous
    October 03, 2008
    I did something similar to this by wrapping the System.IO.Ports.SerialPort class. The problem I found was that since my GPS receiver gave me an updated position every second, the overhead of invoking a method on the main thread in order to update the UI actually caused the UI to become unresponsive despite the fact that the serial port IO was being done on a background thread. I worked around this by specifying an update interval in my GPS class which I normally set to around 5 seconds.  This means I only raise my LocationChanged event when it's been 5 seconds since I last did so. I'm not totally happy with this solution so if anyone has any alternatives I'd be happy to hear them. Colin

  • Anonymous
    October 05, 2008
    I've done something similar to this by creating my own class that wrappers the .NET SerialPort class. I found a small problem though - my GPS device sends a location update every second therefore my LocationChanged event is also raised every second. When attempting to update the GUI it seems that there's a big overhead when using BeginInvoke (to make sure the GUI is updated on the correct thread), so much so that the GUI becomes unresponsive. I've worked around this by only raising the LocationChanged event every 5 seconds but I'd be interested to know if anyone has a better solution.