Language: HTML | XAML

How to adjust the distance between location updates (XAML)

Applies to Windows and Windows Phone

Some apps need location updates only when the user has moved a large distance. This topic shows you how to create an app that lets the user adjust the distance between location updates.

The distance between location updates is known as the movement threshold. It is the minimum change in position required to raise a location update event. By implementing a movement threshold, you can create an app that provides only local information, like news or weather, and may not need location updates unless the user's location has changed to a different city.

The code in this app updates a table each time a location update event is raised. It makes updates by adding a row that contains the new coordinates and the distance that the user has moved since the last update. The distance moved is calculated with the haversine formula.

If a movement threshold is entered in the input box, you'll be able to see that the distanced moved each time an event is raised is always below the movement threshold.

Prerequisites

You should be familiar with XAML, Visual C#, and events.

Access to location must be enabled on the device that you're using.

Instructions

Step 1: Open Microsoft Visual Studio

Open an instance of Microsoft Visual Studio.

Step 2: Create a new project

Create a new project, choosing an Application from the Visual C# > Store project types.

Step 3: Enable the location capability

Open your project's Package.appxmanifest file, select the Capabilities tab, and then enable the Location capability by placing a checkmark in the corresponding check box.

Step 4: Replace the XAML code

Open your project's BlankPage.xaml.cs file and replace the existing code with the following.


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Devices.Geolocation;
using Windows.UI.Core;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/p/?LinkID=234238

namespace App1
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        private Geolocator geo = null;
        private CoreDispatcher _cd;
        private double prevLatitude = -1;
        private double prevLongitude = -1;
        private double totalDistance = 0;

        public MainPage()
        {
            this.InitializeComponent();
            _cd = Window.Current.CoreWindow.Dispatcher;
        }
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            if (geo == null)
            {
                geo = new Geolocator();
            }
            if (geo != null)
            {
                geo.MovementThreshold = float.Parse(tbThreshold.Text);

                geo.PositionChanged += new TypedEventHandler<Geolocator,
                    PositionChangedEventArgs>(geo_PositionChanged);
                geo.StatusChanged += new TypedEventHandler<Geolocator,
                    StatusChangedEventArgs>(geo_StatusChanged);

                tbThreshold.IsEnabled = false;
                TextBox1.Text = "Tracking Started " +
                                "(Time, Latitude, Longitude, Distance)\n";
            }
        }

        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            if (geo != null)
            {
                geo.PositionChanged -= new TypedEventHandler<Geolocator,
                    PositionChangedEventArgs>(geo_PositionChanged);
                geo.StatusChanged -= new TypedEventHandler<Geolocator,
                    StatusChangedEventArgs>(geo_StatusChanged);
                TextBox1.Text += "\nTracking Stopped.\n" +
                                 "Total Distance recorded: " +
                                 totalDistance.ToString("F2") + " m\n";
                tbThreshold.IsEnabled = true;
            }
        }

        private double CalculateDistance(double prevLat, double prevLong, double currLat, double currLong)
        {
            const double degreesToRadians = (Math.PI / 180.0);
            const double earthRadius = 6371; // kilometers

            // convert latitude and longitude values to radians
            var prevRadLat = prevLat * degreesToRadians;
            var prevRadLong = prevLong * degreesToRadians;
            var currRadLat = currLat * degreesToRadians;
            var currRadLong = currLong * degreesToRadians;

            // calculate radian delta between each position.
            var radDeltaLat = currRadLat - prevRadLat;
            var radDeltaLong = currRadLong - prevRadLong;

            // calculate distance
            var expr1 = (Math.Sin(radDeltaLat / 2.0) *
                         Math.Sin(radDeltaLat / 2.0)) +

                        (Math.Cos(prevRadLat) *
                         Math.Cos(currRadLat) *
                         Math.Sin(radDeltaLong / 2.0) *
                         Math.Sin(radDeltaLong / 2.0));

            var expr2 = 2.0 * Math.Atan2(Math.Sqrt(expr1),
                                         Math.Sqrt(1 - expr1));

            var distance = (earthRadius * expr2);
            return distance * 1000;  // return results as meters
        }

        async private void geo_PositionChanged(Geolocator sender, PositionChangedEventArgs e)
        {
            await _cd.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                Geoposition pos = e.Position;
                double updateDistance = 0;

                // Calculate distance;
                if ((prevLatitude == -1) || (prevLongitude == -1))
                {
                    updateDistance = 0;
                }
                else
                {
                    updateDistance = CalculateDistance(prevLatitude, prevLongitude,
                       pos.Coordinate.Point.Position.Latitude, pos.Coordinate.Point.Position.Longitude);
                }

                // Update tracking
                prevLatitude = pos.Coordinate.Point.Position.Latitude;
                prevLongitude = pos.Coordinate.Point.Position.Longitude;
                totalDistance += updateDistance;

                // display the results.
                TextBox1.Text += "Position Update: " +
                                 pos.Coordinate.Timestamp.ToString("T") + ", " +
                                 pos.Coordinate.Point.Position.Latitude.ToString("F3") + ", " +
                                 pos.Coordinate.Point.Position.Longitude.ToString("F3") + ", " +
                                 updateDistance.ToString("F2") + " m\n";
            });
        }

        async private void geo_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
        {
            var newStatus = args.Status;
            var strStatus = "";

            switch (newStatus)
            {
                case PositionStatus.Ready:
                    strStatus = "Location is available.";
                    break;
                case PositionStatus.Initializing:
                    strStatus = "Geolocation service is initializing.";
                    break;
                case PositionStatus.NoData:
                    strStatus = "Location service data is not available.";
                    break;

                case PositionStatus.Disabled:
                    strStatus = "Location services are disabled. Use the " +
                                "Settings charm to enable them.";
                    break;

                case PositionStatus.NotInitialized:
                    strStatus = "Location status is not initialized because " +
                                "the app has not yet requested location data.";
                    break;

                case PositionStatus.NotAvailable:
                    strStatus = "Location services are not supported on your system.";
                    break;

                default:
                    strStatus = "Unknown PositionStatus value (" +
                                newStatus.ToString() + ").";
                    break;
            }

            await _cd.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                TextBox1.Text += strStatus + "\n";
            });

        }
    }
}



You'll need to rename the namespace in the previous snippet with the name you gave your project. For example, if you created a project named GeolocationSample, you'd replace namespace App1 with namespace GeolocationSample.

Step 5: Replace the XAML code

Open the file MainPage.xaml and copy the following XML into this file (replacing its original contents).


<Page
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBox x:Name="tbThreshold" HorizontalAlignment="Left" Text="3" 
            TextWrapping="Wrap" VerticalAlignment="Top" Margin="339,31,0,0" 
            ToolTipService.ToolTip="Movement threshold (meters)" 
            TextAlignment="Center"/>
        <Button Content="Start Tracking" HorizontalAlignment="Left" Width="155"  
            Margin="10,30,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
        <Button Content="Stop Tracking" HorizontalAlignment="Left" Width="155" 
            Margin="170,30,0,0" VerticalAlignment="Top" Click="Button_Click_2"/>
        <ScrollViewer HorizontalAlignment="Left" Height="256" Width="390" 
            Margin="10,255,0,156" VerticalScrollBarVisibility="Auto">
            <TextBox x:Name="TextBox1" TextWrapping="Wrap" 
               Text="&lt;Click Start Tracking to begin.&gt;" Width="366"/>
        </ScrollViewer>

    </Grid>
</Page>


You'll need to replace the first part of the class name in the previous snippet with the namespace of your app. For example, if you created a project named GeolocationSample, you'd replace x:Class="App1.MainPage" with x:Class="GeolocationSample.MainPage". You should also replace xmlns:local="using:App1" with xmlns:local="using:GeolocationSample".

Step 6: Build the app

Choose Build > Build Solution to build the project.

Step 7: Test the app

  1. On the Debug menu, click Start Debugging to test the solution.
  2. The first time you run the sample, you'll get a prompt that asks if the app can use your location. Choose the Allow option.
  3. Click the Get Location button to get the current location.

Note  If location data doesn't display, check the following:

  • Make sure that you've enabled access to location, by opening package.appxmanifest in Solution Explorer and checking Location in the Capabilities tab.
  • If an administrator has disabled location services, your app won't be able to access the user's location. In the desktop Control Panel, open Change Location Settings and check if Turn on the Windows Location platform is checked.
  • On Windows Phone, the user can disable location in the Settings app. Go to the Settings app, select location and make sure that the Location services switch is toggled on.

Remarks

Note  The Windows Location Provider uses Wi-Fi information to determine location. If Wi-Fi is not available, it will use the IP Address instead. If your computer is not Wi-Fi enabled, you might not get any location update events, since IP Address data is not updated frequently.

Related topics

Geolocation Sample
Windows.Devices.Geolocation
Guidelines for Location-Aware Applications

 

 

Show:
© 2014 Microsoft