Acquiring a single Geoposition in Windows Phone 8

Acquiring a single Geoposition in Windows Phone 8

  • Comments 4
  • Likes

This blog post was authored by Daniel Estrada Alva, a software development engineer on the Windows Phone team.

- Adam


With the new set of Geolocation APIs in Windows Phone 8, your app can request a single position at any time, which means more flexibility as you develop your app. This post describes the new API for single position acquisition, with examples to show you how you can use the APIs to declare your needs to the Geolocator object.

How to use single position acquisition

The following code example demonstrates how to use the new Geolocation APIs:

Code Snippet
  1. public async Task<Windows.Devices.Geolocation.Geocoordinate> GetSinglePositionAsync()
  2. {
  3.     Windows.Devices.Geolocation.Geolocator geolocator = new Windows.Devices.Geolocation.Geolocator();
  4.  
  5.     Windows.Devices.Geolocation.Geoposition geoposition = await geolocator.GetGeopositionAsync();
  6.  
  7.     return geoposition.Coordinate;
  8. }

 

When the GetSinglePositionAsync method is called, it creates a new Geolocator object. Then, the code uses the Geolocator object to determine the geoposition. The geoposition is determined in the background, using the Windows Runtime (WinRT) asynchronous pattern. When the geoposition has been determined, the method returns the coordinates as the latitude and longitude associated with the geographic location.

This code works for most cases, in which the app usually only needs to know the current location. However, sometimes an app has certain requirements—such as accuracy—for the position it needs. With the new Geolocation APIs you can set those requirements using the following properties:

Parameter

Brief description

Desired Accuracy

A best-effort approach satisfies the accuracy requirement of the app. This can be expressed either through an enumeration or through a scalar value (in meters).

Timeout

The maximum time the Geolocator object has to obtain a position that satisfies the accuracy requirement. When the timeout is exhausted, the Geolocator object returns the best position it could obtain (even if this doesn’t satisfy the desired accuracy value), or an error if it wasn’t able to acquire any location.

Maximum Age

You can use this new parameter to get a cached position that was obtained by the Geolocator object in the past, and to define a limit of how old that position can be for the app to use it. The cached position will only be returned if the age and accuracy requirements are met.

The following sections expand on each of the properties, how an app can use them, and the set of combinations that will result in error conditions.

Defining the desired accuracy in a request

By default, any request will use an accuracy defined by Windows.Devices.Geolocation.PositionAccuracy.Default, which when possible avoids using the most power-intensive technologies to acquire the Geoposition. This approach works well for most apps, which have the simple requirements of knowing the approximate location of the phone. In addition, it often improves the overall user experience because the app may get the position faster with lower power consumption. However, in some circumstances an app needs a higher level of accuracy. In those cases you can set the desired accuracy in the following way:

Code Snippet
  1. public async Task<Windows.Devices.Geolocation.Geocoordinate> GetSinglePositionAsync()
  2. {
  3.     Windows.Devices.Geolocation.Geolocator geolocator = new Windows.Devices.Geolocation.Geolocator();
  4.     geolocator.DesiredAccuracy = Windows.Devices.Geolocation.PositionAccuracy.High;
  5.  
  6.     Windows.Devices.Geolocation.Geoposition geoposition = await geolocator.GetGeopositionAsync();
  7.  
  8.     return geoposition.Coordinate;
  9. }

 

This approach uses the Windows.Deviced.Geolocation.PositionAccuracy enumeration in Windows Phone 8, which resembles the System.Device.Location.GeoPositionAccuracy enumeration in Windows Phone 7.

It’s also possible to define the accuracy requirements using the DesiredAccuracyInMeters property, through which you can set the target accuracy in meters, in the following way:

Code Snippet
  1. public async Task<Windows.Devices.Geolocation.Geocoordinate> GetSinglePositionAsync()
  2. {
  3.     Windows.Devices.Geolocation.Geolocator geolocator = new Windows.Devices.Geolocation.Geolocator();
  4.     geolocator.DesiredAccuracyInMeters = 100;
  5.  
  6.     Windows.Devices.Geolocation.Geoposition geoposition = await geolocator.GetGeopositionAsync();
  7.  
  8.     return geoposition.Coordinate;
  9. }

 

Note that the DesiredAccuracyInMeters property is a nullable unsigned integer. This way you can switch from one representation of accuracy to another. When DesiredAccuracyInMeters is set (not null), it takes precedence over DesiredAccuracy. When DesiredAccuracyInMeters is not set (is null), DesiredAccuracy takes precedence. In addition, explicitly setting the DesiredAccuracy property has the side effect of setting DesiredAccuracyInMeters to null.

Using Timeout and MaximumAge in a request

Before going through an example of how you can set these parameters, I want to elaborate on the concept of MaximumAge and how it differs from Timeout.

Timeout is the period of time the request has to complete from the point the operation is started. By default, the request has no timeout.

MaximumAge defines a limit of how old a cached Geoposition can be, from the time the operation starts, for it to be used by the app. By default, the request will not return a cached Geoposition. If your app can use cached positions for a brief period of time, this parameter also can be seen as the validity time span. The app can simplify its logic by letting the Geolocator handle the caching.

For example, if the app sets MaximumAge to 5 minutes, the Geolocator can satisfy that request with a Geoposition that is as old as 5 minutes from the time the request is started, as long as that position meets the accuracy requirement specified by the app.

Note that the Geolocator will always take the following sequential steps to satisfy a request:

  1. The request is started.
  2. Identify whether a cached Geoposition satisfies the age and desired accuracy, and if so, immediately return it.
  3. Trigger positioning technologies based on the desired accuracy.
  4. Return a Geoposition when it meets the desired accuracy.
  5. When the timeout is reached, if no position is available yet, error out. If there is a position available, given that the framework follows a best-effort model, this Geoposition with a lower accuracy than desired by the app can be returned at this time. The app can decide whether the information is useful or to discard it.

The following timeline shows the sequence of operations. The first Geoposition from the left, which is already cached with the Geolocator, is too old for the age requirement and therefore is invalid in the context of the given request. The second Geoposition from the left could satisfy the request given the age requirement, but does not satisfy the desired accuracy so a more accurate position is detected. This final Geoposition satisfies the accuracy requirement and is returned to the app.

clip_image002

The following code snippet shows you how to pass these parameters to the Geolocator for a given request. For accuracy, the code builds on top of the preceding example. Also, please note that using named parameters is not necessary; I’ve used them in this example to make the code more readable.

Code Snippet
  1. public async Task<Windows.Devices.Geolocation.Geocoordinate> GetSinglePositionAsync()
  2. {
  3.     Windows.Devices.Geolocation.Geolocator geolocator = new Windows.Devices.Geolocation.Geolocator();
  4.     geolocator.DesiredAccuracyInMeters = 100;
  5.  
  6.     Windows.Devices.Geolocation.Geoposition geoposition = await geolocator.GetGeopositionAsync(
  7.         maximumAge: TimeSpan.FromMinutes(1),
  8.         timeout: TimeSpan.FromSeconds(30)
  9.         );
  10.  
  11.     return geoposition.Coordinate;
  12. }

 

Please note that setting desired accuracy is not required to use MaximumAge and Timeout.

The relationship between Accuracy, Timeout, and MaximumAge

When you build an app that specifies a desired accuracy, consider the following:

· In general, acquiring a Geoposition with a higher accuracy takes longer. With that in mind, if you are specifying a high desired accuracy, consider increasing the Timeout associated with the request.

· If a quick response time is important, consider specifying a lower desired accuracy and increase the maximum age.

If your app desires high-accuracy positions with a short timeout, consider using error handling that falls back to request a value with lower accuracy. You should also consider giving more time to the first request, and then reducing the timeout for subsequent requests.

MaximumAge and Accuracy also are related because the original accuracy of any given Geoposition fades as time passes. The Geolocator takes this into consideration when evaluating known information, and only considers the accuracy of information to be valid for a given time span.

You should design your app to check the accuracy and time stamp of positions returned by the framework, and then decide whether to use or to discard the data.

4 Comments
You must be logged in to comment. Sign in or Join Now
  • bpoker
    0 Posts

    Thanks for this post, it was extremely helpful.  However, I was wondering if you can help with an issue I can't seem to get past and haven't seen anyone else really come across either.  I am building a windows store application and not a windows phone application so it's not exactly the same, but should be the same concept.  When ever I try to get my coordinates I always get the EXACT same long and lat.  I originally thought it had to do with the MaximumAge property, but even when setting it to 1ms it doesn't change.

    A little bit of background:

    1) I am using a MAC running iOS with parallels and developing on a Windows8 image (I suspect this is the issue)

    2) I originally built the application at my home office and the coordinates that came up were about 1 mi. west of where I was, so I assumed it was an accuracy issue.  I then continued working on it at my office which is about 10 mi. away and that's when I noticed I was getting the EXACT same long and lat as before.  At that point I suspected it was an issue with caching the location somehow, so I even went ahead and created a brand new application and was getting the same location info there too.

    Any ideas or suggestions would be greatly appreciated.

    Thanks!

  • Hi all,

    @bryantlikes: I agree with "I've noticed that sometimes the call to GetGeopositionAsync never comes back even though I specify a timeout". I guess this is a bug, as other developpers seems to have the same problem (for example, have a look to lordzoltan.blogspot.fr/.../msdn-and-ms-fail-developing-store-apps.html)

    Here is the workaround I found (working on both simulator and HTC 8X : do not use GetGeopositionAsync(), but use tracking instead... If you need a sample, see: blogs.windows.com/.../geoposition-advanced-tracking-scenarios-for-windows-phone-8.aspx

    Please note that after successful tracking (i.e. at least one PositionChangedEvent received, GetGeopositionAsync() will work as wanted... timeout included)!!

  • Great post, the beauty of Microsoft Windows-8, especially hybrid devices is that you have a laptop and a tablet in one product and it is really good and stable; it is really very helpful post.

    John Irron - www.outsourcingprogrammingservices.com

  • Thanks for the post on this. I've been using the code above but I have a few questions on it.

    1) Is there a requirement to call GetGeopositionAsync on the UI thread the first time it is called? I've seen this documented on all the Windows 8 documentation, but I haven't seen this specifically for Windows Phone 8.

    2) I've noticed that sometimes the call to GetGeopositionAsync never comes back even though I specify a timeout. Is there some way to find out why it isn't coming back? I would expect it to either (a) come back within the timeout period or (b) throw an exception. However, neither seems to happen.

    Thanks for any info! I'm just trying to make sure my code for this always works and then I'm submitting my app.