Signboards
The Navigation SDK provides information about what action a driver needs to take to get from one step to the next along a route in maneuver instructions. The Maneuver instructions guide provides written instructions in the application's UI. But, sometimes additional context is helpful when a maneuver instruction takes place in a particularly complex part of the road network.
Signboards help the user navigate through complex maneuvers. They present a combination of textual and graphical data in a format that aligns with what a user sees on the street signs in reality. A signboard consists of following pieces of information:
- A signboard or panel with a background color consistent with the color of physical road signs of that type in that region.
- Text consistent with what appears on physical road signs. This might include exit, exit number, freeway number or name, road name, from route, to route, destination, and other tertiary data as available.
- Road shields in the style of physical signs in that region.
- Lane indicators in the form of arrows.
- An exit icon.
When you request a route using the Navigation SDK, if a signboard is available for a step along the route it will be returned as a part of the banner instructions for that step in the imageUrl
property of the Mapbox Java SDK's BannerComponents
class.
When you request the image using the imageUrl
, you will receive a signboard in a raw SVG format. The raw SVG cannot be rendered directly on the screen. To render the signboard, you will need to convert the raw SVG to Bitmap. As a convenience, Mapbox Navigation SDK exposes SvgToBitmapParser
, which converts the SVG to Bitmap using a third-party rendering engine. MapboxSvgToBitmapParser
is a default implementation of SvgToBitmapParser
used by MapboxSignboardApi
. The Navigation SDK also exposes a standalone widget, MapboxSignboardView
, that renders the Bitmap on the screen.
Learn how to request a signboard and render it on top of your view below.
Create an instance of signboard API
If you want to use the default SvgToBitmapParser
included with the Navigation SDK, instantiate MapboxSignboardApi
:
private val signboardApi: MapboxSignboardApi by lazy {
MapboxSignboardApi(applicationContext)
}
If you want to use your own implementation of SvgToBitmapParser
, create an implementation of SvgToBitmapParser
and then instantiate MapboxSignboardApi
:
class MySvgToBitmapParser : SvgToBitmapParser() {
override fun parse(
svg: ByteArray,
options: MapboxSignboardOptions,
): Expected<Bitmap, String> {
// your logic to convert svg to bitmap
}
}
private val mySvgParser = MySvgToBitmapParser()
private val signboardApi: MapboxSignboardApi by lazy {
MapboxSignboardApi(mySvgParser)
}
The MapboxSignboardApi
also takes an optional parameter, MapboxSignboardOptions
. The option allows you to specify the desiredWidth
of the signboard you would want to display as well as the CSS styling. If not specified, the signboard will use a default width of 600px
and a default font family and font size in the CSS.
If you would like to define your own options:
private val signboardOptions: MapboxSignboardOptions by lazy {
MapboxSignboardOptions.Builder()
.desiredWidth(<YOUR_VALUE>)
.cssStyle(<YOUR_VALUE>)
.build()
}
With the default Mapbox SVG parser:
private val signboardApi: MapboxSignboardApi by lazy {
MapboxSignboardApi(context, signboardOptions)
}
With a custom SVG parser:
private val signboardApi: MapboxSignboardApi by lazy {
MapboxSignboardApi(mySvgParser, signboardOptions)
}
MapboxSignboardOptions
, you can't specify a height. To make sure the image is scaled properly, we use the aspect ratio defined in the raw signboard SVG image to calculate the height based on the width.The Navigation SDK uses androidsvg to render SVG to Bitmap. To resolve fonts for signboards, the library exposes SVGExternalFileResolver
. The Navigation SDK creates a wrapper around this resolver with a default implementation for reading font files. SVGExternalFileResolver
is injected as a constructor parameter to MapboxSvgToBitmapParser
.
To use your own resolver, instantiate MapboxSignboardApi
:
class MyResolver : SVGExternalFileResolver() {
override fun resolveFont(
fontFamily: String?,
fontWeight: Int,
fontStyle: String?,
): Typeface? {
// Return custom Android Typeface instance, or null
}
}
private val signboardApi: MapboxSignboardApi by lazy {
MapboxSignboardApi(MapboxSvgToBitmapParser(MyResolver(), signboardOptions)
}
Listen to banner instruction changes
Once you have instantiated the API, add the code below to listen to new banner instruction events:
private val bannerInstructionsObserver = object : BannerInstructionsObserver {
override fun onNewBannerInstructions(bannerInstructions: BannerInstructions) {
}
}
Make sure you register and unregister bannerInstructionsObserver
in the appropriate places:
override fun onStart() {
super.onStart()
mapboxNavigation.registerBannerInstructionsObserver(bannerInstructionsObserver)
}
override fun onStop() {
super.onStart()
mapboxNavigation.unregisterBannerInstructionsObserver(bannerInstructionsObserver)
}
Don't forget to cancel any potential in-flight MapboxSignboardApi
requests in onStop
or onDestroy
:
override fun onDestroy() {
super.onDestroy()
signboardApi.cancelAll()
}
Invoke API call
Now that you have registered to banner instruction event updates, every time onNewBannerInstructions
is triggered, invoke the API:
private val bannerInstructionsObserver = object : BannerInstructionsObserver {
override fun onNewBannerInstructions(bannerInstructions: BannerInstructions) {
signboardApi.generateSignboard(bannerInstructions, signboardCallback)
}
}
Create an instance of signboard callback
MapboxSignboardApi#generateSignboard
expects a callback as one of its parameters. The callback is of the type MapboxNavigationConsumer
, which informs you about the state of signboard.
private val signboardCallback = object :
MapboxNavigationConsumer<Expected<SignboardValue, SignboardError>> {
override fun accept(value: Expected<SignboardError, SignboardValue>) {
}
}
This callback is triggered internally by the generateSignboard
API and contains an Expected
property, which is either of the type SignboardValue
or SignboardError
.
Now you have access to the raw SVG contained in SignboardValue
in the form of byteArray
.
Render the signboard
Add MapboxSignboardView
to your layout file:
<com.mapbox.navigation.ui.maps.guidance.signboard.view.MapboxSignboardView
android:id="@+id/signboardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
Now that you have received the data inside the callback as Expected
you need to invoke render
on MapboxSignboardView
to render the data. Inside accept
invoke the call:
private val signboardCallback = object :
MapboxNavigationConsumer<Expected<SignboardValue, SignboardError>> {
override fun accept(value: Expected<SignboardError, SignboardValue>) {
signboardView.render(value)
}
}