Skip to content

Routing

This section explains the use of route computing functionality including multiple waypoints and alternate routes within an Android app.

Car, Truck and Pedestrian Routing

The Sygic Maps SDK for Android supports route calculation with multiple waypoints, optimized for walking or driving. A route describes a path between at least two waypoints, the starting point and the destination, with optional intermediate waypoints in between. Most of the managers need to be called asynchronously.

Applications can provide route information to users in two ways:

  • A line rendered on a map that displays a connecting path between all waypoints
  • Turn-by-turn directions in text format

RouteRequest is a class containing all info about the route we want to calculate. It contains start, finish, via-points and another route options.

val startGeoCoordinates = GeoCoordinates(48.13308, 11.57249) // somewhere in Munich
val endGeoCoordinates = GeoCoordinates(47.54849, 9.68634) // the beautiful city of Lindau
val routeRequest = RouteRequest().apply {
    setStart(startGeoCoordinates)
    setDestination(endGeoCoordinates)
}

To adjust routing options, such as transport mode, dimensional restrictions, avoids and much more, create a RoutingOptions instance that you need to pass to the RouteRequest afterwards:

val routingOptions = RoutingOptions().apply {
    isUnpavedRoadAvoided = true
    routingService = RoutingOptions.RoutingService.Online
    transportMode = RoutingOptions.TransportMode.Car
    tunnelRestriction = RoutingOptions.ADRTunnelType.B
    addDimensionalRestriction(RoutingOptions.VehicleRestrictions.Height, 4500) // in mm, weight in kg  
}

routeRequest.routingOptions = routingOptions

Computing the route is done with computeRouteWithAlternatives method of an instance of the Router class. It takes PrimaryRouteRequest, list of AlternativeRouteRequest objects and a RouteComputeFinishedListener which has only one method onComputeFinished containing all calculated routes (or null in case of error).

To obtain more detailed data about calculated routes, each request (primary or alternative) has its own RouteComputeLister. Creating PrimaryRouteRequest may look like this:

val primaryRouteComputeListener = object : RouteComputeListener {
    override fun onProgress(progress: Int) {
        // progress from 0 to 100
    }

    override fun onComputeFinished(route: Route?, status: Router.RouteComputeStatus) {
        // calculated primary route or null in case of error
        // status/result of route calculation - Success, UnreachableTarget, etc.
        // we can add the primary route to the map using MapView
        route?.let {
            mapView.mapDataModel.addMapObject(
                MapRoute.from(it).setType(MapRoute.RouteType.Primary).build()
            )
        }
    }
}
// wrap listener togegher with routeRequest
val primaryRouteRequest = PrimaryRouteRequest(routeRequest, primaryRouteComputeListener)

Alternative route request is created in a similar way, but instead of routeRequest we will specify a route alternative type. For example, we would like to calculate two alternative routes - fastest and shortest:

val alternativeRouteComputeListener1 = object : RouteComputeListener {
    override fun onProgress(progress: Int) {
        // progress from 0 to 100
    }

    override fun onComputeFinished(route: Route?, status: Router.RouteComputeStatus) {
        // calculated alternative route or null in case of error
        // status/result of route calculation - Success, UnreachableTarget, etc.
        // add the alternative route(s) to the map using MapView
        route?.let {
            mapView.mapDataModel.addMapObject(
                MapRoute.from(it).setType(MapRoute.RouteType.Alternative).build()
            )
        }
    }
}
val alternativeRouteRequest1 = AlternativeRouteRequest(
    AlternativeRouteRequest.RouteAlternativeType.Fastest,
    alternativeRouteComputeListener1
)

val alternativeRouteComputeListener2 = object : RouteComputeListener {
    override fun onProgress(progress: Int) {
        // progress from 0 to 100
    }

    override fun onComputeFinished(route: Route?, status: Router.RouteComputeStatus) {
        // calculated alternative route or null in case of error
        // status/result of route calculation - Success, UnreachableTarget, etc.
        // add the alternative route(s) to the map using MapView
        route?.let {
            mapView.mapDataModel.addMapObject(
                MapRoute.from(it).setType(MapRoute.RouteType.Alternative).build()
            )
        }
    }
}
val alternativeRouteRequest2 = AlternativeRouteRequest(
    AlternativeRouteRequest.RouteAlternativeType.Shortest,
    alternativeRouteComputeListener1
)

Finally, we can start the compute process:

val routeComputeFinishedListener = object : RouteComputeFinishedListener {
    override fun onComputeFinished(route: Route?, alternatives: List<AlternativeRouteResult>) {
        // all route calculations are done here
    }
}

router.computeRouteWithAlternatives(
    primaryRouteRequest,
    listOf(alternativeRouteRequest1, alternativeRouteRequest2),
    routeComputeFinishedListener
)

Please note that the onProgress callback gets continuous progress only in offline mode as the online routing progress cannot be tracked (you only get 0 and then 100).

There are many possibilities how you can present calculated routes to the user using MapView class, for example:

// show the whole route to the user using its bounding box
mapView.cameraModel.setMapRectangle(
    MapRectangle(
        route.boundingBox,
        0.15f,
        0.15f,
        0.15f,
        0.15f
    ),
    MapAnimation(
        500,
        MapAnimation.InterpolationCurve.AccelerateDecelerate
    )
)
// add a SmartLabel showing the primary route's duration
mapView.mapDataModel.addMapObject(
    MapSmartLabel
        .with(route)
        .setText(
            (StyledText(
                // formatDate() is a function that transforms an integer into a stringified format
                formatDate(route.routeInfo.durationWithSpeedProfileAndTraffic)
            ))
        )
        .build()
)
// add a MapMarker at the start
mapView.mapDataModel.addMapObject(
    MapMarker.at(route.start.navigablePosition).withIcon(R.drawable.sygic_sdk_map_pin).build()
)
// add a MapMarker at the end
mapView.mapDataModel.addMapObject(
    MapMarker.at(route.destination.navigablePosition).withIcon(R.drawable.sygic_sdk_map_pin).build()
)

// and if you want to use one of the routes for navigation do not forget to set it into the NavigationManager
NavigationManagerProvider.getInstance().get().setRouteForNavigation(route)

Handling rerouting can be done using the NavigationManager's onRouteChanged listener. Setting the new route for navigation is handled internally, all you need to do is check if the new route is not null, remove the old MapRoute from MapView via mapDataModel and add the new one. Of course, if you have stored the RouteRequest or the RouteInfo, you need to assign the new ones to your variables and also you can update the traffic data for route etc.

The MapRoute map object

The MapRoute class is a type of MapObject that displays a calculated route on a map. Typically, an application creates a MapRoute after a route calculation, passing the relevant Route object as a parameter to the MapRoute(Route) constructor before adding the MapRoute to the map by calling MapView.mapDataModel.addMapObject(MapRoute).

To remove the route from the map, just call the removeMapObject() function.

route?.let {
    val alternativeRouteMapObject = MapRoute.from(it).setType(MapRoute.RouteType.Alternative).build()
    mapView.mapDataModel.addMapObject(alternativeRouteMapObject)
    mapView.mapDataModel.removeMapObject(alternativeRouteMapObject)
} 

You can also process the Route without using it in map, e.g. to get textual instructions:

val instructionList = StringBuilder()
route.maneuvers.forEach {
    when (it.type) {
        RouteManeuver.Type.Left -> {
            instructionList.append("Turn left onto " + it.nextRoadName + "\n")
        }
        RouteManeuver.Type.RoundaboutN -> {
            instructionList.append("On the roundabout, continue straight using the exit #" +
            it.roundaboutExit + "\n")
        }
        // more Route maneuver types
    }

}

It is also possible to request the Route's geometry should you need to work with it. The method getRouteGeometry() returns a List of GeoCoordinates.

route.routeGeometry?.forEach {
    print(it) // prints all of the GeoCoordinates of the route
}

Route Serialization

If you wish to save a route you have computed, you can use Route.serializeToBriefJSON()

val json = route.serializeToBriefJSON()
// use the serialized route - save it to a file, send it over network, sky is the limit...

To deserialize the JSON and get the Route you can use Router.computeRouteFromJSONString().

router.computeRouteFromJSONString(json, object : RouteComputeListener {
    override fun onProgress(progress: Int) {
    }

    override fun onComputeFinished(route: Route?, status: Router.RouteComputeStatus) {
        // use the route
    }
})

You can also extract the RouteRequest first, e.g. if you want to edit it before computing:

router.createRouteRequestFromJSONString(json, object: OnRouteRequestDeserialized {
    override fun onError(error: RouteRequestDeserializerError) {
        // invalid json
    }

    override fun onSuccess(routeRequest: RouteRequest) {
        // e.g. modify route request and use it for another route calculation
        routeRequest.addViaPoint(GeoCoordinates(51.515436, -0.122045))
        router.computeRouteWithAlternatives(...)
    }
})

Routing Options

You can edit parameters of route before computing using RoutingOptions.

val options = RoutingOptions().apply {
    transportMode = RoutingOptions.TransportMode.Car
    routingType = RoutingOptions.RoutingType.Economic
    departureTime = Date(1476498000000) // 15 Oct 2016, 4:20am
}

routeRequest.routingOptions = options

By default, the route is computed at the instant. You can also set a departure time using options.departureTime, but only set it when you want to override it as it computes the route without traffic and you wouldn't want that for a normal compute.

Avoids

To avoid certain elements on route like toll roads or unpaved roads, you can set flags on RoutingOptions.

val routingOptions = RoutingOptions().apply { // this creates an object with default values
    isTollRoadAvoided = true
    isUnpavedRoadAvoided = true
}

val routeRequest = RouteRequest().apply {
    routingOptions = routingOptions
}
RouterProvider.getInstance().get().computeRouteWithAlternatives(...)

After the route is computed, you can get RoutingOptions from Route in order to further edit the route. You can get countries on route and set avoids individually for each or you can avoid a whole country altogether as shown below.

val router = RouterProvider.getInstance().get()
val routingOptions = route.routingOptions
val avoidableCountries = setOf(routingOptions.avoidableCountries)

avoidableCountries.forEach {
    if (it.equals("fr")) {
        routingOptions.setTollRoadAvoided("fr", true)
    }
    if (it.equals("ch")) {
        routingOptions.setCountryAvoided("ch", true)
    }
}
// then assign the new routing options and compute the route again
routeRequest.routingOptions = routingOptions
router.computeRouteWithAlternatives(...)

It is possible that you would like to see what can be avoided on the route that was computed. Imagine that the user computes a route from Bratislava, Slovakia to Saint-Malo, France. The route goes via Austria and Germany. After the route is computed, you can get the countries that can be avoided. In this case, Austria and Germany can be avoided.
Then, if you like, it's possible to avoid only certain types of roads in each country. For example, the route is computed via Toll roads and Motorways in each country (except for Germany, as there are no Toll roads). For example, the user should be able to avoid Toll roads in France, only. Therefore, you can let the user decide for each country just like this:

val routingOptions = route.routingOptions
val transitCountries = route.transitCountries
transitCountries.forEach {
    // ask for each country if the options are avoidable
    if (routingOptions.isHighwayAvoidable(it)) {
        // if highway is avoidable in the country, show it to the user
        // and then set isHighwayAvoided to true if the user chooses to avoid it
        routingOptions.isHighwayAvoided = true
    }
    if (routingOptions.isTollRoadAvoidable(it)) {
        // same here
        routingOptions.isTollRoadAvoided = true
    }
    // and for other avoids
}
// remember to recompute the route with the new options!
routeRequest.routingOptions = routingOptions
router.computeRouteWithAlternatives(...)

It is also possible to avoid a chosen GeoBoundingBox. A GeoBoundingBox is a bounding box that consists of the top left and bottom right GeoCoordinates. Let's see an example of how to avoid two separate bounding boxes:

val geoRectangleAvoids = listOf(
    GeoBoundingBox(
        GeoCoordinates(48.814903, 18.599957),
        GeoCoordinates(48.413607, 19.022931)
    ),
    GeoBoundingBox(
        GeoCoordinates(48.426366, 18.218182),
        GeoCoordinates(48.309587, 18.696088)
    )
)

val routingOptions = RoutingOptions()
for (geoRectangleAvoid in geoRectangleAvoids) {
    routingOptions.addGeoRectangleAvoid(geoRectangleAvoid)
}

After setting the geographical rectangle avoids (geoRectangleAvoids) and computing the route, the avoids will be stored in the RoutingOptions of the computed route so that you will be able to work with them.

Route Information

We will work with a route: Route that we have computed in the previous chapters.

Some of the basic info can be received directly from the Route object:

val routingOptions = route.routingOptions
val boundingBox = route.boundingBox
val routeInfo = route.routeInfo
val routeManeuvers = route.maneuvers

The routingOptions can be used to show the routing options that have been used to compute the route. The boundingBox of the route can be passed into the setMapRectangle function to see just the whole route on the map:

mapView.cameraModel.setMapRectangle(
  MapRectangle(
    route.boundingBox,
    0.15f,
    0.15f,
    0.15f,
    0.15f
  ), MapAnimation(500, MapAnimation.InterpolationCurve.AccelerateDecelerate)
)

routeManeuvers can be used to show the all of the directions on the route beforehand. The routeInfo object holds information about the length of the route and three different durations - one includes speed profile information and traffic delays, the other includes only speed profile information and the last one doesn't use any of the additional information.

Route Warnings

If your route is starting on a toll road and you avoided toll roads, it is impossible to avoid it and this unavoidable part will be included in the route warnings. Please note that the route warnings are casted.

Or, as in this example, we check if the route warning is of type WeightRestriction and then we can work with it.

route.getRouteWarnings(object: RouteWarningsListener {
    override fun onRouteWarnings(routeWarnings: List<RouteWarning>) {
        routeWarnings.forEach { routeWarning ->
            if (routeWarning is RouteWarning.SectionWarning.WeightRestriction){
                routeWarning.limitValue // the value on the road in the map
                routeWarning.realValue // the value of your vehicle (routing options)
                routeWarning.section.geoCoordinates // list of geocoordinates of the violated section
            }
        }
    }
})

Route Explorer

After computing a route, you can get the traffic information, places along the route and more using the RouteExplorer.

Again, we will work with a pre-computed route route. Let's see how to explore the traffic on route and its most important members:

TrafficManagerProvider.getInstance().get().enableTrafficService() // the traffic service has to be enabled!
RouteExplorer.exploreTrafficOnRoute(route, object: RouteExplorer.OnExploreTrafficOnRouteListener{
  override fun onExploreTrafficLoaded(info: TrafficNotification) {
    info.delayOnRoute // total delay of all traffic events
    info.trafficLevel // from Low to High
    info.trafficInfoList.forEach { // as there can be more traffic events
      it.affectedArea // bounding box of the traffic
      it.affectedLength // length of the traffic event
      it.delay // delay in seconds
      it.distance // distance from the last callback position
      it.severity // Normal to Blocking
    }
  }

  override fun onExploreTrafficError(error: TrafficManager.ErrorCode) {
    // handle the error
  }
})

Next, let's see how to explore the Places on route. Please note that this time, you also receive progress of the exploring and the operation is fully completed when it reaches the value 100.

RouteExplorer.explorePlacesOnRoute(route, listOf(PlaceCategories.PetrolStation), object: RouteExplorer.OnExplorePlacesOnRouteListener{
  override fun onExplorePlacesLoaded(
    info: List<PlaceInfo>,
    progress: Int
  ) {
    if (progress == 100){
      info.forEach {
        print(it.distance) // distance to the place in meters
        print(it.placeInfo.name) // name of the place
        print(it.placeInfo.category) // category of the place
      }
    }
  }
  override fun onExplorePlacesError(error: PlacesManager.ErrorCode) {
    // handle the error
  }
})

You can also explore various types of incidents on the route. Here we want to get only the instances of static radars on our route, if there are any :

RouteExplorer.exploreIncidentsOnRoute(route, listOf(IncidentType.RadarStaticSpeed), object: RouteExplorer.OnExploreIncidentsOnRouteListener{
  override fun onExploreIncidentsLoaded(
    incidents: List<IncidentInfo>,
    progress: Int
  ) {
    if (progress == 100){
      incidents.forEach{
        print(it.distance) // distance to the incident in meters
        print(it.recommendedSpeed) // recommended speed if there's any
        it.incidentLink.location // GeoCoordinates of the incident
      }
    }
  }
  override fun onExploreIncidentsError(error: IncidentsManager.ErrorCode) {
    // handle the error
  }
})

Last but not least - if the maps that you use contain EV charging stations, you can explore those on your route.

This is most convenient when using the routing for electric vehicles. The function is similar to those above:

RouteExplorer.exploreChargingStationsOnRoute(route, evProfile, object: RouteExplorer.OnExploreChargingStationsOnRouteListener{
  override fun onExploreChargingStationsLoaded(
    info: List<ChargingStation>,
    progress: Int
  ) {
    if (progress == 100) {
      info.forEach {
        print(it.distance) // distance to the ChargingStation in meters
        print(it.timeToFullCharge) // time in minutes needed to reach charging station and charge the battery
        print(it.link.name) // name of the ChargingStation
      }
    }
  }
  override fun onExploreChargingStationsError(error: PlacesManager.ErrorCode) {
    // handle the error
  }
})

Next Durations

If you would like to show the "ideal" time to leave, you can do that using the computeNextDurations() function.

It does not directly compute the Time to Leave, but you can achieve that on your own. Our engine can compute the next durations of the route if a list of times is passed. For example, let's see how to compute the next 30 durations with a span of 15 minutes. As always, we will work with an already computed route, route. If you do not know how to compute a route, please refer to the top of this page.

val times = mutableListOf<Long>()
for (x in 0..30) {
  // the time that we pass has to be in seconds, therefore we divide by 1000, and 900 stands for 15 minutes
  times.add(System.currentTimeMillis() / 1000 + x * 900)
}

router.computeNextDurations(route, times, object: Router.RouteDurationListener{
  override fun onRouteDurations(p0: Route?, p1: MutableList<Int>?) {
    p1?.let {
      for (item in p1){
        // here you will get the durations as integers
      }
    }
  }
}

Routing for electric vehicles

As Electromobility is one of the strongest trends in the automotive industry and with the increasing number of electric vehicles, we recognize the necessity of introducing a routing engine for such vehicles. You can find the Automatic EV routing in the newest versions of SygicMapsSDK.

Our EV routing works in the offline mode only, yet. The route is computed according to the EV Profile that you set - if you wouldn't reach the destination with your current charge or capacity, the computing algorithm will try to find places where you can charge your vehicle so that you reach your destination without your vehicle running out of power. The time necessary to charge your vehicle is added to the final ETA and you can check the delay of each ChargingWaypoint.

You can (actually, you need to) define the Battery profile and a complete EV profile of your vehicle along with your preferences. You can choose from a wide range of connector types, according to your needs - e.g. CCS½, CHAdeMo, NEMA, Tesla etc.

Let's see what is required in order to compute an EV route. At first, we specify new EVPreferences:

val evPreferences = EVPreferences(
    chargeRangeLowVal = -1.0, // Lower margin of preferred EV station power output (-1 = unlimited)
    chargeRangeUpperVal = -1.0, // Upper margin of preferred EV station power output (-1 = unlimited)
    preferredProvider = listOf(
        EVProvider("Provider 1"),
        EVProvider("Provider 2", true)
    ), // List of preferred EV station providers
    enforceDCChargers = false, // Use DC stations when possible
    enforceNonstopChargers = false, // Use chargers without opening hours when possible
    chargerPermission = EVPreferences.EVChargerAccessType.Any, // Controls accessibility of preferred chargers
    payType = EVPreferences.EVPayType.Any // Indicates which payment method is preferred for EV station services
)

Then, we can configure the battery profile:

 val batteryProfile = BatteryProfile(
    batteryCapacity = 70f, // Battery capacity in kWh
    remainingCapacity = 70f, // Remaining battery capacity in kWh
    batteryChargingThreshold = .2f, // Percent of battery capacity when the next charging station should be reached
    batteryFullChargeThreshold = .8f, // Percent of capacity when battery is considered fully charged
    batteryMinimumReserveThreshold = .05f, // Percent of capacity as safety (will be never used)
    chargingCurve = mapOf(5.0 to 10.0, 15.0 to 30.0) // Battery capacity kilowatt hours (kWh) to charging power in kilowatts (kW) curve
)

Next, we need to set up an EVProfile in which we will use the information above and specify some new values.

val evProfile = EVProfile(
    batteryProfile = batteryProfile,
    chargingMaxPower = 100, // Max charging power in kW
    connector = setOf(EVConnector.ConnectorType.Ccs2),
    power = setOf(EVConnector.PowerType.DC),
    consumptionCurve = mapOf(0.0 to 0.01, 50.0 to 0.25), // Velocity kilometers per hour (km/h) to consumption kilowatt hour per kilometer (kWh/km) curve
    batteryMinimumDestinationThreshold = 0.05 // percent of capacity desired at the destination
)

Naturally, you can add more connector types into the connector set. You can also add the EVConnector.PowerType.AC to the power set.

Next, we need to configure the Router and RoutingOptions just like we would do in a normal compute and then we will call the computeRouteWithAlternatives() method that is available via the Router.

Info

The RouteComputeStatus listener may return a status called SuccessWithWarnings that computes the route to the destination, but with either of two warnings: InsufficientBatteryCharge (to reach any following station) or LowBatteryAtDestination.

val options = RoutingOptions().apply {
    routingService = RoutingOptions.RoutingService.Offline // as EV routing is available for Offline mode
    transportMode = RoutingOptions.TransportMode.Car
}
val routeRequest = RouteRequest(evProfile, evPreferences).apply {
    setStart(start) // GeoCoordinates
    setDestination(destination) // GeoCoordinates
    routingOptions = options
}
val primaryRouteRequest = PrimaryRouteRequest(
  routeRequest, // route request created above 
  object : RouteComputeListener {
      override fun onComputeFinished(route: Route?, status: Router.RouteComputeStatus) {
          if (status == Router.RouteComputeStatus.SuccessWithWarnings) {
              route?.getRouteWarnings(object: RouteWarningsListener {
                  override fun onRouteWarnings(routeWarnings: List<RouteWarning>) {
                      routeWarnings.forEach {
                          print(it.geoCoordinates) // where the warning occured
                          print(it.warningType) // what is the type of the warning
                      }
                  }
              })
          }
      }
      override fun onProgress(progress: Int) {
      }
  })

RouterProvider.getInstance().get().computeRouteWithAlternatives(
  primaryRouteRequest,
  listener = object : RouteComputeFinishedListener {
      override fun onComputeFinished(
          route: Route?,
          alternatives: List<AlternativeRouteResult>
      ) {
          // your code
      }
  })

Info

Please remember that the route that you obtain may contain ChargingWaypoint(s)! These are the waypoints that are necessary to visit in order to charge the vehicle. You will also receive alternative charging stations for each ChargingWaypoint.

One of the main purposes of our SDK is to be able to navigate to a point that the user selects from the map. Let's see the idea on how it's done (please note that this is a really simple example, you wouldn't normally want to call the routing after tapping into the map):

// once you got the mapView from the onMapReady()
var destination : GeoCoordinates
val currentPosition = PositionManagerProvider.getInstance().get().lastKnownPosition

mapView.addMapGestureListener(object: MapGestureAdapter() {
  override fun onMapClicked(
    e: MotionEvent?,
    isTwoFingers: Boolean
  ): Boolean {
    if (isTwoFingers){
      return false
    }
    e?.let { // request the objects at the point of the MotionEvent's x,y coordinates
      mapView.requestObjectsAtPoint(e.x, e.y, object: RequestObjectCallback{
        override fun onRequestResult(
          p0: MutableList<ViewObject<ViewObjectData>>,
          p1: Float,
          p2: Float,
          p3: Int
        ) {
          destination = p0[0].position // here we work only with the position of the first result, but you can get more of them
          routeRequest.start = currentPosition // you can get your current position via the PositionManager
          routeRequest.destination = destination
          router.computeRoute(routeRequest, ...) // compute the route just like in the examples above
        }
      })
    }
    return super.onMapClicked(e, isTwoFingers)
  }
})

Computing a route using a polyline

It is possible to create a route and then use it for navigation if you know its polyline. It is necessary to supply a GuidedRouteProfile into the RouteRequest as an argument and then you can compute the route just like in the examples above. The engine will match the polyline on the map and the polyline can then be used as a navigable route.

val polyline = mutableListOf<GeoCoordinates>()
polyline.add(GeoCoordinates(41.85588,-87.64821))
polyline.add(GeoCoordinates(41.85601,-87.64822))
polyline.add(GeoCoordinates(41.85619,-87.64827))
polyline.add(GeoCoordinates(41.85658,-87.64823))
polyline.add(GeoCoordinates(41.85672,-87.64820))
polyline.add(GeoCoordinates(41.85671,-87.64749))
polyline.add(GeoCoordinates(41.85672,-87.64704))
polyline.add(GeoCoordinates(41.85670,-87.64634))
val guidedRouteProfile = GuidedRouteProfile(polyline)

val routeRequest = RouteRequest(guidedRouteProfile)
// compute the route using the Router object 

Getting transit countries

Info

Available in SDK21.2.0

There are some cases in which you might need to retrieve the countries through which the computed route is passing. If the country has more regions, regions will be returned, too.

route?.getTransitCountriesInfo(object: TransitCountriesInfoListener {
    override fun onTransitCountriesInfo(transitCountries: List<TransitCountryInfo>) {
        transitCountries.forEach {
            it.country // String
            it.regions // List
        }
    }
})

Turn preference setting

It is now possible to choose/let the user choose whether it is preferred to use right turns (left turns in the UK, Australia) or turns that cross the line of opposite direction. This can be easily set in the RoutingOptions object. There are two possibilities:

Adjacent road turn: Prefer turns to adjacent roads without crossing the line of opposite driving direction. For example, in countries with driving on the right side of the road this means to prefer right turns.

Crossover line turn: Prefer turns with crossing the line of opposite driving direction. For example, in countries with driving on the right side of the road this means to prefer left turns.

Default is AdjacentRoadTurn.

val routingOptions = RoutingOptions()
routingOptions.turnPreference = TurnPreference.CrossoverLineTurn