Разработка под Android в .NET MAUI. Класс Location

В предыдущей части мы использовали объекты класса Location исключительно для вывода данных о широте и долготе местоположения устройства. Вместе с тем, класс Location предоставляет и другие полезные свойства и методы, которые мы изучим в этой части.

Класс Location

Свойства класса

Класс Location содержит информацию о местоположении, а также предоставляет ряд методов для расчёта расстояний между двумя точками. У класса определены следующие свойства:

Свойство Тип Описание
Accuracy double? Горизонтальная точность определения местоположения в метрах
Altitude double? Высота расположения
AltitudeReferenceSystem AltitudeReferenceSystem Система в которой определялось значение Altitude. Может принимать следующие значения:

  • Unspecified — эталонная система высоты не указана.
  • Terrain — эталонная система высоты основана на расстоянии выше местности или на уровне земли
  • Ellipsoid — эталонная система высоты основана на эллипсоиде (обычно WGS84), который является математическим приближением формы Земли.
  • Geoid — эталонная система высоты основана на расстоянии выше уровня моря.
  • Surface — эталонная система высоты основана на расстоянии над самыми высокими поверхностными структурами, такими как здания, деревья, дороги и т. д., над ландшафтом или уровнем земли
Course double? Азимут местоположения (градусы относительно истинного севера на момент определения местоположения)
IsFromMockProvider bool Указывает, является ли это расположение источником из макетированного датчика и, следовательно, может не быть реальным расположением устройства
Latitude double Широта
Longitude double Долгота
ReducedAccuracy bool Указывает имеет ли это расположение сниженную точность чтения (используется только в iOS)
Speed double? Скорость в метрах в секунду на момент определения местоположения
Timestamp DateTimeOffset Метка времени в формате UTC
VerticalAccuracy double? Вертикальная точность определения местоположения в метрах

Как можно видеть по типам данных, которые используются в свойствах, не все значения свойств могут возвращаться. Например, горизонтальная и вертикальная точность, высота и скорость могут отсутствовать в возвращаемых значениях.

Что касается определения высоты (Altitude), то в Android это значение всегда возвращается в системе WGS-84 (свойство AltitudeReferenceSystem равно Ellipsoid).

Методы класса

Что касается методов класса Location, то здесь стоит отметить метод CalculateDistance(), который имеет несколько перегрузок и позволяет рассчитываться расстояние между двумя местоположениями. Одна из версий этого метода выглядит следующим образом:

public static double CalculateDistance(Location locationStart, Location locationEnd, DistanceUnits units);

здесь locationStart — начальное местоположение, locationEnd — конечное местоположение, DistanceUnits — единицы измерения, может принимать следующие значения:

  • Kilometers — расстояние возвращается в километрах
  • Miles — расстояние возвращается в милях

Попробуем воспользоваться этим методом в нашем приложении, которое мы разработали в предыдущей части.

Определение расстояний с помощью класса Location

На данный момент наше приложение умеет прослушивать событие смены местоположения. Сам обработчик события выглядит следующим образом:

private void OnLocationChanged(object? sender, GeolocationLocationChangedEventArgs args)
{
    Locations.Add(args.Location);
}

А весь исходный код класса MainPage следующий:

using System.Collections.ObjectModel;

namespace MauiLocation
{
    public partial class MainPage : ContentPage
    {
        private CancellationTokenSource _cancelTokenSource;
        private bool _isCheckingLocation;

        public ObservableCollection<Location> Locations { get; private set; } = [];
        public string LastError { get; private set; } = string.Empty;

        public MainPage()
        {
            InitializeComponent();
        }


        private void OnLocationChanged(object? sender, GeolocationLocationChangedEventArgs args)
        {
            Locations.Add(args.Location);
        }

        public void OnListeningFiled(object? sender, GeolocationListeningFailedEventArgs args)
        {
            switch (args.Error) 
            {
                case GeolocationError.Unauthorized:
                {
                        LastError = "Разрешение на получение данных о местоположении устройства было отозвано";
                        break;
            }
                    case GeolocationError.PositionUnavailable: 
                    {
                        LastError = "Не удалось получить данные о местоположении";
                        break;
                    }
            }  
        }


        private async void OnListeningClicked(object sender, EventArgs e)
        {
            if (Geolocation.Default.IsListeningForeground)
            {
                Geolocation.Default.LocationChanged -= OnLocationChanged;
                Geolocation.Default.ListeningFailed -= OnListeningFiled;
                Geolocation.Default.StopListeningForeground();
            }
            else
            {
                Geolocation.Default.LocationChanged += OnLocationChanged;
                Geolocation.Default.ListeningFailed += OnListeningFiled;
                GeolocationListeningRequest request = new GeolocationListeningRequest();
                request.DesiredAccuracy = GeolocationAccuracy.Best;
                request.MinimumTime = TimeSpan.FromSeconds(1);

                if (await Geolocation.Default.StartListeningForegroundAsync(request) == false)
                    LastError = "Не смогли запустить прослушивание";

            }
        }

        private async void OnCounterClicked(object sender, EventArgs e)
        {
            
            _isCheckingLocation = true;
            Indicator.IsRunning = _isCheckingLocation;
            GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Best, TimeSpan.FromSeconds(20));
            _cancelTokenSource = new CancellationTokenSource();
            Location? location = await Geolocation.Default.GetLocationAsync(request, _cancelTokenSource.Token);
            if (location != null)
            {
                LatitudeLabel.Text = location.Latitude.ToString();
                LongitudeLabel.Text = location.Longitude.ToString();
            }
            _isCheckingLocation = false;
            Indicator.IsRunning = _isCheckingLocation;
        }

        public void CancelRequestClicked(object sender, EventArgs e)
        {
            if (_isCheckingLocation && _cancelTokenSource != null && _cancelTokenSource.IsCancellationRequested == false)
                _cancelTokenSource.Cancel();
        }
    }
}

Изменим обработчик события смены местоположения следующим образом:

public partial class MainPage : ContentPage, INotifyPropertyChanged
{
    ...

    public double Distance { get; set; }

    ...

    private void OnLocationChanged(object? sender, GeolocationLocationChangedEventArgs args)
    {
        
        if (Locations.Count > 0)
        {
            var startLocation = Locations.Last();
            Distance = Location.CalculateDistance(startLocation, args.Location, DistanceUnits.Kilometers);
            DistanceLabel.Text = Distance.ToString();
        }
        Locations.Add(args.Location);
    }

  ...
}

Здесь мы, используя метод CalculateDistance(), рассчитываем расстояние между текущей и предыдущей точкой расположения устройства.

Исходный код страницы MainPage сделаем таким:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:locations="clr-namespace:Microsoft.Maui.Devices.Sensors;assembly=Microsoft.Maui.Essentials"
             x:Class="MauiLocation.MainPage"
             xmlns:m="clr-namespace:MauiLocation"
             x:DataType="m:MainPage">
    
    <ScrollView>
        <VerticalStackLayout
            Padding="30,0"
            Spacing="25">
            <ActivityIndicator ZIndex="1" IsRunning="False" x:Name="Indicator"/>

            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                    <RowDefinition></RowDefinition>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition></ColumnDefinition>
                    <ColumnDefinition></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <Label Text="Широта" Grid.Column="0" Grid.Row="0"/>
                <Label Text="00,000000"  x:Name="LatitudeLabel" Grid.Column="1" Grid.Row="0"/>

                <Label Text="Долгота" Grid.Column="0" Grid.Row="1"/>
                <Label Text="00,000000"  x:Name="LongitudeLabel" Grid.Column="1" Grid.Row="1"/>

                <Label Text="Расстояние, км" Grid.Column="0" Grid.Row="2"/>
                <Label Text="0"  x:Name="DistanceLabel" Grid.Column="1" Grid.Row="2"/>
            </Grid>
            
            <Button x:Name="CounterBtn"
                    Text="Click me"
                    Clicked="OnCounterClicked"
                    HorizontalOptions="Fill" />
            <Button Text="Отменить запрос"
                    Clicked="CancelRequestClicked"
                    HorizontalOptions="Fill" />
            <Button Text="Начать прослушивание"
                    Clicked="OnListeningClicked"/>
            <CollectionView ItemsSource="{Binding Locations, Source={RelativeSource AncestorType={x:Type m:MainPage}}, Mode=TwoWay}">
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="locations:Location">
                        <HorizontalStackLayout>
                            <Label Text="{Binding Latitude, StringFormat='Широта {0}; '}"/>
                            <Label Text="{Binding Longitude, StringFormat='Долгота {0} '}"/>
                        </HorizontalStackLayout>
                    </DataTemplate>
                </CollectionView.ItemTemplate>
            </CollectionView>
            <Label Text="{Binding LastError}"/>
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Теперь запустим приложение, включим прослушивание событий и по перемещаем устройство. В результате, вы должны увидеть на экране примерно следующую информацию:Location

Итого

Класс Location содержит информацию о местоположении устройства, включая данные о высоте точки расположения, азимуте и, если доступно, то скорости, с которой перемещалось устройство в момент получения данных о местоположении. Также, Location содержит методы для расчёта расстояния между двумя точками.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии