Navigation
SDK allows you to implement the following navigation scenarios:
- Start the navigator in various modes (free-drive, turn-by-turn, and simulated navigation), including indoor navigation mode.
- Suggest alternative routes to a user along the way.
- Get dynamic route data during navigation.
- Configure sound notifications in the navigator.
To create a navigator, use the ready-to-use interface components and the NavigationManager class. If necessary, you can enable calculating the current device geolocation via the Radar API.
Getting started
Create a NavigationManager object.
Add a marker with the current device geolocation to the map using the MyLocationMapObjectSource data source and pass it to the addSource() method of the map.
import 'package:dgis_mobile_sdk_full/dgis.dart' as sdk;
// Creating a NavigationManager object
final sdk.Context sdkContext = sdk.DGis.initialize();
final sdk.NavigationManager navigationManager = sdk.NavigationManager(sdkContext);
// Adding a marker with the current geolocation to the map
final sdk.MyLocationMapObjectSource locationSource = sdk.MyLocationMapObjectSource(sdkContext);
map.addSource(locationSource);
navigationManager.mapManager.addMap(map);
You can start navigation in one of the three modes: free-drive, turn-by-turn, or simulated navigation.
You can change additional navigation settings using the properties of a NavigationManager object.
Using the Radar API
You can additionally use the Radar API to determine the device geolocation. The Radar API calculates the device geolocation based on cell towers and nearest Wi-Fi access points.
If the Radar API is enabled, the accuracy of the geolocation obtained from GPS and via the Radar API is constantly compared. By default, the geolocation calculated via GPS takes priority. The geolocation calculated via the Radar API is used in the following cases:
- GPS signal is lost.
- GPS accuracy is decreased to the value of the
minimalAcceptableGpsAccuracyM
parameter, which is passed during the initialization ofDefaultLocationSource
. In this case, the accuracy of the geolocation from the Radar API must be higher than from GPS.
The Radar API service is available only for Android, so you need to configure it in the Android part of the application. The service only works with the standard geolocation source used in Android. If you override the geolocation source in the Flutter part of your application, the service will not work. To learn how the Radar API works, see Using the Radar API for the Android SDK.
Application permissions
For the Radar API to work correctly, the application must receive permissions:
- ACCESS_FINE_LOCATION
- ACCESS_WIFI_STATE
- CHANGE_WIFI_STATE
- NEARBY_WIFI_DEVICES (optional) - is used to calculate the distance to a Wi-Fi access point. The permission is sensitive, so the application requests and receives it on its own. The service can work without this permission, but in some cases it increases the accuracy of geolocation.
Enabling the Radar API
To use the Radar API:
-
Contact the technical support to get an access key for the Radar API. Be sure to specify the
appId
of the application for which the key will be generated. For more information on obtaining a key for working with the SDK, see the Getting API keys section. -
For the build to succeed, add a build dependency required at compile time (
compileOnly
) in theapp
module in thebuild.gradle
of the application.It is recommended to use an artifact that matches the package you use. For example:
"ru.dgis.sdk.flutter_plugin:full:<version>"
- fordgis_mobile_sdk_full
"ru.dgis.sdk.flutter_plugin:map:<version>"
- fordgis_mobile_sdk_map
The
version
must match the version of the package you use, but not lower than12.6.0
(the first version with the Radar API support).Example for Full version:
dependencies { compileOnly("ru.dgis.sdk.flutter_plugin:full:12.6.0") }
-
In
MainActivity.kt
, override theconfigureFlutterEngine
method and register the settings provider for the Radar API in it.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) // Calling the register() method and passing an instance of the settings provider RadarSettingsProviderRegistry.register(MyRadarSettingsProvider()) } } // Implementing a provider without using the Radar API (RadarApiSettings.OFF object) class MyRadarSettingsProvider(): RadarSettingsProvider() { override fun provideSettings(): RadarApiSettings { return RadarApiSettings.OFF } } // Implementing a provider using the Radar API class MyRadarSettingsProvider(): RadarSettingsProvider() { override fun provideSettings(): RadarApiSettings { return RadarApiSettings.ON(apiKey = "your-api-key"). } }
For a full list of settings, see the Enabling the Radar API section for the Android SDK.
Free-drive mode
In free-drive mode, no route will be displayed on the map, but the user will still be informed about speed limits, traffic cameras, incidents, and road closures.
To start navigation in this mode, call the startFreeRoam() method without parameters.
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();
Turn-by-turn mode
In turn-by-turn mode, a route will be displayed on the map, and the user will receive navigation instructions as they move along the route.
To start navigation in this mode, call the start() method and specify a RouteBuildOptions object – arrival coordinates and route settings.
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);
Additionally, when calling the start()
method, you can specify a TrafficRoute object – a complete route object for navigation (for details, see the Routing chapter). In this case, a NavigationManager object will use the specified route instead of building a new one.
navigationManager.start(routeBuildOptions, trafficRoute);
Simulated navigation
In this mode, a NavigationManager object will not track the current location of the device. Instead, it will simulate a movement along the specified route. This mode is useful for debugging.
To use this mode, call the startSimulation() method and specify a RouteBuildOptions object (the route settings) and a TrafficRoute object (the route itself).
You can change the speed of the simulated movement using the SimulationSettings.speedMode property (specified in meters per second).
navigationManager.simulationSettings.speedMode = sdk.SimulationSpeedMode.overSpeed(
sdk.SimulationAutoWithOverSpeed(10)
);
navigationManager.startSimulation(routeBuildOptions, trafficRoute);
To stop the simulation, call the stop() method:
navigationManager.stop();
Indoor navigation
The SDK provides the ability to build routes within certain buildings for which there are floor plans. For example, you can build a route to a specific store in a mall, make a complex route between several points, etc.
This type of navigation is included in the Full SDK, but has a number of limitations:
- Can only be activated in pedestrian navigation mode.
- Due to the way modern positioning systems work, the ability to determine the position inside buildings is severely limited. When the system determines that the device has entered the limits of the building, the geopositioning tracking and its display will be disabled, leaving only the constructed route and recommendations for the passage.
Creating a route
Creating a route follows the principles described in Turn-by-turn mode with a few important additions. When creating a route endpoint RouteSearchPoint, the objectId
and levelId
parameters are mandatory.
import 'package:dgis_mobile_sdk_full/dgis.dart' as sdk;
// RouteBuildOptions object is also required
final routeBuildOptions = sdk.RouteBuildOptions(
finishPoint: sdk.RouteSearchPoint(
coordinates: sdk.GeoPoint(
latitude: sdk.Latitude(55.757670),
longitude: sdk.Longitude(37.660160),
),
objectId: ..., // You can get it from DgisMapObject
levelId: ..., // You can get it from DirectoryObject or RenderedObjectInfo
),
// The type of routeSearchOptions is PedestrianRouteSearchOptions
routeSearchOptions: sdk.RouteSearchOptions.pedestrian(
sdk.PedestrianRouteSearchOptions(),
),
);
Displaying a route
You can use the standard SDK features to detect when a user enters the indoor navigation mode by subscribing to an indoorChannel channel. This channel can be obtained from NavigationManager.indoorDetector.
Alternative routes
While driving along a car route, the navigator regularly searches for other ways to reach the destination point. For alternative routes search, all specified route parameters and intermediate points (except for already visited ones) are taken into account.
An alternative route is displayed on the map when a corresponding intersection is approaching, but the user still has enough time to make a decision.
The alternative route is shown on the map as a line with a bubble indicating the difference in travel time: how much time the user saves or loses if they switch to the alternative route. To switch to this route, the user can tap the route line on the screen or simply turn onto the alternative route at the intersection. If the user continues driving along the main route at the intersection, the alternative route disappears from the map.
"Better Route"
Among the alternative routes, a special type called the "better route" may be highlighted if a route meets the following criteria:
- The expected travel duration on the "better route" must be significantly shorter than the remaining part of the main route. The default difference is 5 minutes.
- The "better route" must be significantly different from the main route in geometry (in the roads it passes). By default, the difference in the length of differing route segments is 500 meters.
If a "better route" is found among the detected alternative routes, an additional UI control is displayed on the map. By tapping this element, the user can either switch to the "better route" or explicitly decline it. This element disappears from the screen after a certain time (30 seconds by default), but the route line continues to be displayed on the map until the user passes the intersection.
At any given time, only one route can be suggested to the user as the "better route". The search for a new "better route" begins after the user explicitly declines the previous one (by tapping the UI control) or after passing the intersection where the "better route" was available.
Parameters of alternative routes search
To change the parameters for searching for alternative routes, use the AlternativeRoutesProviderSettingsproperty of the NavigationManager object:
navigationManager.alternativeRoutesProviderSettings.alternativeRoutesEnabled = true;
To switch navigation to an alternative route, use AlternativeRouteSelector:
navigationManager.alternativeRouteSelector.selectAlternativeRoute(route);
Dynamic route information
During navigation, you can receive dynamic route information through the uiModel
property in NavigationManager and display it in the navigator UI. The Model class provides the following parameters:
- navigator state (
state
andstateChannel
) - current geolocation used by the navigator (
location
andlocationChannel
) - flag indicating if current geolocation is being used for navigation (
locationAvailable
andlocationAvailableChannel
) - route information with maneuvers (
route
androuteChannel
) - road events and traffic data on the route (
dynamicRouteInfo
anddynamicRouteInfoChannel
) - current user position on the route (
routePosition
androutePositionChannel
) - flag indicating if the speed limit is exceeded (
exceedingMaxSpeedLimit
andexceedingMaxSpeedLimitChannel
) - flag indicating if a "better route" is found (
betterRoute
andbetterRouteChannel
) - distance (
distance
) and time (duration
) to the end of the route - flag indicating if the free roam mode is used (
isFreeRoam
)
The route information in Model changes dynamically relative to the user current position on the route. To get the current position, subscribe to the routePositionChannel and then use this value to obtain other route data. See more about working with channels in the Data channels section.
For example, you can get the following information:
-
Distance (distance) and time (duration) from the current point to the end of the route:
// Channel for getting a position on the route final positionChannel = navigationManager.uiModel.routePositionChannel; final subscription = positionChannel.listen((routePosition) { WidgetsBinding.instance.addPostFrameCallback((_) { // Distance to the end of the route in meters final distance = navigationManager.uiModel.distance(); // Time to the end of the route in seconds final duration = navigationManager.uiModel.duration(); ... }); });
-
Road events and traffic jam data (dynamicRouteInfo). Road events data is stored in the RoadEventRouteAttribute and you can get an element from it by a position on the route.
For example, to get the next nearest road event on the route:
// Channel for getting a position on the route final positionChannel = navigationManager.uiModel.routePositionChannel; final subscription = positionChannel.listen((routePosition) { WidgetsBinding.instance.addPostFrameCallback((_) { // Get the nearest road event relative to the current position final dynamicInfo = navigationManager.uiModel.dynamicRouteInfo; final nearestEvent = dynamicInfo.roadEvents?.findNearForward(routePosition); ... }); });
-
Current navigator state. You can get it by subscribing to the stateChannel channel or separately (state):
// Get navigator state outside of the channel final currentState = navigationManager.uiModel.state;
// Channel for getting navigator state final stateChannel = navigationManager.uiModel.stateChannel; final subscription = stateChannel.listen((state) { WidgetsBinding.instance.addPostFrameCallback((_) { ... }); });
Navigator can be in one of the following states:
disabled
- inactive (initial state).navigation
- in the turn-by-turn navigation mode.routeSearch
- searching for a new route.finished
- completed navigation on the route (the finish point is reached).
Sound notifications
You can configure sound notifications in the navigator:
-
Adjust the volume level using the
volume
parameter of the AudioSettings class. Passed to the getAudioSettings method:final audioSettings = getAudioSettings(sdkContext); audioSettings.volume = 10;
-
Turn off the sound using the
mute
parameter of the AudioSettings class. Passed to the getAudioSettings method:final audioSettings = getAudioSettings(sdkContext); audioSettings.mute = true;
-
Configure categories of sound notifications using the
enabledSoundCategories
parameter of the SoundNotificationSettings class. Passed to thesoundNotificationSettings
property of a NavigationManager object.Examples of event categories: road works (
roadWorks
), toll roads (tolls
), obstacles (obstacles
), and others (see the full list in SoundCategory).By default, all categories of sound notifications are enabled. To configure notifications only for particular events, specify them:
import 'package:dgis_mobile_sdk_map/dgis.dart' as sdk; final soundSettings = navigationManager.soundNotificationSettings; soundSettings.enabledSoundCategories = sdk.SoundCategoryEnumSet({ sdk.SoundCategory.crossroadCameras, // Cameras of the intersection control sdk.SoundCategory.averageSpeedCameras, // Cameras for average speed control sdk.SoundCategory.roadWorks, // Road works sdk.SoundCategory.tolls, // Toll roads sdk.SoundCategory.obstacles // Obstacles }); navigationManager.soundNotificationSettings = soundSettings;