Skip to main content

Routing

Last updated: June 16, 2026 | 4 minutes read

This example shows how to compute a route between a list of waypoints and read its summary information - without rendering a map. Once the route is calculated, the example extracts its waypoint names and its total distance and travel time, and presents them in a simple list. It is the most basic routing example: a good starting point before moving on to Routing On Map, where the route is drawn on an interactive map.

The example keeps the RoutingService inside MainActivity. In a production MVVM app the service would usually live in the repository layer and be consumed by a ViewModel, but for the scope of this example an activity keeps the flow easy to follow.

Route waypoints and summary

Routing Service

MainActivity retains a single RoutingService instance. Its onStarted and onCompleted callbacks drive the UI: onStarted shows the progress bar, and onCompleted hides it and then dispatches on the error code - displaying the first route's info on success, ignoring a cancellation, and surfacing a dialog for any other error. Because the callbacks may arrive after the activity has gone away, the UI work is routed through runOnAliveUi, which posts to the main thread only while the activity is still alive.

MainActivity.ktView on Github
private val routingService = RoutingService(
onStarted = {
runOnAliveUi {
binding.progressBar.visibility = View.VISIBLE
}
},

onCompleted = { routes, errorCode, _ ->
runOnAliveUi {
binding.progressBar.visibility = View.GONE

when (errorCode) {
GemError.NoError -> routes.firstOrNull()?.let { displayRouteInfo(it) }
GemError.Cancel -> { /* Routing canceled - no action needed. */ }
else -> showDialog(
getString(
R.string.routing_service_error,
SdkCall.runSynced {
GemError.getMessage(errorCode, this)
},
),
)
}
}
},
)

Calculating the route

Routing needs the worldwide road map to be available, so the calculation is started from the SdkSettings.onWorldwideRoadMapSupportStatus listener once the map data reports EOffboardListenerStatus.UpToDate. The listener clears itself after the first successful fire so the route is computed only once.

MainActivity.ktView on Github
SdkSettings.onWorldwideRoadMapSupportStatus = { status ->
if (status == EOffboardListenerStatus.UpToDate) {
SdkSettings.onWorldwideRoadMapSupportStatus = {}
calculateRoute()
}
}

In calculateRoute() the waypoints are defined as a list of Landmark instances. A route needs at least two waypoints - a departure and a destination - but it may contain more if you want the route to pass through additional intermediate points. This example uses three: it departs from Frankfurt, passes through Karlsruhe, and arrives in Munich. Each Landmark holds a name together with its latitude and longitude in degrees.

The waypoint list is then passed to the routingService to calculate the route. calculateRoute returns synchronously an error code indicating whether the calculation could be started. When it starts successfully the result is delivered later through the service's onCompleted callback; but when it fails to start, that callback never fires - so this case is handled here directly by hiding the progress bar and showing an error dialog.

MainActivity.ktView on Github
private fun calculateRoute() = SdkCall.execute {
val wayPoints = arrayListOf(
Landmark("Frankfurt", 50.11428, 8.68133),
Landmark("Karlsruhe", 49.0069, 8.4037),
Landmark("Munich", 48.1351, 11.5820),
)

// calculateRoute returns synchronously whether the calculation could be started. On
// failure onCompleted never fires, so report the error and hide the progress bar here.
val errorCode = routingService.calculateRoute(wayPoints)
if (errorCode != GemError.NoError) {
val message = GemError.getMessage(errorCode, this)
runOnAliveUi {
binding.progressBar.visibility = View.GONE
showDialog(getString(R.string.routing_failed_to_start, message))
}
}
}

Displaying the route information

When the calculation succeeds, displayRouteInfo reads the summary data from the first route. The waypoint names come from route.waypoints, while route.timeDistance provides the total distance and total time. The raw values are formatted into human-readable text with GemUtil.getDistText (respecting the configured unit system) and GemUtil.getTimeText; the travel time also adds any traffic-related delay via GemUtil.getTrafficEventsDelay. The formatted values are then laid out as two sections - the list of waypoints and the route info - built dynamically from the inflated row layouts.

MainActivity.ktView on Github
private fun displayRouteInfo(route: Route) {
SdkCall.execute {
val wayPoints = route.waypoints ?: return@execute
val timeDistance = route.timeDistance ?: return@execute

val distTextPair = GemUtil.getDistText(
timeDistance.totalDistance,
SdkSettings.unitSystem,
bHighResolution = true,
)
val timeTextPair = GemUtil.getTimeText(
timeDistance.totalTime + GemUtil.getTrafficEventsDelay(route, true),
)

val waypointNames = wayPoints.map { it.name ?: "" }
val distText = "${distTextPair.first} ${distTextPair.second}"
val timeText = "${timeTextPair.first} ${timeTextPair.second}"

runOnAliveUi {
binding.routeInfoContainer.removeAllViews()

addSectionHeader(getString(R.string.route_waypoints))
waypointNames.forEachIndexed { index, name ->
addInfoRow(
when (index) {
0 -> R.drawable.departure_waypoint
waypointNames.lastIndex -> R.drawable.destination_waypoint
else -> R.drawable.intermediate_waypoint
},
name,
)
}

addSectionHeader(getString(R.string.route_info))
addInfoRow(R.drawable.distance, distText)
addInfoRow(R.drawable.time_duration, timeText)
}
}
}