With
the user’s permission, a Windows Phone 7 program can obtain the
geographic location of the phone using a technique called Assisted-GPS
or A-GPS.
The most accurate method of determining location is accessing signals from Global Positioning System (GPS) satellites. However, GPS
can be slow. It doesn’t work well in cities or indoors, and it’s
considered expensive in terms of battery use. To work more cheaply and
quickly, an A-GPS system can attempt to determine location from
cell-phone towers or the network. These methods are faster and more
reliable, but less accurate.
The core class involved in location detection is GeoCoordinateWatcher. You’ll need a reference to the System.Device assembly and a using direction for the System.Device.Location namespace. The WMAppManifest.xml file requires the tag:
<Capability Name="ID_CAP_LOCATION" />
This is included by default.
The GeoCoordinateWatcher constructor optionally takes a member of the GeoPositionAccuracy enumeration:
After creating a GeoCoordinateWatcher object, you’ll want to install a handler for the PositionChanged event and call Start. The PositionChanged event delivers a GeoCoordinate object that has eight properties:
Latitude, a double between –90 and 90 degrees
Longitude, a double between –180 and 180 degrees
Altitude of type double
HorizontalAccuracy and VerticalAccuracy of type double
Course, a double between 0 and 360 degrees
Speed of type double
IsUnknown, a Boolean that is true if the Latitude or Longitude is not a number
If the application does not have permission to get the location, then Latitude and Longitude will be Double.NaN, and IsUnknown will be true.
In addition, GeoCoordinate has a GetDistanceTo method that calculates the distance between two GeoCoordinate objects.
I’m going to focus on the first two properties, which together are referred to as geographiccoordinates
to indicate a point on the surface of the Earth. Latitude is the
angular distance from the equator. In common usage, latitude is an angle
between 0 and 90 degrees and followed with either N or S meaning north
or south. For example, the latitude of New York City is approximately
40°N. In the GeoCoordinate
object, latitudes north of the equator are positive values and south of
the equator are negative values, so that 90° is the North Pole and –90°
is the South Pole.
All locations with the same latitude define a line of latitude.
Along a particular line of latitude, longitude is the angular distance
from the Prime Meridian, which passes through the Royal Observatory at
Greenwich England. In common use, longitudes are either east or west.
New York City is 74°W because it’s west of the Prime Meridian. In a GeoCoordinate
object, positive longitude values denote east and negative values are
west. Longitude values of 180 and –180 meet up at the International Date
Line.
Although the System.Device.Location namespace includes classes that use the geographic
coordinates to determine civic address (streets and cities), these are
not implemented in the initial release of Windows Phone 7.
The XnaLocation project simply displays numeric values.
Example 1. XNA Project: XnaLocation File: Game1.cs (excerpt showing fields)
public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; SpriteFont segoe14; string text = "Obtaining location..."; Viewport viewport; Vector2 textPosition; ... }
|
As with the accelerometer, I chose to create and initialize the GeoCoordinateWatcher in the Initialize
override. The event handler is called in the same thread, so nothing
special needs to be done to format the results in a string:
Example 2. XNA Project: XnaLocation File: Game1.cs (excerpt)
protected override void Initialize() { GeoCoordinateWatcher geoWatcher = new GeoCoordinateWatcher(); geoWatcher.PositionChanged += OnGeoWatcherPositionChanged; geoWatcher.Start();
base.Initialize(); }
void OnGeoWatcherPositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> args) { text = String.Format("Latitude: {0:F3}\r\n" + "Longitude: {1:F3}\r\n" + "Altitude: {2}\r\n\r\n" + "{3}", args.Position.Location.Latitude, args.Position.Location.Longitude, args.Position.Location.Altitude, args.Position.Timestamp); }
|
The LoadContent method simply obtains the font and saves the Viewport for later text positioning:
Example 3. XNA Project: XnaLocation File: Game1.cs (excerpt)
protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); segoe14 = this.Content.Load<SpriteFont>("Segoe14"); viewport = this.GraphicsDevice.Viewport; }
|
The size of the
displayed string could be different depending on different values.
That’s why the position of the string is calculated from its size and
the Viewport values in the Update method:
Example 4. XNA Project: XnaLocation File: Game1.cs (excerpt)
protected override void Update(GameTime gameTime) { // Allows the game to exit if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();
Vector2 textSize = segoe14.MeasureString(text); textPosition = new Vector2((viewport.Width - textSize.X) / 2, (viewport.Height - textSize.Y) / 2); base.Update(gameTime); }
|
The Draw method is trivial:
Example 5. XNA Project: XnaLocation File: Game1.cs (excerpt)
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Navy);
spriteBatch.Begin(); spriteBatch.DrawString(kootenay14, text, textPosition, Color.White); spriteBatch.End();
base.Draw(gameTime); }
|
Because the GeoCoordinateWatcher is left running for the duration of the program, it should update the location as the phone is moved. Here’s where I live:
With the phone emulator, however, the GeoCoordinateWatcher
program might not work. With some beta software releases of Windows
Phone 7 development tools, the Accelerometer always returned the
coordinates of a spot in Princeton, New Jersey, perhaps as a subtle
reference to the college where Alan Turing earned his PhD.