Build Different

Advanced Maps and Routing for Truck Drivers in Android Apps

Developer Portal
Oct 28, 2020 • Last edit on Sep 20, 202211 min read
Mobile device showing 3-D map with POI labels and a truck on a route

In a previous article, we showed how to use the TomTom Android SDK to build an application for truck drivers. Specifically, we set up an application and used the Maps SDK for Android to determine the best route to the destination and the time to leave to arrive on time.

[]There’s so much more that you can do with the Maps SDK and TomTom location services to build compelling apps for fleet management, logistics, and mobility. In this article, we will extend the previous project by adding waypoints, custom section styles for different road types, and points of interest (POI).

Waypoints have many uses. For example, more than one destination in a route is common for deliveries. Marking different road types is useful for marking toll roads, secondary roads, or possible hazards such as tunnels. POIs can be helpful when a driver routes with waypoints, so they can view key areas such as fuel stops, rest stops, weigh stations, and so on.

[]Before continuing, you need a TomTom Developer account, Java and Android Studio, and the source code from theprevious project. The previous article has instructions on how to request your free TomTom developer key and add it to the project. Once you have the previous project working, you can continue here.

Adding Waypoints

[]The first change that we will make to the project is to add a way to accumulate waypoints. The previous version of this application only required that a user enter a destination. Their starting point came from the phone’s location service.

[]When a location is entered in this version of the application, instead of immediately calculating a route, the location is added to a list of destinations. To hold information on a destination, I made a new class.

1public class Destination {
3 String label;
4 LatLng location;
7 public Destination(LatLng location, String label) {
8 this.label = label;
9 this.location = location;
10 }
12 public Destination(LatLng location) {
13 this.label = "";
14 this.location = location;
15 }
17 public LatLng getLocation() { return this.location; }
18 public String getLabel() { return this.label; }

The application’s main activity needs a few new fields. Two of the fields are for holding and displaying the selected destinations. One field is for holding the routing result. The last new field contains style information. The style information will be used to change how a road is rendered. This can be used to convey specific information about the road.

1Vector<Destination> waypointList = new Vector<Destination>();
3ArrayAdapter<Destination> waypointListAdapter;
5FullRoute fullRoute;
7Vector<RouteStyle> routeStyleList = new Vector<RouteStyle>();

We also need to change the user interface for the application. We are keeping the text box for fuzzy searches and the map. I’m adding a ListView for displaying the address list and a button for adding addresses to the list.

[]The layout for the main activity now contains the following elements. I’m showing the layout without the constraints for simplicity.

1<?xml version="1.0" encoding="utf-8"?>
3<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=""
5 xmlns:app=""
6 xmlns:tools=""
7 android:layout_width="match_parent"
8 android:layout_height="match_parent"
9 tools:context=".MainActivity">
11 <AutoCompleteTextView
12 android:id="@+id/txtAddress"
13 android:hint="@string/address_hint"
14 android:visibility="visible"
15 />
17 <TextView
18 android:id="@+id/lblAddress"
19 app:layout_constraintBottom_toBottomOf="@+id/txtAddress"
20 />
22 <Button
23 android:id="@+id/btnAddStop"
24 />
26 <Button
28 android:id="@+id/btnFuelSearch"
29 android:onClick="onPoiSearchClick"
30 android:text="@string/action_search_poi_fuel" />
32 <Button
33 android:id="@+id/btnCalculateRoute"
34 android:onClick="onCalculateRouteButtonClicked"
35 android:text="@string/action_calculate_route" />
37 <ListView
38 android:id="@+id/lvWaypointList"
39 android:background="@drawable/border_ui"></ListView>
40 <fragment
41 android:id="@+id/mapFragment"
42 android:name=""
43 />
45 <Button
46 android:id="@+id/btnClear"
47 android:onClick="onClearButtonClicked"
48 android:text="@string/action_clear" />
50 <TextView
51 android:id="@+id/txtArrivalTimeLabel"
52 android:text="@string/arrival_time" />
54 <EditText
55 android:id="@+id/txtArrivalTime"
56 android:hint="@string/time_hint" />

After the changes we’ve made, you can see the updated user interface below:


As the user enters an address, the application performs a fuzzy search to find potential matches. After selecting a match, the coordinates of the address are stored in a variable named destination. When the user clicks on the button to add the destination as a stop, the location is added to the waypoint list and the ListView is updated.

1public void onAddDestinationButtonClicked(View view) {
3 Destination wp = new Destination(destination, txtAddress.getText().toString());
4 waypointList.add(wp);
5 txtAddress.setText("");
6 waypointListAdapter.notifyDataSetChanged();

After several items are added to the list, the user can ask for the route. The click handler for the routing button prepares the data for the request and passes it to the requestRoute function where most of the work for the request is done. The phone's current location is used for the starting point of the routing request and the last location is used as the destination. The coordinates for the stops between the two are placed in another list.

1public void onCalculateRouteButtonClicked(View view) {
2 if(waypointList.size() == 0)
3 return;
5 Vector<LatLng> waypoints = new Vector<LatLng>();
6 Destination lastWaypoint = waypointList.get(waypointList.size()-1);
7 destination = lastWaypoint.getLocation();
8 for(int i=0;i<waypointList.size()-1;++i) {
9 Destination wp = waypointList.get(i);
10 waypoints.add(waypointList.get(i).getLocation());
11 }
13 LatLng destination = waypointList.get(waypointList.size()-1).getLocation();
15 requestRoute(
16 currentLocation,
17 destination,
18 travelMode,
19 waypoints
20 );
22 hideKeyboard(view);
23 tomtomMap.zoomToAllMarkers();

The function requestRoute was part of the previous version of this program. Here, it’s been updated to accept an additional parameter for the waypoints. These waypoints are added to the routing query.

1private void requestRoute(final LatLng departure, final LatLng destination, TravelMode byWhat, List<LatLng> waypoints) {
3 RouteQuery routeQuery = new RouteQueryBuilder(departure, destination)
4 .withRouteType(RouteType.FASTEST)
5 .withConsiderTraffic(true)
6 .withTravelMode(byWhat)
7 .withWayPointsList(waypoints)
8 .build();


Creating Styles

[]The route displayed by the application now passes through all of the waypoints. But shown as a continuous path, there is no way to distinguish one part of the trip from another. This can be remedied by applying styles to paths.

[]Previously, the entire path was displayed by passing the coordinates that make up the trip to a route builder. Instead of passing all of the coordinates at once, they can be passed in groups with a style applied to each group.

Let’s create a few styles. In the onCreate method we’ll make a few styles that differ in color. Each leg of the trip will be rendered with one of these colors. Color information for a style is encoded as an ARGB 32-bit integer. The first byte is the alpha or transparency value. The next three bytes are for the amount of red, green, and blue in the color.

[]Styles are built using the RouteStyleBuilder. The line properties of interest for these styles are the width, fillColor, and outlineColor. These properties are set using methods on the builder with the prefix with.

To build my styles, I first made a list of the color codes that will go to each style. Then I iterate through the codes and create the style for each one.

1int[] colorList = new int[]{
3 0x8000FFFF, //teal
4 0x8000FF00, //green
5 0x800000FF, //blue
6 0x80FF00FF //purple
9for(int i=0;i<colorList.length;++i) {
11 RouteStyle rs = RouteStyleBuilder.create()
12 .withWidth(1.0)
13 .withFillColor(colorList[i])
14 .withOutlineColor(0x80000000)
15 .build();
17 routeStyleList.add(rs);

In addition to styles for the legs of a trip, I have also made a style for labeling slow traffic conditions. This style uses a red dashed line.

1DashDescriptor dashDescription = new DashDescriptor(2.0, 2);
3Vector<DashDescriptor> dashList = new Vector<DashDescriptor>();
7badTrafficStyle = RouteStyleBuilder.create()
8 .withWidth(0.8)
9 .withDashList(dashList)
10 .withFillColor(0xFFFF0000)
12 .withOutlineColor(0x00000000)
14 .build();

To use these styles, the method displayRouteOnMap must be modified. The original version receives a list of the coordinates.

1private void displayRouteOnMap(List<LatLng> coordinates) {
2 RouteBuilder routeBuilder = new RouteBuilder(coordinates)
3 .isActive(true);
4 tomtomMap.clear();
5 tomtomMap.addRoute(routeBuilder);
6 tomtomMap.displayRoutesOverview();

The updated version requires the FullRoute value. FullRoute contains the coordinates organized into groups.

[]The FullRoute.getLegs method returns the coordinates group for each leg of a trip. The first leg is from the user’s starting point to the first waypoint. The second leg is from the first waypoint to the second waypoint, and so on.

[]To add each leg with a different color, iterate through the legs and add the coordinates of each leg using a different style. In addition to using color to mark the paths, each route will also display a flag. The first leg will have a green start flag. Each waypoint will be marked with a yellow flag. The final destination will have a checkered flag.

1private void displayRouteOnMap(FullRoute route) {
2 Icon endIcon = Icon.Factory.fromResources(getApplicationContext(), R.drawable.ic_checkeredflag);
4 Icon waypointIcon = Icon.Factory.fromResources(getApplicationContext(), R.drawable.ic_yellowflag);
6 Icon startIcon = Icon.Factory.fromResources(getApplicationContext(), R.drawable.ic_greenflag);
8 final int lastLegIndex = route.getLegs().length-1;
9 tomtomMap.clear();
10 int styleIndex = 0;
11 for(RouteLeg leg:route.getLegs()) {
12 int trafficDelay = leg.getSummary().getTrafficDelayInSeconds();
13 //marking legs with delays longer than 10 minutes as bad traffic
14 boolean isBadTraffic = trafficDelay > (10*60);
15 isBadTraffic = true;
17 LatLng[] legPoints = leg.getPoints();
19 //Wrap the points array into a vector list
20 Vector<LatLng> legList = new Vector<LatLng>();
21 Collections.addAll(legList, legPoints);
23 //Build the route with the applied style and add it to the map
25 RouteBuilder routeBuilder = new RouteBuilder(legList)
26 .style(routeStyleList.get((styleIndex) % routeStyleList.size()));
27 if(styleIndex == 0)
28 routeBuilder.startIcon(startIcon);
29 if(styleIndex == lastLegIndex)
30 routeBuilder.endIcon(endIcon);
31 else
32 routeBuilder.endIcon(waypointIcon);
33 tomtomMap.addRoute(routeBuilder);
35 //if this leg has bad traffic, draw a red dashed line over the route
37 if(isBadTraffic) {
38 RouteBuilder badTrafficRoute = new RouteBuilder(legList)
39 .style(badTrafficStyle);
40 tomtomMap.addRoute(badTrafficRoute);
41 }
42 ++styleIndex;
44 }
45 tomtomMap.displayRoutesOverview();

[]If we run the application now and select several waypoints, the legs of the trip are now visually distinct from each other. If you needed to plan a stop, the details for each leg are easy to see — no need to estimate on the fly.


Searching for Points of Interest

[]There may be other places of interest along a route such as gas stations, weigh stations, and places offering other services. Once we have a route result, it can be used as a search parameter for other POI.

[]The constraints for POI are adjustable. I will create a search for fuel stations that can be reached through a detour that is within a certain time constraint from the route. The search could also be for some energy or fuel consumption constraint or consider the business hours of potential matches.

To search along a route, create a new AlongRouteSearchQueryBuilder. Its constructor takes a keyword that characterizes the type of business being sought, the coordinates of the path along which to search, and the maximum amount of time from the planned route allowed (in seconds).

[]For the coordinates, I am using the list of coordinates for the entire route. A search could be constrained to a single leg of a trip by passing the coordinates that make up that leg instead of those of the entire route. The following performs a search for gas stations that can be reached within a five-minute (600-second) deviation. It also sets the maximum number of results to ten items.

1final int MAX_DETOUR_TIME = 600;
2final int RESULT_LIMIT = 10;
5 new AlongRouteSearchQueryBuilder("gas", fullRoute.getCoordinates(), MAX_DETOUR_TIME)
6 .withLimit(RESULT_LIMIT).build()
9.subscribe(new DisposableSingleObserver<AlongRouteSearchResponse>() {
11 //Body of Observer omitted
12 });

The search results are passed back to the observer specified in the subscribe method. The observer must implement the methods onSuccess and onError. For an error, the observer will show a toast notification that the search failed. For success, we want to display the results on the map as markers. If a user touches a marker, it will display text that we choose. The name of the point of interest will be used for the display text.

1.subscribe(new DisposableSingleObserver<AlongRouteSearchResponse>() {
2 @Override
3 public void onSuccess(AlongRouteSearchResponse alongRouteSearchResponse) {
4 showSearchResults(alongRouteSearchResponse.getResults());
5 }
7 @Override
8 public void onError(Throwable e) {
9 Toast.makeText(MainActivity.this, getString(R.string.no_search_results), Toast.LENGTH_LONG).show();
10 }
12 private void showSearchResults(List<AlongRouteSearchResult> resultList) {
13 if(resultList.isEmpty()) {
14 Toast.makeText(MainActivity.this, R.string.no_search_results, Toast.LENGTH_SHORT).show();
15 return;
16 }
18 for(AlongRouteSearchResult result:resultList) {
19 LatLng position = result.getPosition();
20 String poiName = result.getPoi().getName();
21 SimpleMarkerBalloon markerBalloonData = new SimpleMarkerBalloon(poiName);
22 markerBalloonData.addProperty(getString(R.string.address_key), address);
24 MarkerBuilder markerBuilder = new MarkerBuilder(position)
25 .markerBalloon(markerBalloonData)
26 .shouldCluster(true);
27 tomtomMap.addMarker(markerBuilder);
29 }
30 }


Taking it Further

With the TomTom Maps SDK it is easy to build applications for location search and navigation. We were able to extend the previous application to route through multiple stops and search for points of interest along the way.

If you would like to know more about how to track vehicles when they are on the road, check out our Asset and Vehicle Tracking demo.

You can get started with using the Maps SDK for Android by registering for a free developer account. If you would like to learn more about the TomTom Maps SDK for Android, check out the following links:

Get the developer

No marketing fuff. Tech content only.
Thanks for contacting us

We will reach out to you soon.
Blog cards
tomtom tech news