April 7, 2016 10:00 am

Map APIs and controls: Location services

Smartphones and tablets use a complex network of satellites, maps, and constantly updated databases to provide highly accurate information about the location of each device and its surroundings. The Universal Windows Platform (UWP), fortunately, removes all this complexity and provides you with a simple way to access geolocation information and translate it into useful address information.

In the first post of the Map APIs and Controls series, you learned how to get started with the Map Control. In this second post, we will dive a little deeper and learn how to:

  • Use geolocation, geocoding, and reverse geocoding
  • Find a route between two locations
  • Show directions for a route
  • Use offline maps
  • Use the Windows Map app launcher

Cartography 101

Let’s start with a refresher on geopositioning. Places on the earth can be identified by their latitude and longitude coordinates. Latitude lines (highlighted in orange below) run parallel to the equator (in red) and have increasing positive values above the equator, ending at 90 degrees, and negative values below, ending at -90 degrees. Longitude lines (in green) run from the North Pole to the South Pole. The prime meridian (in blue) has a value of zero and all other longitude lines have a value between zero and 180 degrees in relation to it.

Geopositioning will sometimes include altitude information. A position’s altitude is often measured as the distance above sea level, but can also be measured in other ways depending on the altitude reference system you are using.

1_geolocation

You use geolocation to determine the global coordinates of a device running your UWP app. In order to do this, you first need to remember to add Location explicitly as a feature in your app.

2_location

Having done this, you then use the Geolocator class to request the current geoposition of the device:



var status = await Geolocator.RequestAccessAsync();
if (status == GeolocationAccessStatus.Allowed)
{
    var locator = new Geolocator();
    var position = await locator.GetGeopositionAsync();
    var lat = position.Coordinate.Latitude;
    var lon = position.Coordinate.Longitude;
}


Geocoding, by contrast, involves retrieving a geoposition from a physical address or place name. Forexample, say you want to track down the fictional address of Sherlock Holmes. According to the stories by Arthur Conan Doyle, Holmes lived at 221B Baker Street in London. You use the MapLocationFinder class to perform the lookup.



var address = "221B Baker Street, London, England";

var results = await MapLocationFinder.FindLocationsAsync(address, null);
if (results.Status == MapLocationFinderStatus.Success)
{
    var lat = results.Locations[0].Point.Position.Latitude;
    var lon = results.Locations[0].Point.Position.Longitude;
}


The second argument of FindLocationsAsync allows you to pass a geoposition as a hint to the service, though in the case above a null value was sufficient. The results actually come back as an array of locations but, in this case, only one coordinate is returned for the request. These results can in turn be passed to a MapControl to bring up an aerial photo of the fictional Holmes home.



this.myMap.Center = new Geopoint(new BasicGeoposition()
{
    Latitude = lat,
    Longitude = lon
});


3_aerialHolmeshome

Reverse geocoding is the process of retrieving a physical address when you start with a geoposition. It works much the same as forward geocoding except that you call FindLocationsAtAsync rather than FindLocationsAsync.  For example, let’s see what happens if we start with the geopoint below:



Geopoint geoPosition = new Geopoint(new BasicGeoposition()
{
    Latitude = 51.502265,
    Longitude = -0.190998
});

var result = await MapLocationFinder.FindLocationsAtAsync(geoPosition);
if (result.Status == MapLocationFinderStatus.Success)
{
    var address = result.Locations[0].Address.FormattedAddress;
}


Your result is “13 Kensington Church Street London W8 4 United Kingdom,” which is approximately the address of the medical practice belonging to Sherlock Holmes’s friend and collaborator Dr. James Watson.

Finding your way from point A to point B with routes

Now that you are a master of geolocation, geocoding, and reverse geocoding, you’ll probably want to be able to find a route between two different geopositions. For instance, you want to help Sherlock Holmes get from his apartment over to Watson’s office for lunch.

To do this, you just need to pass your starting and ending coordinates to the MapRouteFinder class. You can get either walking directions or driving directions. In this case, walking directions seems more appropriate. You will be able to extract a route from the route finder and use it to create a MapRouteView. You then add the MapRouteView to your Map Control and a beautiful route is drawn out for you.



//221B Baker St
Geopoint holmes = new Geopoint(new BasicGeoposition()
{
    Latitude = 51.523304,
    Longitude = -0.158087
});

//Church St
Geopoint watson = new Geopoint(new BasicGeoposition()
{
    Latitude = 51.502265,
    Longitude = -0.190998
});

//find route
var result = await MapRouteFinder.GetWalkingRouteAsync(holmes, watson);
if (result.Status == MapRouteFinderStatus.Success)
{
    //put route in a map route view object
    var routeView = new MapRouteView(result.Route);

    //add route view object to map control
    this.myMap.Routes.Add(routeView);
}


The default route color is blue, but you can customize this with your own choice of fill color and outline by setting MapRouteView.RouteColor and MapRouteView.OutlineColor.

4_routecolor

Getting turn by turn directions

A picture is worth a thousand words, but when it comes to mapping services you sometimes still want the thousand words. The MapRoute object, which you used to create a MapRouteView above, actually contains detailed textual information about how to proceed from point A to point B. The structure of the MapRoute object is somewhat complex, however.

A MapRoute object contains a collection of legs that make up the route. Each leg, in turn, is composed of a collection of maneuvers (twists and turns required to finish that leg of the journey).  To extract the directions from the route, then, you will need to iterate through all of the legs and all of the maneuvers.



//create a new string builder
var sb = new StringBuilder();

//write out detailed directions
foreach (var leg in result.Route.Legs)
{
    foreach (var maneuver in leg.Maneuvers)
    {
        sb.AppendLine(maneuver.InstructionText);
    }
}


In this case, the string builder object contains the text instructions for a pleasant 1 hour and 3-minute stroll through London by way of Hyde Park.



Head south on Baker Street.
Turn right onto Marylebone Road.
Turn left onto Old Marylebone Road.
Turn right.
Turn left onto Lancaster Terrace.
Turn left onto Bayswater Road.
Turn right.
Turn right onto North Walk.
Turn left onto Budges Walk.
Turn slightly right.
Turn left.
Turn slightly right.
Turn slightly right.
Turn left onto Kensington Palace Green.
Turn right.
Turn slightly left onto Lancer Square.
Turn right onto Old Court Place.
Turn left onto Kensington Church Street.
You have reached your destination.


Using maps even when you are offline

Internet access is not always available. It would be unfortunate if this lack of access automatically disabled your UWP mapping application. Fortunately, Windows 10 Desktop offers a new feature (previously it was available only on phone) that allows maps to be downloaded.  Once they are downloaded, users can take advantage of Offline Maps when there’s no connectivity.

Users can configure their offline maps in Windows 10 by going to their Systems settings.

5_offlinemaps

For System settings, users may then select which maps they want to download and make available offline. The offline maps do not include aerial 3D maps or street views which are prohibitively large. Users may select the maps they want to store offline by country and region.

6_selectofflinemaps

The Windows Maps launcher

While the UWP Map Control provides you with an amazing amount of control over how you render maps, you don’t actually have to use all this power if you don’t want to do so. You can instead use the Windows Maps app, which is a URI-based pattern for displaying maps. Note that the URI for Windows Maps must always start with “bingmaps” and a colon.



var uri = new Uri(@"bingmaps:");
await Launcher.LaunchUriAsync(uri);


This launches the simplest possible map with no specifications.

7_simplestpossiblemap

In order to really take advantage of the Windows Maps app, you need to tack on a query string to the base URI and add name value pairs. The examples below demonstrate passing world coordinates, setting the zoom level, and searching for 221B Baker Street.



Bingmaps:?cp=51.505695~-0.135996
Bingmaps:?lvl=12
Bingmaps:?where=221B%20Baker%20Street%20London%20England


You can learn more about the Windows Maps app URI syntax on MSDN.

Wrapping up

In this post, you learned how to use geolocation, geocoding and reverse geocoding in UWP apps. You also learned how to use location services to create detailed routes between various points on a map. Finally, you learned about offline maps and the Windows Maps app. In the last post in this series, you’ll discover how to add pins and draw external elements on top of your map in order to customize it further for your app’s specific needs.