Skip to main content
GuidesAPI ReferenceExamples

Search Along Route

|

In this guide you will learn how to compute and render a route on an interactive map, from a departure position to a desired destination.

The app also includes a search button which searches along the selected route for points of interest (POIs) such as fuel stations, and displays the results of the search in a scrollable pop-up panel.

Computed route
Search results along the computed route

Routing

A RoutingService() is instantiated to compute and render the route.

The onStarted and onCompleted routing callbacks are overridden and implemented, to show, and then hide, respectively, the routing computation progress bar, which is useful for very slow devices, where the route computation may take long enough to be noticeable by the user.

When the route calculation is completed, if there is no error, the resulting routes (as there could be more than one alternate route in the resulting set between the specified departure and destination points) are drawn on the map: gemSurfaceView.mapView?.presentRoutes() and a main route is choosen mainRoute = routes[0].

Then the search button is set to visible searchButton.visibility = View.VISIBLE, so that the user can search the landmarks present along the route.

MainActivity.kt
private val routingService = RoutingService(
onStarted = {
progressBar.visibility = View.VISIBLE
},

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

when (errorCode)
{
GemError.NoError ->
{
if(routes.isNotEmpty())
{
routesList = routes
mainRoute = routes[0]
Util.postOnMain {
searchButton.visibility = View.VISIBLE
}
SdkCall.execute {
gemSurfaceView.mapView?.presentRoutes(routes, displayBubble = true)
}
}
}

GemError.Cancel ->
{
// The routing action was cancelled.
}

else ->
{
// There was a problem at computing the routing operation.
showDialog("Routing service error: ${GemError.getMessage(errorCode)}")
}
}
}
)

The function calculateRoute() is used to set the waypoints and to initiate the route calculation.

The starting, or departure point of the route is the first waypoint in a list of 2 or more Landmarks (2 in this case), each containing a name, latitude (in degrees) and longitude (in degrees). The destination point is the last waypoint in the list.

MainActivity.kt
private fun calculateRoute() = SdkCall.execute {
val waypoints = arrayListOf(
Landmark("Folkestone", 51.0814, 1.1695),
Landmark("Paris", 48.8566932, 2.3514616)
)
routingService.calculateRoute(waypoints)
}

A SearchService() is instantiated, which carries out on-demand search along the route.

MainActivity.kt
private val searchService = SearchService(
onStarted = {
progressBar.visibility = View.VISIBLE
},
onCompleted = onCompleted@{ results, errorCode, _ ->
progressBar.visibility = View.GONE
when (errorCode) {
GemError.NoError -> {
// Display results in AlertDialog
onSearchCompleted(results)
}
GemError.Cancel -> {
// The search action was canceled.
}
else -> {
// There was a problem in the search operation.
Toast.makeText(
this@MainActivity,
"Search service error: ${GemError.getMessage(errorCode)}",
Toast.LENGTH_SHORT
).show()
}
}
}
)

When the user clicks the search button, the searchAlongRoute() function is called and set to return up to 25 gas stations (defined as EGenericCategoriesIDs.GasStation) along the route.

MainActivity.kt
private fun searchAlongRoute(route: Route) = SdkCall.execute {
// Set the maximum number of results to 25.
searchService.preferences.maxMatches = 25
// Search Gas Stations along the route.
searchService.searchAlongRoute(route, EGenericCategoriesIDs.GasStation)
}

The onCompleted listener in the searchService calls the onSearchCompleted() function when the user-triggered search is finished. Then the results are displayed in a scrollable list inside a pop-up panel.

MainActivity.kt
private fun onSearchCompleted(results: ArrayList<Landmark>) {
val builder = AlertDialog.Builder(this)

val convertView = layoutInflater.inflate(R.layout.dialog_list, null)
convertView.findViewById<RecyclerView>(R.id.list_view).apply {
layoutManager = LinearLayoutManager(this@MainActivity)

addItemDecoration(DividerItemDecoration(
applicationContext,
(layoutManager as LinearLayoutManager).orientation
))

setBackgroundResource(R.color.background_color)

val lateralPadding = resources.getDimension(R.dimen.big_padding).toInt()
setPadding(lateralPadding, 0, lateralPadding, 0)

adapter = CustomAdapter(results)
}

builder.setView(convertView)

builder.create().show()
}

Map Implementation

When the map data is ready using the onMapDataReady@{} callback it calculates the route using the function calculateRoute() and then attaches a touch event to the Map View as seen in gemSurfaceView.mapView?.onTouch = {}.

This touch event which will set, gemSurfaceView.mapView?.apply {...}, the main route to be highlighted and centered on.

Searching along the route is triggered at the user’s discretion using the searchButton.

MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

progressBar = findViewById(R.id.progressBar)
gemSurfaceView = findViewById(R.id.gem_surface)
searchButton = findViewById(R.id.search_button)

SdkSettings.onMapDataReady = onMapDataReady@{ isReady ->
if (!isReady) return@onMapDataReady

// Defines an action that should be done when the world map is ready (Updated/ loaded).
calculateRoute()

// onTouch event callback
gemSurfaceView.mapView?.onTouch = { xy ->
// xy are the coordinates of the touch event
SdkCall.execute {
// tell the map view where the touch event happened
gemSurfaceView.mapView?.cursorScreenPosition = xy

// get the visible routes at the touch event point
val routes = gemSurfaceView.mapView?.cursorSelectionRoutes
// check if there is any route
if (!routes.isNullOrEmpty())
{
// set the touched route as the main route and center on it
mainRoute = routes[0]
gemSurfaceView.mapView?.apply {
preferences?.routes?.mainRoute = mainRoute
centerOnRoutes(routesList)
}
}
}
}

searchButton.setOnClickListener {
searchAlongRoute(mainRoute)
}
}
}