31 Days of Windows 8 | Day #14: Geolocation

HTMLCallout

This article is Day #14 in a series called 31 Days of Windows 8.  Each of the articles in this series will be published for both HTML5/JS and XAML/C#. You can find additional resources, downloads, and source code on our website.

advertisementsample4

Today, we get to talk about one of my favorite features of any development platform: geolocation.  Having knowledge about the user’s location (or more specifically, the device) makes every app better.  Here’s some examples of how:

Line of Business Apps

Know which plant your field manager is visiting today, so that he has all of the appropriate documentation for the machinery in that facility.  In addition, your app could surface the photos and names of the employees in that facility, so that they are more prepared and personable when they arrive.

Games

Identify other nearby players, and make an opportunity to match them up in game.  It will not only potentially improve network latency for them, but you might also form a guild out of nothing.  Create a global game of tag, based solely on proximity, not touching.

Maps

Duh.  We’ll do this as part of the sample code today.

Travel

Recognize the user’s distance and potential travel time from the airport, and alert them to the impending urgency to leave before missing their flight.  In addition, once you’ve established they can’t make it on time, help them find the next available flight without the user having to find it on their own.

Exercise

There are tons of apps available to track your exercise route.  Think running, cycling, etc.  The obvious application of geolocation to these apps is the ability to map the route you took during your travels.  A more important one, however, and I think this responsibility probably falls to the mobile OS manufacturers themselves, is the ability to recognize an accident.  On average, 4500 cyclists a year are victims of hit-and-run situations, where they are left injured (or worse) on the side of the road.  In today’s world, nearly everyone has their phone with them, even when running or cycling.  Imagine that your phone is capable of recognizing a large impact (using accelerometer or gyroscope data), followed by no movement at all.  After 30 seconds of no movement, your phone prompts you to call 911 and send text messages to a pre-selected list of contacts with an SOS message and your location.  After 30 more seconds, it immediately sends that information and dials 911.  This functionality could save lives, as many of these cyclists die needlessly only because nobody knows where they are.  Sorry for being so morbid, but it’s an important thing to think about.

As you can see there is always a way to take location data and make your app even better.  In addition, if you love my ideas above, you are completely welcome to steal them outright.  I will likely never build any of those apps, specifically.  There needs to be 48 hours in every day to make that happen.

Updating our Manifest

Thankfully, getting our geolocation data is pretty easy, and as usual, we have to declare that Location is one of our capabilities in order to use it.  To do this, open the Capabilities section of your package.appxmanifest file, and check the Location box.

14-XAML-Manifest

If you skip this step, and just try implementing the code in the rest of the article, you won’t get any errors, but you also won’t get any data.

Getting our Geolocation Data

In this example, I have constructed an application that constantly tracks the device’s location.  The first thing that got me confused in this process was how to ask the user for permission to start tracking their location in the app.  If you’ll recall, in Day #11 Lock Screen Apps, we had to explicitly prompt the user for permission to access their lock screen.  With Location, however, we don’t.  The simple act of trying to access the data will prompt the user on your behalf.  To make this incredibly easy, here’s ALL of the code you’ll need to access a device’s location from your app.  (Keep in mind that I am sending all of my values to TextBlocks on my MainPage.xaml that display the data.  They are all named like [Datatype]Value to make it obvious.)

Geolocator location;


protected override void OnNavigatedTo(NavigationEventArgs e)

{

    location = new Geolocator();

    location.PositionChanged += location_PositionChanged;

    location.StatusChanged += location_StatusChanged;

}


protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)

{

    location.PositionChanged -= location_PositionChanged;

    location.StatusChanged -= location_StatusChanged;

}


async void location_PositionChanged(Geolocator sender, PositionChangedEventArgs args)

{

    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>

    {

        Geoposition position = args.Position;


        LatitudeValue.Text = position.Coordinate.Latitude.ToString();

        LongitudeValue.Text = position.Coordinate.Longitude.ToString();

        AccuracyValue.Text = position.Coordinate.Accuracy.ToString();


        TimestampValue.Text = position.Coordinate.Timestamp.ToString();


        if (position.Coordinate.Altitude != null)

            AltitudeValue.Text = position.Coordinate.Altitude.ToString()

                                    + "(+- " + position.Coordinate.AltitudeAccuracy.ToString() + ")";

        if (position.Coordinate.Heading != null)

            HeadingValue.Text = position.Coordinate.Heading.ToString();

        if (position.Coordinate.Speed != null)

            SpeedValue.Text = position.Coordinate.Speed.ToString();

    });

}


async void location_StatusChanged(Geolocator sender, StatusChangedEventArgs args)

{

    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>

    {

        StatusValue.Text = args.Status.ToString();

    });

}

As you might have guessed, this is an event-driven operation.  When my page loads, I create a new Geolocator object, and add two events: PositionChanged and StatusChanged.

PositionChanged is the important one.  This is where we get all of the useful data like Longitude, Latitude, and Accuracy.  All of these values come from the Geocoordinate class, and as you can see from the code above, there’s several more.

  • Accuracy is the measurement, in meters, of the radius of a circle that has our latitude and longitude value as the centerpoint.  Like this:

14-XAML-Accuracy

  • Timestamp is the actual time that the Location value was read.  This includes timezone information, so you’ll have to convert the times to store everything in UTC.
  • Altitude is the elevation of the device, in meters.  This is likely only to be found in devices with a barometer (we will cover sensors later in this series), but it’s a valuable piece of information if you can get it.
  • AltitudeAccuracy is just like our other Accuracy value, except that it is simple the margin of error for the Altitude value.  It means you are + or – this many meters.
  • Heading will also only be displayed with the appropriate sensor, the compass.  This is measures in degrees relative to true north.
  • Speed can technically be calculated by tracking the lat/long values over time, but they’ve made it incredibly easy for us.  It is measured in meters/second, but is an optional value that the device’s GPS sensor can provide.  This means that not all devices will return a value.

The other code, from the sample above, that I’d like to talk about is the async/await stuff.  You should have noticed that both of our event handler methods have async at the beginning, and then we have something like this:

await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {};

Because the acquisition of our Location data happens on a background thread, we need to use a Dispatcher to get us back to the primary UI thread in order to write our values to the screen.  Without this code, we will get an error the moment we try to manipulate the UI layer, because background threads do not have access to this directly.  Here’s what my basic UI looks like (no, that is not where I live, but you’d know that if you check out that location):

14-XAML-UI

So we’ve gathered our location data, but there’s still that “StatusChanged” event handler we created earlier.  This is not something we would generally give our user direct access to, as it’s more administrative data for us, but it’s an interesting set of data nonetheless.  It can have six different values:

  • notInitialized is returned when your application has not yet requested permission to access the location data.
  • disabled is the value you will get when the user declines your request to access their location information.
  • notAvailable is returned if the user’s device does not support location data.
  • noData is returned if the user’s device supports location, but can’t acquire any.  This could happen in a scenario where the device is not connected to wi-fi, and is inside a buiding, for example.
  • initializing is the status value you’ll receive between the time the user grants access to location data, and the time you actually start receiving that data.  In future uses of your app, this will also be sent when your app is waiting to access location data for the first time.
  • ready is the golden response, and lets you know that data is available for your use.

So the example I’ve provided you in this article allows you to recognize every time your user’s location data changes, and one that has millions of use cases for.  If you only need this data ONCE, say to tag an image that the user has just taken, you just make a request to the Geolocator object without having to create all of the events I’ve outlined above.  In the method below, we can make one simple call to the GetGeopositionAsync() method instead.

async private void GetLocationDataOnce()

{

    Geoposition position = await location.GetGeopositionAsync().AsTask();


    LatitudeValue.Text = position.Coordinate.Latitude.ToString();

    LongitudeValue.Text = position.Coordinate.Longitude.ToString();

    AccuracyValue.Text = position.Coordinate.Accuracy.ToString();

}


 

In this case, we just need one simple value, one time.  Do not create a loop that makes this call over and over, it’s much slower, and you’ll save tons of machine resources by following the earlier example that registered event handlers.

Using the Simulator to Fake It

If you’re using a traditional desktop PC, you have probably noticed that your machine doesn’t do a great job of determining your location.  Network location can be decent, but it’s unlikely your lat/long coordinates are very accurate.  Enter the simulator.  You can set a very specific location in the simulator without actually having to get on an airplane.  To do this, just open the Location Settings in the simulator:

14-XAML-SimulatorOption

At this point, you can provide latitude, longitude, altitude, and accuracy values:

14-XAML-SimulatorSettings

So there you have it!  You can now write an application that can determine where the user’s device is, and while you’re building it, you can pretend to be anywhere on Earth.  Not a bad way to travel…at least the costs are low. 🙂

Summary

Today, we talked about Geolocation data, and how important it can be to any application in the market.  We looked at tracking a user’s location, as well as getting their location once, for applications that just need the specific location “right now,” but not all the time.

If you would like to download a sample application that includes all of the code from this article, click the icon below:

downloadXAML

Tomorrow, we are going to look at the on-screen keyboard, InputScopes, and TextBoxes.  See you then!

downloadTheTools

6 thoughts on “31 Days of Windows 8 | Day #14: Geolocation

  1. I get both PositionChanged and StatusChanged events to fire, but when I turn off the apps permission to use Location, and then turn it on again – the StatusChanged event wont fire. I also downloaded the sample in this article, and it does the same thing. I need to restart the app to get the event to fire again.

    Is this by design, or am I missing something? Have been at this for a couple of hours now, and I cant figure it out…

  2. I see a similar issue as Hans reported, the StatusChanged event does not fire in a timely manner. I can be receiving location/GPS data with high accuracy (e.g. 6 meters) for quite a while (e.g. 1 or 2 minutes) before I ever receive a geolocator_StatusChanged event indicating PositionStatus is Ready.
    Also, even when I set the Geolocator’s ReportInterval = 2000; (2seconds) the Geolocator’s PositionChanged event does not fire anywhere near a frequently as I ask for.
    Any insights would be appreciated?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s