Sorry, you need to enable JavaScript to visit this website.

Highlight Important Details with Custom Styling for Routing Sections

Use data from the Routing API to provide custom styling for segments along a route based on the section type.


Determining the best path from A to B is the cornerstone of efficient fleet management. With the TomTom Routing service, you can easily determine the best route based on variables including traffic, preferred road types, segment types to avoid, and routing considerations for hazardous loads. It even provides advanced features like batch and matrix routing that can route entire fleets at once.

An important consideration for paving a smooth route is giving your drivers details about important road features along their way. Highlighting conditions such as toll roads and secondary roads may be important, as is information such as bridges, tunnels, ferries, and other sections that may be restricted to certain kinds of cargo.

Fortunately, the Routing API returns extensive metadata about route segments that we can use to highlight important route features with custom styling. Through the TomTom Maps SDK for Web, we can access custom styling options for these route sections based on their segment type metadata. The section types are included in the metadata, but not styled differently by default.

An obvious use case for this feature is for long-distance fleet logistics. Custom styling for route sections lets us easily create map displays that highlight information that might require attention or additional planning. For example, tunnels that may be restricted to certain kinds of cargo, bridges that may have height or weight limits, toll roads, and so on.

In this article, we’ll show you how you can apply custom styling to route sections based on the metadata that routing requests return.

Routing Basics

In many cases, we’ll never need to call the TomTom Routing service directly. Instead we can use features provided by the Maps SDK for Web or the TomTom Maps SDKs for Android and iOS. Nevertheless, it's always good to know what happens under the hood of the Maps SDK, especially when we need to leverage the routing information.

When we call the Routing service, the API endpoint URL looks like this:

https://api.tomtom.com/routing/1/calculateRoute/{start}:{end}/json?key={key}

In this example, {start} and {end} are geo coordinates (latitude and longitude, comma separated). The {key} field should be a valid API key.

There are two possible kinds of request to the URL:

●     A GET request, where we pass all the required parameters in the form of query key-value pairs.

●     A POST request, where the required parameters are transported via a JSON-serialized body.

While GET requests may be limited in their size and could have other side-effects such as caching, POST requests are well suited to the more specialized tasks involving the Routing service.

Let’s see an example GET request:

GET https://api.tomtom.com/routing/1/calculateRoute/52.50931,13.42936:52.50274,13.43872/json?key={key}&instructionsType=text&language=en-US&vehicleHeading=90&sectionType=tollRoad&sectionType=traffic&routeType=eco&traffic=true&vehicleLoadType=USHazmatClass1

The sectionType parameter is optional. By default, its value is set to travelMode. Here, we set it to both tollRoad and traffic. Other values (such as bicyclecarTraincountry, and ferry) are available, too.

Another useful optional parameter is vehicleLoadType. It can be used to tell the TomTom Routing service about special restrictions on the vehicle and has supported values that conform to US Hazmat classes 1 through 9, as well as additional classifications for other countries. This is only an option if the travelMode is not pedestrian or bicycle. 

If these hazardous load types are specified, roads with certain conditions are avoided. For example, explosive or flammable loads may be routed away from tunnels where they are excluded.

The result to the above call would look similar to this (some arrays not pertinent to the example scenario are trimmed for brevity):

{

  "formatVersion": "0.0.12",

  "routes": [

    {

      "summary": {

        "lengthInMeters": 1588,

        "travelTimeInSeconds": 349,

        "trafficDelayInSeconds": 0,

        "departureTime": "2020-07-24T00:58:45+02:00",

        "arrivalTime": "2020-07-24T01:04:34+02:00"

      },

      "legs": [

        {

          "summary": {

            "lengthInMeters": 1588,

            "travelTimeInSeconds": 349,

            "trafficDelayInSeconds": 0,

            "departureTime": "2020-07-24T00:58:45+02:00",

            "arrivalTime": "2020-07-24T01:04:34+02:00"

          },

          "points": [

            {

              "latitude": 52.5093,

              "longitude": 13.42937

            },

            {

              "latitude": 52.50944,

              "longitude": 13.42949

            }

          ]

        }

      ],

      "guidance": {

        "instructions": [

          {

            "routeOffsetInMeters": 0,

            "travelTimeInSeconds": 0,

            "point": {

              "latitude": 52.50931,

              "longitude": 13.42937

            },

            "pointIndex": 0,

            "instructionType": "LOCATION_DEPARTURE",

            "street": "An der Schillingbrücke",

            "countryCode": "DEU",

            "possibleCombineWithNext": false,

            "drivingSide": "RIGHT",

            "maneuver": "DEPART",

            "message": "Leave from An der Schillingbrücke"

          }

        ],

        "instructionGroups": [

          {

            "firstInstructionIndex": 0,

            "lastInstructionIndex": 3,

            "groupMessage": "Leave from An der Schillingbrücke. Take the Andreasstraße",

            "groupLengthInMeters": 668

          }

        ]

      }

    }

  ]

}

The routes array contains the found options. The summary inside indicates the estimated time, along with the length of the route and the current delay.

How can we make the same call using the Maps SDK? Well, turns out it’s as easy as calling calculateRoute on the services object.

The call we’d discussed would thus change to something like the following JavaScript:

tt.services.calculateRoute({

  key: `{key}`,

  locations: '52.50931,13.42936:52.50274,13.43872',

  instructionsType: 'text',

  language: 'en-US',

  vehicleHeading: 90,

  sectionType: ['tollRoad', 'traffic'],

  routeType: 'eco',

  traffic: true,

  vehicleLoadType: ['USHazmatClass1'],

})

  .go()

  .then(data => {

    // ...

  });

The options can be constructed in a straightforward way using the known API parameters.

Route Styling Basics

Visualizing a calculated route is not very difficult. Let’s start by transforming the API response to a GeoJSON object:

const route = data.toGeoJson();

Then, add another layer to the map instance. As an example, we could make the route line blue, 6 pixels wide, with the following code:

map.addLayer({

  id: "route",

  type: "line",

  source: {

    type: "geojson",

    data,

  },

  paint: {

    "line-color": "#00d7ff",

    "line-width": 6,

  },

});

The paint property is the key thing to notice here. paint makes it possible to define the style of the entire route. In short, we can adjust the painting behavior to whatever we consider best.

The downside of “just” adding a single layer, without anything else, is that the route may not be in the user’s focus frame. One simple function to fix this is fitBounds:

function fitBounds(geojson) {

  const coordinates = geojson.features[0].geometry.coordinates;

  const bounds = [].concat.apply([], coordinates).reduce(

    (bounds, coord) => bounds.extend(tt.LngLat.convert(coord)),

    new tt.LngLatBounds()

  );

  map.jumpTo(map.cameraForBounds(bounds, { padding: 5 }));

}

The padding parameter is quite arbitrary. Depending on our needs, we can play with different values here. The usage of the function is as simple as calling fitBounds(route).

More Granular Styling with Route Sections

So far, we only applied a single style to the whole route. However, we can also apply different styles to the different parts of the route. Remember that the addLayer function can be called multiple times – we only need to provide different IDs for the different layers.

Using the sections property in the GeoJSON object, we can build different sub-routes of the route. That way, we can add different layers for all distinct section types. As an example, let’s consider showing toll roads.

First, let’s write a simple function to get the corresponding section data in the GeoJSON format from the original route GeoJSON:

function getSectionsData(geojson, type) {

  const { sections } = geojson.features[0].properties;

  const result = [];

  const coordinates = Array.prototype.concat.call(

    [],

    geojson.features[0].geometry.coordinates

  );

  for (const section of sections) {

    if (section.sectionType === type) {

      result.push({

        type: "Feature",

        properties: {

          ...section,

          sectionType: type,

        },

        geometry: {

          type: "LineString",

          coordinates: coordinates.slice(

            section.startPointIndex,

            section.endPointIndex + 1

          ),

        },

      });

    }

  }

  return result;

}

Ultimately, this function converts the data so that we get back an array of GeoJSON objects, where each entry represents one segment using the given type.

Custom Styling Example

In this section, we’ll style route segments to represent tolls, and even color them to show that we are traveling across multiple countries.

Let’s see an example that makes use of the getSectionsData function:

tt.services

  .calculateRoute({

    key: "{key}",

    locations: "2.354,48.857:13.4159,52.5222:19.4586,51.75905",

    sectionType: ["tollRoad", "country"],

    traffic: false,

  })

  .go()

  .then((routeJson) => {

    const route = routeJson.toGeoJson();

    drawLayer(route, "route", "#00d7ff", 6);

    getSectionsData(route, "TOLL_ROAD").forEach((tollRoad, i) =>

      drawLayer(tollRoad, `tollRoad_${i}`, "red", 4)

    );

    fitBounds(route);

  });

Note that the identifier for toll roads is TOLL_ROAD. For the list of all section type identifiers have a look at the official API documentation.

In this example, we’ll style the roads in a contrast color (red) with a slightly thinner line. The result looks like this.

sections1

Obviously, we can also group multiple sections, as well as use different sections. 

Let’s add to our example differentiation by country. Since the example route spans four countries, we can color toll roads with a different color for each country.

Assuming we constraint ourselves to the four countries on the given route we can change our code to be:

const colors = {

  FRA: 'blue',

  BEL: 'yellow',

  DEU: 'black',

  POL: 'white',

};

const route = routeJson.toGeoJson();

drawLayer(route, "route", "#00d7ff", 6);

getSectionsData(route, "COUNTRY").forEach(country =>

  drawLayer(country, `country_${country.properties.countryCode}`, colors[country.properties.countryCode], 6)

);

getSectionsData(route, "TOLL_ROAD").forEach((tollRoad, i) =>

  drawLayer(tollRoad, `tollRoad_${i}`, "red", 4)

);

fitBounds(route);

As you can see, the only difference is the use of another drawLayer call for the various countries. Here, we customize the look per section depending on the country. We could also change the line width or any other painting property if we so desired.

With the additional highlight in the form of different colors, the route offers a lot more valuable information. As shown below, we can now identify the relevant information – country to toll road association.

sections2

While France seems to have a general toll on their highway, Poland has exception to this rule in parts. Belgium and Germany do not have any tolls – at least on the roads used in the example scenario.

Here's another example showing roadways through part of Asia, covering Myanmar, Cambodia, and Vietnam.

sections3

Leveraging the power of custom styling, we can increase efficiency and streamline our fleet planning, scheduling, and routing processes many times over.

Next Steps

Using the Maps SDK, we can fully leverage the powerful and flexible TomTom Routing service without too much effort. The ability to style display of the route sections helps us present the relevant information on the map in the most efficient way.

Custom styling is also available in the iOS SDK and the Android SDK.

If you haven’t already, check out the TomTom Maps SDK by registering your own account. Follow the examples online to gain expertise quickly.

If you want to learn more, check out the other blog posts on routing and fleet logistics:

Happy mapping!

First published: 
Thursday, September 3, 2020 - 20:15
Last edited: 
Thursday, October 29, 2020 - 22:29