Перейти к основному содержимому

Навигация

С помощью SDK вы можете работать со следующими сценариями навигации:

Чтобы создать навигатор, используйте готовые элементы интерфейса и класс NavigationManager. При необходимости вы можете включить определение текущего местоположения через Radar API.

Начало работы

Создайте объект NavigationManager.

Добавьте на карту маркер с текущей геопозицией устройства с помощью источника данных MyLocationMapObjectSource и передайте его в метод карты addSource().

import 'package:dgis_mobile_sdk_full/dgis.dart' as sdk;

// Создание объекта NavigationManager
final sdk.Context sdkContext = sdk.DGis.initialize();
final sdk.NavigationManager navigationManager = sdk.NavigationManager(sdkContext);

navigationManager.mapManager.addMap(map);

// Добавление маркера с текущей геопозицией на карту
final sdk.MyLocationMapObjectSource locationSource = sdk.MyLocationMapObjectSource(sdkContext);
map.addSource(locationSource);

Навигатор может работать в трёх режимах: свободная навигация, ведение по маршруту и симуляция ведения.

Настройки навигатора можно изменить через свойства объекта NavigationManager.

Использование Radar API

Для определения местоположения устройства вы можете дополнительно использовать Radar API. Radar API вычисляет местоположение по сотовым вышкам и ближайшим точкам доступа Wi-Fi.

Если включено использование Radar API, то постоянно сравнивается точность геопозиции, полученная из GPS и через Radar API. По умолчанию приоритетной являются геопозиция, вычисленная с помощью GPS. Геопозиция, вычисленная через Radar API, используется в следующих случаях:

  • Потеря сигнала GPS.
  • Ухудшение точности GPS до значения параметра minimalAcceptableGpsAccuracyM, который передаётся при инициализации DefaultLocationSource. При этом точность геопозиции из Radar API должна быть выше, чем у GPS.

Сервис Radar API доступен только для Android, поэтому настройка происходит в Android-части приложения. Сервис работает только со стандартным источником геопозиции, который используется в Android. Если вы переопределили источник геопозиции в Flutter-части приложения, сервис не будет работать. Подробнее о работе Radar API см. в разделе Использование Radar API для Android SDK.

Разрешения для приложения

Для правильной работы Radar API приложение должно получить разрешения:

  • ACCESS_FINE_LOCATION;
  • ACCESS_WIFI_STATE;
  • CHANGE_WIFI_STATE;
  • NEARBY_WIFI_DEVICES (опционально) — используется для расчёта расстояния до точки доступа Wi-Fi. Разрешение является чувствительным, поэтому приложение запрашивает и получает его самостоятельно. Сервис может работать без этого разрешения, но в некоторых случаях оно увеличивает точность геопозиции.

Включение Radar API

Чтобы использовать Radar API:

  1. Обратитесь в службу поддержки 2ГИС для получения ключа доступа к Radar API. Обязательно укажите appId приложения, для которого будет создан ключ. Подробнее о получении ключа для работы с SDK см. в разделе Получение ключей доступа.

  2. Чтобы сборка прошла успешно, в build.gradle приложения в модуле app добавьте зависимость сборки, требуемую во время компиляции (compileOnly).

    Рекомендуется использовать артефакт, соответствующий используемому пакету. Например:

    • "ru.dgis.sdk.flutter_plugin:full:<version>" — для dgis_mobile_sdk_full;
    • "ru.dgis.sdk.flutter_plugin:map:<version>" — для dgis_mobile_sdk_map.

    Версия (<version>) должна соответствовать версии используемого пакета, но не ниже 12.6.0 (первая версия с поддержкой Radar API).

    Пример для Full-сборки:

    dependencies {
    compileOnly("ru.dgis.sdk.flutter_plugin:full:12.6.0")
    }
  3. В MainActivity.kt переопределите метод configureFlutterEngine() и зарегистрируйте в нём провайдер настроек для Radar API.

    import io.flutter.embedding.android.FlutterActivity
    import io.flutter.embedding.engine.FlutterEngine
    import ru.dgis.sdk.RadarSettingsProviderRegistry
    import ru.dgis.sdk.positioning.RadarSettingsProvider
    import ru.dgis.sdk.positioning.radar.RadarApiSettings

    class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    // Вызов метода register() и передача экземпляра провайдера настроек
    RadarSettingsProviderRegistry.register(MyRadarSettingsProvider())
    }
    }

    // Реализация провайдера без использования Radar API (объект RadarApiSettings.OFF)
    class MyRadarSettingsProvider(): RadarSettingsProvider() {
    override fun provideSettings(): RadarApiSettings {
    return RadarApiSettings.OFF
    }
    }

    // Реализация провайдера с использованием Radar API
    class MyRadarSettingsProvider(): RadarSettingsProvider() {
    override fun provideSettings(): RadarApiSettings {
    return RadarApiSettings.ON(apiKey = "your-api-key").
    }
    }

    Полный список настроек см. в разделе Подключение Radar API для Android SDK.

Свободная навигация

В этом режиме маршрут следования отсутствует, но навигатор будет информировать о превышениях скорости, дорожных камерах, авариях и ремонтных работах.

Чтобы запустить навигатор в этом режиме, нужно вызвать метод startFreeRoam() без параметров.

import 'package:dgis_mobile_sdk_full/dgis.dart' as sdk;

final sdk.Context sdkContext = sdk.DGis.initialize();
final sdk.NavigationManager navigationManager = sdk.NavigationManager(sdkContext);

navigationManager.startFreeRoam();

Ведение по маршруту

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

Чтобы запустить навигатор в этом режиме, нужно вызвать метод start() и указать объект RouteBuildOptions: координаты точки назначения и настройки маршрута.

import 'package:dgis_mobile_sdk_full/dgis.dart' as sdk;

final routeBuildOptions = sdk.RouteBuildOptions(
finishPoint: sdk.RouteSearchPoint(
coordinates: sdk.GeoPoint(
latitude: sdk.Latitude(55.757670),
longitude: sdk.Longitude(37.660160),
),
),
routeSearchOptions: sdk.RouteSearchOptions.car(
sdk.CarRouteSearchOptions(),
),
);

navigationManager.start(routeBuildOptions);

Дополнительно при вызове метода start() можно указать объект TrafficRoute — готовый маршрут для навигации (см. детали в разделе Маршруты). В таком случае навигатор не будет пытаться построить маршрут от текущего местоположения, а начнёт ведение по указанному маршруту.

navigationManager.start(routeBuildOptions, trafficRoute);

Симуляция ведения по маршруту

В этом режиме навигатор не будет отслеживать реальное местоположение устройства, а запустит симулированное движение по указанному маршруту. Режим удобно использовать для отладки.

Чтобы запустить навигатор в режиме симуляции, нужно вызвать метод startSimulation(), указав готовый маршрут (TrafficRoute) и его настройки (RouteBuildOptions).

Скорость движения можно изменить с помощью свойства SimulationSettings.speedMode (метры в секунду).

navigationManager.simulationSettings.speedMode = sdk.SimulationSpeedMode.overSpeed(
sdk.SimulationAutoWithOverSpeed(10)
);

navigationManager.startSimulation(routeBuildOptions, trafficRoute);

Остановить симуляцию можно с помощью метода stop().

navigationManager.stop();

Навигация внутри зданий

  

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

Этот тип навигации включен в набор стандартной поставки Full SDK, однако имеет ряд ограничений:

  • Может быть активирован только в режиме пешеходной навигации.
  • Из-за особенностей работы современных систем позиционирования возможности определения позиции внутри зданий сильно ограничены. Когда система определит, что устройство попало в пределы здания, слежение за геопозицией и ее отображение отключится, оставив построенный маршрут и рекомендации по проходу.

Создание маршрута

Создание маршрута происходит по принципам, описанным в разделе Ведение по маршруту с несколькими важными дополнениями. При создании конечной точки маршрута RouteSearchPoint обязательно указать параметры objectId и levelId.

import 'package:dgis_mobile_sdk_full/dgis.dart' as sdk;

// Также необходим объект RouteBuildOptions
final routeBuildOptions = sdk.RouteBuildOptions(
finishPoint: sdk.RouteSearchPoint(
coordinates: sdk.GeoPoint(
latitude: sdk.Latitude(55.757670),
longitude: sdk.Longitude(37.660160),
),
objectId: ..., // Можно получить из DgisMapObject
levelId: ..., // Можно получить из DirectoryObject или RenderedObjectInfo
),
// routeSearchOptions должен иметь тип PedestrianRouteSearchOptions
routeSearchOptions: sdk.RouteSearchOptions.pedestrian(
sdk.PedestrianRouteSearchOptions(),
),
);

Отображение маршрута

Для определения вхождения в режим навигации внутри здания можно пользоваться стандартными возможностями SDK, а именно подпиской на канал indoorChannel. Этот канал можно получить из NavigationManager.indoorDetector.

Альтернативные маршруты

Во время движения по автомобильному маршруту навигатор периодически ищет другие способы доехать до финишной точки. При поиске альтернативных маршрутов учитываются все заданные параметры маршрута и промежуточные точки (кроме тех, которые пользователь уже посетил). Интервал поиска составляет 1 минуту и не может быть изменён.

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

Альтернативный маршрут показывается на карте в виде линии с бабликом, на котором показана разница во времени движения: сколько времени сэкономит или потеряет пользователь, если перейдёт на альтернативный маршрут. Для перехода на этот маршрут пользователь может нажать на линию маршрута на экране или просто свернуть на развилке на альтернативный маршрут. Если на развилке пользователь продолжает движение по основному маршруту, альтернативный маршрут исчезает с карты.

Чтобы начать ведение по альтернативному маршруту, используйте AlternativeRouteSelector:

navigationManager.alternativeRouteSelector.selectAlternativeRoute(route);

«Маршрут лучше»

Среди альтернативных маршрутов может быть выделен особый тип «маршрут лучше», если маршрут соответствует следующим критериям:

  • Ожидаемая длительность движения по «маршруту лучше» должна быть существенно меньше, чем по оставшейся части основного маршрута. Разница по умолчанию — 5 минут.
  • «Маршрут лучше» должен существенно отличаться от основного маршрута по геометрии (по дорогам, через которые он проходит). По умолчанию разница в длине отличающихся рёбер маршрута — 500 метров.

Если среди обнаруженных альтернативных маршрутов находится «маршрут лучше», на карте дополнительно отображается отдельный UI-элемент, при нажатии на который пользователь может перейти на движение по «маршруту лучше» или явно отказаться от него. Этот элемент исчезает с экрана через определённое время (по умолчанию 30 секунд), но линия маршрута продолжает отображаться на карте до проезда развилки.

В один и тот же момент времени только один маршрут может быть предложен пользователю как «маршрут лучше». Поиск нового «маршрута лучше» начинается либо после явного отказа пользователя от предыдущего (при нажатии на UI-элемент), либо после проезда развилки с ним.

Настройки поиска альтернативных маршрутов

Чтобы изменить параметры поиска альтернативных маршрутов, используйте свойство AlternativeRoutesProviderSettings объекта NavigationManager.

Например, чтобы отключить поиск альтернативных маршрутов и «маршрута лучше», используйте параметры alternativeRoutesEnabled и betterRouteEnabled соответственно:

// Отключить поиск альтернативных маршрутов
navigationManager.alternativeRoutesProviderSettings.alternativeRoutesEnabled = false;

// Отключить поиск «маршрута лучше»
navigationManager.alternativeRoutesProviderSettings.alternativeRoutesEnabled = false;

Поиск «маршрута лучше» не может быть включён самостоятельно, если не включён поиск альтернативных маршрутов в целом.

Динамическая информация о маршруте

В процессе навигации вы можете получать динамическую информацию о маршруте через свойство uiModel в NavigationManager и отображать её в UI навигатора. В классе Model представлены следующие параметры:

  • состояние навигатора (state и stateChannel);
  • текущая геопозиция, с которой работает навигатор (location и locationChannel);
  • флаг использования текущей геопозиции для навигации (locationAvailable и locationAvailableChannel);
  • информация о маршруте с манёврами (route и routeChannel);
  • дорожные события и пробочные данные на маршруте (dynamicRouteInfo и dynamicRouteInfoChannel);
  • текущая позиция пользователя на маршруте (routePosition и routePositionChannel);
  • флаг превышения скорости (exceedingMaxSpeedLimit и exceedingMaxSpeedLimitChannel);
  • флаг нахождения «маршрута лучше» (betterRoute и betterRouteChannel);
  • расстояние (distance) и время (duration) до конца маршрута;
  • флаг режима свободной навигации (isFreeRoam).

Информация о маршруте в Model изменяется динамически относительно текущей позиции пользователя на маршруте. Чтобы получать актуальную позицию, подпишитесь на канал routePositionChannel и затем используйте это значение для получения других данных о маршруте. Подробнее о работе с каналами см. в разделе Потоки значений.

Например, получите следующую информацию:

  • Расстояние (distance) и время (duration) от текущей точки до конца маршрута:

    // Канал для получения позиции на маршруте
    final positionChannel = navigationManager.uiModel.routePositionChannel;

    final subscription = positionChannel.listen((routePosition) {
    WidgetsBinding.instance.addPostFrameCallback((_) {

    // Расстояние до конца маршрута в метрах
    final distance = navigationManager.uiModel.distance();

    // Время до конца маршрута в секундах
    final duration = navigationManager.uiModel.duration();

    ...
    });
    });
  • Дорожные события и данные о пробках (dynamicRouteInfo). Данные о дорожных событиях хранятся в контейнере RoadEventRouteAttribute, откуда элемент можно получить по позиции на маршруте.

    Например, чтобы получить следующее ближайшее дорожное событие на маршруте:

    // Канал для получения позиции на маршруте
    final positionChannel = navigationManager.uiModel.routePositionChannel;

    final subscription = positionChannel.listen((routePosition) {
    WidgetsBinding.instance.addPostFrameCallback((_) {

    // Получить ближайшее дорожное событие относительно текущей позиции
    final dynamicInfo = navigationManager.uiModel.dynamicRouteInfo;
    final nearestEvent = dynamicInfo.roadEvents?.findNearForward(routePosition);

    ...
    });
    });
  • Текущее состояние навигатора. Вы можете получать его через подписку на канал stateChannel или отдельно (state):

    // Получить состояние навигатора вне канала
    final currentState = navigationManager.uiModel.state;
    // Канал для получения состояния навигатора
    final stateChannel = navigationManager.uiModel.stateChannel;

    final subscription = stateChannel.listen((state) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
    ...
    });
    });

    Навигатор может находиться в одном из следующих состояний:

    • disabled — неактивен (начальное состояние);
    • navigation — в режиме ведения по маршруту;
    • routeSearch — в поиске нового маршрута;
    • finished — завершил ведение по маршруту (конечная точка достигнута).

Звуковые оповещения

Вы можете настроить звуковые оповещения в навигаторе:

  • Установить уровень громкости через параметр volume класса AudioSettings. Передаётся в метод getAudioSettings():

    final audioSettings = getAudioSettings(sdkContext);
    audioSettings.volume = 10; // Значение от 0 до 100
  • Отключить звук через параметр mute класса AudioSettings. Передаётся в метод getAudioSettings():

    final audioSettings = getAudioSettings(sdkContext);
    audioSettings.mute = true; // Звук отключен
  • Указать категории звуковых оповещений через параметр enabledSoundCategories класса SoundNotificationSettings. Передаётся в свойстве soundNotificationSettings объекта NavigationManager.

    Примеры категорий событий: дорожные работы (roadWorks), платные дороги (tolls), препятствия (obstacles) и другие (см. полный список в SoundCategory).

    По умолчанию все категории оповещений включены. Чтобы включить оповещения только для определённых событий, укажите их:

    import 'package:dgis_mobile_sdk_map/dgis.dart' as sdk;

    final soundSettings = navigationManager.soundNotificationSettings;

    soundSettings.enabledSoundCategories = sdk.SoundCategoryEnumSet({
    sdk.SoundCategory.crossroadCameras, // Камеры контроля перекрёстка
    sdk.SoundCategory.averageSpeedCameras, // Камеры контроля средней скорости
    sdk.SoundCategory.roadWorks, // Дорожные работы
    sdk.SoundCategory.tolls, // Платные дороги
    sdk.SoundCategory.obstacles // Препятствия
    });

    navigationManager.soundNotificationSettings = soundSettings;
  • По умолчанию язык озвучки навигатора зависит от языка приложения. Вы можете установить язык звуковых оповещений только для навигатора. Выберите голосовой пакет навигатора Voice с нужным языком и передайте объект NavigationVoice в класс VoiceSelector. Передаётся в свойстве voiceSelector объекта NavigationManager.

    Пример установки звуковых оповещений на английском языке:

    import 'package:dgis_mobile_sdk_full/dgis.dart' as sdk;

    // Получение списка голосов из VoiceManager
    final voices = sdk.getVoiceManager(context).voices;

    // Выбор голоса с нужным языком
    final voice = voices.firstWhere(
    (voice) => voice.language == 'en',
    );
    final navigationVoice = voice.navigationVoice;

    // Передача NavigationVoice в NavigationManager
    navigationManager.voiceSelector.voice = navigationVoice;

    Доступные значения языков озвучки (параметр language):

    • ru — русский;
    • en — английский;
    • ar — арабский.