Skip to content
Ali Noshahi
  • Tutorials
MapControl

Part 2 – Track User’s location in UWP

  • August 18, 2018August 18, 2018
  • by Ali Noshahi

Guidelines for location-aware apps and tracking user’s location

In Part 1 of the tutorial, we learned how to get an API key for using MapControl, How to add a basic MapControl to a project and how we can show user location on it. In this section, we will learn how can we track user location and show it continuously on the MapControl.

For tracking the user’s location we need to first take a look at the great documentation of Microsoft for location-aware apps that I will mention some of them below belong the tutorial but reading it is worse than nothing.

The important things we should know before starting

The first call of use GeoLocator should be in UI Thread. The first call can be GetGeopositionAsync or register the PositionChanged event.

And the second important thing you should remember is Altitude = 0! I will say what I mean.

Implement, Configure and make a new ViewModel for user location tracking

First of all, I made a class to work easily with the GeoLocator. Create a folder named as Helper and add a class named “GeoLocatorHelper.cs” and here is the code for this file:


using System;
using Windows.ApplicationModel.ExtendedExecution;
using Windows.Devices.Geolocation;
using Windows.Foundation;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Popups;
using Windows.UI.Xaml;

class GeoLocatorHelper
{
public static event EventHandler<Geoposition> LocationFetched;
public static event EventHandler<Geocoordinate> LocationChanged;
public static bool IsLocationBusy { get; set; }
private static ExtendedExecutionSession session;
public static Geolocator Geolocator = new Geolocator() { DesiredAccuracy = PositionAccuracy.High, ReportInterval = 500 };

private static async void StartLocationExtensionSession()
{
try
{
session = new ExtendedExecutionSession();
session.Description = "Location Tracker";
session.Reason = ExtendedExecutionReason.LocationTracking;
var result = await session.RequestExtensionAsync();
if (result == ExtendedExecutionResult.Denied)
{
//TODO: handle denied
session.Revoked += new TypedEventHandler<object, ExtendedExecutionRevokedEventArgs>(ExtendedExecutionSession_Revoked);
}
}
catch { }
}

private static void ExtendedExecutionSession_Revoked(object sender, ExtendedExecutionRevokedEventArgs args)
{
try
{
if (session != null)
{
session.Dispose();
session = null;
}
}
catch { }
}

static GeoLocatorHelper()
{
IsLocationBusy = false;
StartLocationExtensionSession();
GetUserLocation();
}

public static async void GetUserLocation()
{
try
{
if (IsLocationBusy) return;
IsLocationBusy = true;
var access = await Geolocator.RequestAccessAsync();
if(access != GeolocationAccessStatus.Allowed)
{
var msg = new MessageDialog("We couldn't access location. Click ok to go to location settings,");
msg.Commands.Add(new UICommand("Ok", async delegate
{
await Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-location", UriKind.RelativeOrAbsolute));
Window.Current.Activated += Current_Activated;
}));
msg.Commands.Add(new UICommand("Cancel", delegate { }));
var a = await msg.ShowAsync();
}
Geolocator.PositionChanged += Geolocator_PositionChanged;
Geolocator.StatusChanged += GeoLocate_StatusChanged;
var res = Geolocator.GetGeopositionAsync();
if (res != null)
res.Completed += new AsyncOperationCompletedHandler<Geoposition>(LocationGetComplete);
IsLocationBusy = false;
}
catch { }
}

private static void Current_Activated(object sender, WindowActivatedEventArgs e)
{
if (e.WindowActivationState == CoreWindowActivationState.CodeActivated)
{
Window.Current.Activated -= Current_Activated;

GetUserLocation();
}
}

private static void Geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args)
{
try
{
LocationChanged?.Invoke(null, args.Position.Coordinate);
}
catch (Exception ex) { }
}

private static void GeoLocate_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
{
if (args.Status == PositionStatus.NoData || args.Status == PositionStatus.NotAvailable)
{
GetUserLocation();
}
}

private static void LocationGetComplete(IAsyncOperation<Geoposition> asyncInfo, AsyncStatus asyncStatus)
{
try
{
var res = asyncInfo.GetResults();
LocationFetched?.Invoke(null, res);
LocationChanged?.Invoke(null, res.Coordinate);
IsLocationBusy = false;
}
catch
{
}
}

}

Now, we need to modify the helper class based on our use case.
The first thing we should modify is the GeoLocator accuracy and the report interval.

public static Geolocator Geolocator = new Geolocator() { DesiredAccuracy = PositionAccuracy.High, ReportInterval = 500 };

GeoLocator accuracy modification

We can easily modify accuracy and report interval by changing values in the line of code mentioned above. If you want to create an app that needs a high accuracy like a navigation app like Windows Maps or WinGo Maps, So you need to use high for DesiredAccuracy but if you want to, for example, find user’s city or country so use default and keep this in mind that your desitions are so effective and will may cause better battery performance while using your app. Keep in mind only use location when you need, In this class because of our need to track the user’s location so I registered the PositionChanged and StatusChanged events, so if you don’t need to track the user’s location continuously, do not register them and only call GetGeopositionAsync.

GeoLocator report interval modification

The report interval is another value that sets how long does the user’s location is valid for your app in milliseconds. If you want to update the user’s location faster you can use small int values like the 500 I used that means it will let me know the new user’s location every 0.5 seconds but keep in mind this value should not be very small because it can easily make your app a battery drainer.

Using GeoLocatorHelper class in our codes

After these changes, we are good to go. Now we should do some changes in our ViewModel to use our helper class.

First, we no longer need the Geolocator property so remove it, also, we no longer need old codes of CallLoadPage function and LoadPage function so remove them too.

For getting started simply add these lines to your ViewModel constructor and create a method for them.

GeoLocatorHelper.LocationChanged += GeoLocatorHelper_LocationChanged;
GeoLocatorHelper.LocationFetched += GeoLocatorHelper_LocationFetched;

So, now your ViewModel should look like this :


using System;
using System.ComponentModel;
using Windows.Devices.Geolocation;
using Windows.UI.Core;
using Windows.UI.Xaml;

namespace UWPMapControl.ViewModel
{
class MapViewVM : INotifyPropertyChanged
{
private Geopoint _userloc;

public event PropertyChangedEventHandler PropertyChanged;
public Geopoint UserLocation { get => _userloc; set { _userloc = value; UpdateUI("UserLocation"); } }

CoreDispatcher Dispatcher { get; set; }
private void UpdateUI(string PropName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropName));
}

public MapViewVM()
{
Dispatcher = Window.Current.Dispatcher;
GeoLocatorHelper.LocationChanged += GeoLocatorHelper_LocationChanged;
GeoLocatorHelper.LocationFetched += GeoLocatorHelper_LocationFetched;
}

private void GeoLocatorHelper_LocationFetched(object sender, Geoposition e)
{

}

private void GeoLocatorHelper_LocationChanged(object sender, Geocoordinate e)
{

}
}
}

Now, we have all the things we need to track the user’s location.

Fetch and Update the user’s location

As mentioned above we registered two events of the GeoLocatorHelper class. First one is LocationChanged, and it will notify you that the user’s location changed and the second event is the LocationFetched, that notify you the user’s location after calling GetUserLocation method of the class. This event helps you to get the user’s location after the launch of the application and after clicking refresh location button.

Now we need to code for this events and it seems to be easy so far. We need to only set the UserLocation property with the new values coming from the events. So, we will write these lines for our events.

private async void GeoLocatorHelper_LocationFetched(object sender, Geoposition e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
{
UserLocation = e.Coordinate.Point;
});
}

private async void GeoLocatorHelper_LocationChanged(object sender, Geocoordinate e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
{
UserLocation = e.Point;
});
}

and also we need to a OneWay mode in our view:

<Grid maps:MapControl.Location="{Binding UserLocation, Mode=OneWay}">
<Ellipse Width="12" Height="12" Fill="DarkGreen"/>
<Ellipse Width="60" Height="60" Fill="DarkGreen" Opacity="0.7"/>
</Grid>

Now, if you run the application you can see a prompt for accessing location and after accepting it you can see the location pointer will be the point to your location.

But, if you think you’re done your wrong. If you try this app on a mobile or a device with a GPS hardware which is not present in Windows PCs and tablets you will see that location pointer starts moving around the map, but the fix is actually easy. As I mentioned before remember Altitude = 0. For any kind of pointer and element you want to add to the map you should set it’s Altitude = 0, and here is how you can do that:


private async void GeoLocatorHelper_LocationFetched(object sender, Geoposition e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
{
var point = e.Coordinate.Point.Position;
UserLocation = new Geopoint(new BasicGeoposition()
{
Latitude = point.Latitude,
Longitude = point.Longitude,
Altitude = 0
});
});
}

private async void GeoLocatorHelper_LocationChanged(object sender, Geocoordinate e)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
{
var point = e.Point.Position;
UserLocation = new Geopoint(new BasicGeoposition()
{
Latitude = point.Latitude,
Longitude = point.Longitude,
Altitude = 0
});
});
}

by doing this easy change you will fix the most annoying issue with elements on the map.

Hope you enjoy this tutorial. You can find the source code of this part here.

MapControl

Part 1 – UWP MapControl quick start

  • August 17, 2018August 18, 2018
  • by Ali Noshahi

A quick start guide for MapControl in UWP apps

Hi and welcome to the first part of using MapControl in UWP. In this part, we will learn how to get an API key for using MapControl, How to add a basic MapControl to our project, and how we can show user location on it.

Step 1: Getting API key for MapControl

We have to take these steps to get an API key for bing maps

  1. Go to Bing maps dev center website at https://www.bingmapsportal.com/
  2. Sign in to Bing maps dev center using your Microsoft account (Hotmail, Live or Outlook)
  3. If it is the first time you are logging in to Bing maps dev center you should specify an account name and email address and agree to the Bing Maps Platform APIs’ Terms of Use (TOU).
  4. Select my keys under my account drop-down menu.
  5. Select create a new API key option or if it is the first time you are getting an API key Bing maps dev center will show you the form of creating a new API key.
  6. In this form, you should select a name for your application, key type, and Application type.
  7. I will use dev/Test application type and Basic option for the key type.
  8. After clicking on the create button, you’ll see your key (it is a long string) and you can see more information about it.
  9. Copy your API key and keep it to use in MapControl.

Step 2: Adding MapControl to UWP project

First, we have to create a blank universal Windows platform (UWP) project. I will set Windows 10, version 1803 as my target version and Windows 10 Anniversary update as my minimum version.

In this tutorial, we will use the MVVM (Model, View, ViewModel) pattern to make the project update easier. If you don’t know what is MVVM pattern I suggest you read this post on Wikipedia and this one on CodeProject.

First, we will create a new View called “MapView.Xaml” in our View folder on the root of the solution.
In MapView now we should add following lines

<Grid>
    <maps:MapControl MapServiceToken="YOUR_API_KEY_HERE"/>
</Grid>

Remember to replace YOUR_API_KEY_HERE with your API Key to don’t see the red warning message anymore and do not forget to add the following reference:

xmlns:maps="using:Windows.UI.Xaml.Controls.Maps"

Now we need a view model for our view, So we have to add a “MapViewVM.cs” class in the ViewModel folder at the root of the project.

using System.ComponentModel;

namespace UWPMapControl.ViewModel
{
class MapViewVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
}

Now we have to link our View to our ViewModel. in our view Xaml, we should add the following lines

    <Page.DataContext>
        <VM:MapViewVM/>
    </Page.DataContext>

also, we should add this reference:

 xmlns:VM="using:UWPMapControl.ViewModel" 

If you see any error rebuild your project.

Now on MainPage, we should add a frame to navigate our UI to MapView, So add following lines:

<Grid>
<Frame x:Name="fr">
<view:MapView/>
</Frame>
</Grid>

and do not forget to add the following reference:


xmlns:View="using:UWPMapControl.View"

Now you can run your app. Congratulations, You now have a map control in your application.

Step 3: Find the user’s location and show it on the map

First, we have to add Location capability to the application, so launch the “Package.AppxManifest” file and go to Capabilities tab and check the Location capability if it is not checked.

Now we are able to get user location in our app. Go to the MapViewVM file and add these lines to get user’s location.


class MapViewVM : INotifyPropertyChanged
{
private Geopoint _userloc;

public event PropertyChangedEventHandler PropertyChanged;
public Geopoint UserLocation { get => _userloc; set { _userloc = value; UpdateUI("UserLocation"); } }

CoreDispatcher Dispatcher { get; set; }
Geolocator Geolocator { get; set; }
private void UpdateUI(string PropName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropName));
}

public MapViewVM()
{
Dispatcher = Window.Current.Dispatcher;
Geolocator = new Geolocator();
CallLoadPage();
}

public async void CallLoadPage()
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, LoadPage);
}

private async void LoadPage()
{
//Requesting to access user location.
var Access = await Geolocator.RequestAccessAsync();
//If we weren't able to access user's location because location service is off
//or user didn't accept the location access for our application we will notify user.
if(Access != GeolocationAccessStatus.Allowed)
{
var msg = new MessageDialog("We weren't able to access your location. click ok to navigate windows location settings.");
msg.Commands.Add(new UICommand("OK", async delegate
{
await Launcher.LaunchUriAsync(new Uri("ms-settings:privacy-location", UriKind.RelativeOrAbsolute));
}));
msg.Commands.Add(new UICommand("Cancel", delegate { }));
var a = await msg.ShowAsync();
return;
}

//If we were able to access user location now we get his/her location.
var Position = await Geolocator.GetGeopositionAsync();
UserLocation = Position.Coordinate.Point;
}
}

In the code above we create an instance of GeoLocator to access user’s location and RequestAccessAsync to let user know we want to user his/her location. After user’s approve we try to get user’s location by calling GetGeopositionAsync.

Now we have current user location in UserLocation property, but we need some way to show it on the MapControl.
So, we’ll back to the view to add a pointer to locate the user on the map.


<Grid>
<maps:MapControl MapServiceToken="Qe4rzjfqSL7zZlp8xb8A~4o8bBFXIWVuiCUT9pX2cUg~Auk92xku8o5QuFTpMvN0_7Os9b6pREma6NBC8ojGjU0N3Eq3BBzkI9ehNfu065Wv">
<Ellipse Width="25" Height="25" Fill="DarkGreen" maps:MapControl.Location="{Binding UserLocation}"/>
</maps:MapControl>
</Grid>

I added an Ellipse to show the user’s location on the map. After locating user a green pointer will show the user’s location, But it still doesn’t track the user’s location to update the location. In the next tutorials, we will learn how to track the user’s location and how to add some controls to make the application’s user experience so much greater.

Hope you enjoy the first part of our tutorial. See the second part here.

Download Source code project for this part here.

Recent Posts

  • Part 2 – Track User’s location in UWP
  • Part 1 – UWP MapControl quick start

Recent Comments

    Archives

    • August 2018

    Categories

    • MapControl

    Meta

    • Log in
    • Entries feed
    • Comments feed
    • WordPress.org
    ©Ali Noshahi 2018 - 2019
    Theme by Colorlib Powered by WordPress