Navigation | Mobile SDK | Urbi Documentation
Android SDK

Navigation

SDK allows you to implement the following navigation scenarios:


To create a navigator, use the ready-to-use interface components and the NavigationManager class. You can get data about the current device geolocation in one of the following ways:

  • Use the standard data source. If necessary, you can also enable calculating the geolocation via the Radar API.
  • Configure the navigator to work with your custom geolocation source.

Add NavigationView elements and a DefaultNavigationControls to your MapView.

<ru.dgis.sdk.map.MapView
    android:id="@+id/mapView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <ru.dgis.sdk.navigation.NavigationView
        android:id="@+id/navigationView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
        <ru.dgis.sdk.navigation.DefaultNavigationControls
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </ru.dgis.sdk.navigation.NavigationView>
</ru.dgis.sdk.map.MapView>

To allow the SDK access to the current device geolocation, use the standard DefaultLocationSource data source or create a custom geolocation source. Register the data source in the SDK by calling the registerPlatformLocationSource() method and passing the SDK context and source to it.

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.

Create a NavigationManager object.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    sdkContext = DGis.initialize(applicationContext, apiKeys)

    // Creating and registering a current geolocation data source in the SDK
    locationProvider = DefaultLocationSource(applicationContext)
    registerPlatformLocationSource(sdkContext, locationProvider)

    setContentView(R.layout.activity_navigation)

    findViewById<MapView>(R.id.mapView).apply { mapView ->
        lifecycle.addObserver(mapView)

        mapView.getMapAsync { map ->
            // Adding a marker with the current geolocation to the map
            map.addSource(
                MyLocationMapObjectSource(
                    sdkContext,
                    MyLocationDirectionBehaviour.FOLLOW_SATELLITE_HEADING,
                    createSmoothMyLocationController()
                )
            )
        }
    }

    // Creating a NavigationManager object
    navigationManager = NavigationManager(sdkContext)

    findViewById<NavigationView>(R.id.navigationView).apply {
        // Linking the created NavigationManager object to the NavigationView
        navigationManager = this@NavigationActivity.navigationManager
    }

    // Starting navigation in a free-drive mode
    navigationManager.start()
}

You can start navigation in one of three modes: free-drive, turn-by-turn, and the simulated navigation mode.

You can change additional navigation settings using the properties of a NavigationManager object.

Along with the default device geolocation data source (DefaultLocationSource) you can use the Radar API. 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 of DefaultLocationSource. In this case, the accuracy of the geolocation from the Radar API must be higher than from GPS.

For the Radar API to work correctly, the application must receive the permissions:

To use the Radar API:

  1. 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.

  2. Pass the RadarApiSettings parameter when creating DefaultLocationSource:

    import ru.dgis.sdk.positioning.DefaultLocationSource
    import ru.dgis.sdk.positioning.radar.RadarApiSettings
    
    val source = DefaultLocationSource(this, RadarApiSettings.ON(apiKey = "your-api-key"))
    
  3. When creating a RadarApiSettings.ON object, you can additionally customize the service behavior. Example with a full set of parameters:

    data class ON(
        val apiKey: String,
        val baseUrl: String = "https://radar.api.2gis.com",
        val appId: String? = null,
        val httpClient: RadarApiHttpClient = DefaultHttpClient(),
        val idlePollTimeoutMs: Int = 15_000,
        val activePollTimeoutMs: Int = 1_000,
        val minimalAcceptableGpsAccuracyM: Int = 99,
    ) : RadarApiSettings()
    

    Where:

    • apiKey - access key to the Radar API.
    • baseUrl - API base URL. Default value: https://radar.api.2gis.com.
    • appId - appId of the application with the applied restrictions of the access key. By default, the applicationId value of the current application is used. If the key uses the Wildcard and does not match the current applicationId, or if restrictions are set on a different appId, specify this appId.
    • httpClient - HTTP client for making requests. By default, the standard DefaultHttpClient is used.
    • idlePollTimeoutMs - timeout between requests in inactive state in ms. It is used when the Radar API is not the main source of geolocation (for example, when GPS accuracy is higher). To keep data up-to-date, geolocation requests are made no more frequently than this time period.
    • activePollTimeoutMs - timeout between requests in active state in ms. It is used when the system decides that the geolocation obtained from the Radar API is preferable. The service turns on an active mode and sends requests to the Radar API no more frequently than this time period. Requests can be sent less frequently if the data received from the device sensors does not change (for example, the device stays in place).
    • minimalAcceptableGpsAccuracyM - minimum acceptable GPS accuracy in meters. If the GPS accuracy is higher or equal to this value, the GPS data source is considered preferable, even if the accuracy of the geolocation from the Radar API is higher.

To disable the Radar API for calculating geolocation, you can use one of the following methods:

  • Create DefaultLocationSource, passing only the application context:

    val source = DefaultLocationSource(this)
    
  • Pass RadarApiSettings.OFF object to DefaultLocationSource:

    val source = DefaultLocationSource(this, RadarApiSettings.OFF)
    

You can use a custom geolocation source as a source of current geolocation data. To do this, implement the LocationSource interface and register the created source in the SDK using the registerPlatformLocationSource() method.

// Creating a current geolocation data source
val customSource = CustomLocationSource()

// Registering a data source in the SDK
registerPlatformLocationSource(sdkContext, customSource)

When the SDK requires a geolocation, a LocationChangeListener object is passed to the activate() function.

public class CustomLocationSource: LocationSource {
    // Enabling a geolocation source
    override fun activate(listener: LocationChangeListener?) {
    }
    ...
}

After that, to report the current geolocation, pass an array of Location objects to LocationChangeListener (ordered from the oldest geoposition to the newest) using the onLocationChanged() method.

val location = Location(...)
val newLocations = arrayOf(location)
listener.onLocationChanged(newLocations)

To notify about source availability change, use the onAvailabilityChanged() method.

You can add additional logic to handle different levels of geolocation accuracy and pass it to the setDesiredAccuracy() method as a DesiredAccuracy object.

When the geolocation source is no longer required, the SDK calls the deactivate() function.

public class CustomLocationSource: LocationSource {
    // Enabling a geolocation source
    override fun activate(listener: LocationChangeListener?) {
    }

    // Disabling a geolocation source
    override fun deactivate() {
    }

    // Changing the required accuracy level
    override fun setDesiredAccuracy(accuracy: DesiredAccuracy?) {
    }
}

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 start() method without arguments.

navigationManager.start()

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.

val routeBuildOptions = RouteBuildOptions(
    finishPoint = RouteSearchPoint(
        coordinates = GeoPoint(latitude = 55.752425, longitude = 37.613983)
    ),
    routeSearchOptions = CarRouteSearchOptions(
        avoidTollRoads = true,
        avoidUnpavedRoads = false,
        avoidFerry = false,
        routeSearchType = RouteSearchType.JAM
    )
)

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, NavigationManager will use the specified route instead of building a new one.

navigationManager.start(routeBuildOptions, trafficRoute)

In this mode, NavigationManager 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 (route settings) and a TrafficRoute object (the route itself).

You can change the speed of the simulated movement using the SimulationSettings.speed property (specified in meters per second).

navigationManager.simulationSettings.speed = 30 / 3.6
navigationManager.startSimulation(routeBuildOptions, trafficRoute)

To stop the simulation, call the stop() method.

navigationManager.stop()

  

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 the constructed route and recommendations for the passage. We are working to overcome these challenges and hope to provide a reliable way to determine the location inside buildings in the future.

Creating a route follows the principles described in Turn-by-turn mode with a few important additions. When creating a route endpoint RouteSearchPoint, you must specify the objectId and levelId parameters.

// RouteBuildOptions object is also required
val routeBuildOptions = RouteBuildOptions(
    finishPoint = RouteSearchPoint(
        coordinates = GeoPoint(latitude = 55.752425, longitude = 37.613983),
        objectId = ..., // You can get it from DgisMapObject
        levelId = ..., // You can get it from DirectoryObject or RenderedObjectInfo
    ),
    // routeSearchOptions must be of type PedestrianRouteSearchOptions
    routeSearchOptions = PedestrianRouteSearchOptions()
)

If you use the standard set of NavigationView and DefaultNavigationControls, as recommended in beginning of section, this feature is already available and does not require preconfiguration.

When using your own controllers in the UI, you can use the standard SDK features to detect when entering the indoor navigation mode, namely subscribing to indoorChannel. This channel can be obtained from [NavigationManager](/en/android/sdk/reference/stable/ru.dgis.sdk.navigation.NavigationManager#nav-lvl1--val indoorDetector)

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.

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.

To change the parameters for searching for alternative routes, use the AlternativeRoutesProviderSettings property of the NavigationManager object:

navigationManager.alternativeRoutesProviderSettings.alternativeRoutesEnabled = true

To switch navigation to an alternative route, use AlternativeRouteSelector:

navigationManager.alternativeRouteSelector.selectAlternativeRoute(route)

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 and stateChannel)
  • current geolocation used by the navigator (location and locationChannel)
  • flag indicating if current geolocation is being used for navigation (locationAvailable and locationAvailableChannel)
  • route information with maneuvers (route and routeChannel)
  • road events and traffic data on the route (dynamicRouteInfo and dynamicRouteInfoChannel)
  • current user position on the route (routePosition and routePositionChannel)
  • flag indicating if the speed limit is exceeded (exceedingMaxSpeedLimit and exceedingMaxSpeedLimitChannel)
  • flag indicating if a "better route" is found (betterRoute and betterRouteChannel)
  • time to the end of the route (duration)
  • 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:

  • Time (duration) from the current point to the end of the route:

    // Channel for getting a position on the route
    val positionChannel = navigationManager.uiModel.routePositionChannel
    
    val positionSubscription = positionChannel.connect {
        // Time to the end of the route in seconds
        val 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
    val positionChannel = navigationManager.uiModel.routePositionChannel
    
    val positionSubscription = positionChannel.connect { routePosition ->
        // Get the nearest road event relative to the current position
        val dynamicInfo = navigationManager.uiModel.dynamicRouteInfo
        val nearestEvent = routePosition?.let { dynamicInfo.roadEvents.findNearForward(it) }
        ...
    }
    
  • Current navigator state. You can get it by subscribing to the stateChannel channel or separately (state):

    // Get navigator state outside of the channel
    val currentState = navigationManager.uiModel.state
    
    // Channel for getting navigator state
    val stateChannel = navigationManager.uiModel.stateChannel
    
    val stateSubscription = stateChannel.connect {
        ...
    }
    

    Navigator can be in one of the following states:

    • DISABLED - inactive (initial state).
    • NAVIGATION - in the turn-by-turn navigation mode.
    • ROUTE_SEARCH - searching for a new route.
    • FINISHED - completed navigation on the route (the finish point is reached).

You can configure sound notifications in the navigator using the soundNotificationSettings property of a NavigationManager object. The SoundNotificationSettings class provides the following parameters:

  • Volume level (notificationVolume):

    navigationManager.soundNotificationSettings.notificationVolume = 10 // Value from 0 to 100
    
  • Turning off the sound (mute):

    navigationManager.soundNotificationSettings.mute = true // Sound off
    
  • Categories of sound notifications (enabledSoundCategories). Examples of event categories: road works (ROAD_WORKS), 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:

    navigationManager.soundNotificationSettings.enabledSoundCategories = EnumSet.of(
        SoundCategory.CROSSROAD_CAMERAS,     // Cameras of the intersection control
        SoundCategory.AVERAGE_SPEED_CAMERAS, // Cameras for average speed control
        SoundCategory.ROAD_WORKS,            // Road works
        SoundCategory.TOLLS,                 // Toll roads
        SoundCategory.OBSTACLES              // Obstacles
    )