Navigation instructions
The Maps SDK for Android offers comprehensive real-time navigation guidance, providing detailed information on the current and upcoming route, including road details, street names, speed limits, and turn directions. It delivers essential data such as remaining travel time, distance to destination, and upcoming turn or road information, ensuring users receive accurate, timely instructions. Designed for both navigation and simulation scenarios, this feature enhances the overall user experience by supporting smooth and efficient route planning and execution.
The main class responsible for turn-by-turn live navigation guidance is the NavigationInstruction class.
It is important to distinguish between NavigationInstruction and RouteInstruction. NavigationInstruction offers real-time, turn-by-turn navigation based on the user's current position and is relevant only during navigation or simulation. In contrast, RouteInstruction provides an overview of the entire route available as soon as the route is calculated and the list of instructions do not change as the user navigates on the route.
Instantiating navigation instructions
Navigation instructions cannot be directly instantiated. Instead, they must be provided by the SDK while navigating. For detailed guidance on how to navigate on routes, refer to the Getting Started with Navigation Guide.
There are two main ways of getting a navigation instruction:
NavigationInstructioninstances can be obtained via the callback provided as parameters to thestartNavigationandstartSimulationmethods through theNavigationListener.- The
NavigationServiceclass provides agetNavigationInstructionmethod which returns the currently available navigation instruction. Make sure navigation/simulation is active before using the method provided above.
Import statements
To work with navigation instructions in your Android project, you'll need the following imports:
- Kotlin
- Java
import com.magiclane.sdk.routesandnavigation.NavigationInstruction
import com.magiclane.sdk.routesandnavigation.NavigationListener
import com.magiclane.sdk.routesandnavigation.NavigationService
import com.magiclane.sdk.core.Rgba
import com.magiclane.sdk.util.GemUtilImages
import com.magiclane.sdk.util.SdkCall
import android.graphics.Bitmap
import android.widget.Toast
import com.magiclane.sdk.routesandnavigation.NavigationInstruction;
import com.magiclane.sdk.routesandnavigation.NavigationListener;
import com.magiclane.sdk.routesandnavigation.NavigationService;
import com.magiclane.sdk.core.Rgba;
import com.magiclane.sdk.util.GemUtilImages;
import com.magiclane.sdk.util.SdkCall;
import android.graphics.Bitmap;
import android.widget.Toast;
Getting navigation instructions through NavigationListener
The most common way to receive navigation instructions is through the NavigationListener.onNavigationInstructionUpdated callback:
- Kotlin
- Java
private val navigationListener: NavigationListener = NavigationListener.create(
onNavigationInstructionUpdated = { instr ->
// Handle the navigation instruction
val currentStreetName = instr.currentStreetName
val nextTurnInstruction = instr.nextTurnInstruction
// Process instruction data...
}
)
private final NavigationListener navigationListener = NavigationListener.create(
instr -> {
// Handle the navigation instruction
String currentStreetName = instr.getCurrentStreetName();
String nextTurnInstruction = instr.getNextTurnInstruction();
// Process instruction data...
}
);
Getting navigation instructions from NavigationService
You can also directly query the navigation service for the current instruction:
- Kotlin
- Java
private val navigationService = NavigationService()
// Get current navigation instruction (ensure navigation is active)
val currentInstruction = navigationService.getNavigationInstruction()
private final NavigationService navigationService = new NavigationService();
// Get current navigation instruction (ensure navigation is active)
NavigationInstruction currentInstruction = navigationService.getNavigationInstruction();
NavigationInstruction structure
| Member | Type | Description |
|---|---|---|
currentCountryCodeISO | String? | Returns the ISO 3166-1 alpha-3 country code for the current navigation instruction. Empty string means no country. |
currentStreetName | String? | Returns the current street name. |
currentStreetSpeedLimit | Double | Returns the maximum speed limit on the current street in meters per second. Returns 0 if not available. |
driveSide | EDriveSide | Returns the drive side flag of the current traveled road. |
hasNextNextTurnInfo() | Boolean | Returns true if next-next turn information is available. |
hasNextTurnInfo() | Boolean | Returns true if next turn information is available. |
instructionIndex | Int | Returns the index of the current route instruction on the current route segment. |
laneImage | Image? | Returns a customizable image representation of current lane configuration. The user is responsible to verify if the image is valid. |
navigationStatus | ENavigationStatus | Returns the navigation/simulation status. |
nextCountryCodeISO | String? | Returns the ISO 3166-1 alpha-3 country code for the next navigation instruction. |
nextNextStreetName | String? | Returns the next-next street name. |
nextNextTurnDetails | TurnDetails? | Returns the full details for the next-next turn. Used for customizing turn display in UI. |
nextNextTurnInstruction | String? | Returns the textual description for the next-next turn. |
nextStreetName | String? | Returns the next street name. |
nextTurnDetails | TurnDetails? | Returns the full details for the next turn. Used for customizing turn display in UI. |
nextTurnInstruction | String? | Returns the textual description for the next turn. |
remainingTravelTimeDistance | TimeDistance? | Returns the remaining travel time in seconds and distance in meters. |
remainingTravelTimeDistanceToNextWaypoint | TimeDistance? | Returns the remaining travel time in seconds and distance in meters to the next waypoint. |
currentRoadInformation | List<RoadInfo>? | Returns the current road information list. |
nextRoadInformation | List<RoadInfo>? | Returns the next road information list. |
nextNextRoadInformation | List<RoadInfo>? | Returns the next-next road information list. |
segmentIndex | Int | Returns the index of the current route segment. |
signpostDetails | SignpostDetails? | Returns the extended signpost details. |
signpostInstruction | String? | Returns the textual description for the signpost information. |
timeDistanceToNextNextTurn | TimeDistance? | Returns the time (seconds) and distance (meters) to the next-next turn. Returns values for next turn if no next-next turn available. |
timeDistanceToNextTurn | TimeDistance? | Returns the time (seconds) and distance (meters) to the next turn. |
traveledTimeDistance | TimeDistance? | Returns the traveled time in seconds and distance in meters. |
The field nextTurnInstruction provides an instruction in text format, suitable for displaying on UI. Please use the onNavigationSound callback in NavigationListener for getting an instruction suitable for text-to-speech.
Turn details
Next turn details
The following snippet shows how to extract detailed instructions for the next turn along the route. It's typically used in the navigation UI to show users the upcoming maneuver. You may also use this to provide turn-by-turn instructions with images or detailed text for navigation display:
- Kotlin
- Java
// If hasNextTurnInfo() is false some details are not available
val hasNextTurnInfo = navigationInstruction.hasNextTurnInfo()
if (hasNextTurnInfo) {
// The next turn instruction
val nextTurnInstruction = navigationInstruction.nextTurnInstruction
// The next turn details
val turnDetails = navigationInstruction.nextTurnDetails
turnDetails?.let {
// Turn event type (continue straight, turn right, turn left, etc.)
val event = it.event
// Roundabout exit number (-1 if not a roundabout)
val roundaboutExitNumber = it.roundaboutExitNumber
// Get turn image using GemUtilImages
val turnImageSize = 300
val turnImage = getNextTurnImage(navigationInstruction, turnImageSize, turnImageSize)
}
}
// Helper function to get turn image (based on sample code)
private fun getNextTurnImage(
navInstr: NavigationInstruction,
width: Int,
height: Int
): Bitmap? {
return SdkCall.execute {
if (!navInstr.hasNextTurnInfo()) return@execute null
val image = navInstr.nextTurnDetails?.abstractGeometryImage
val aInner = Rgba(255, 255, 255, 255)
val aOuter = Rgba(0, 0, 0, 255)
val iInner = Rgba(128, 128, 128, 255)
val iOuter = Rgba(128, 128, 128, 255)
GemUtilImages.asBitmap(image, width, height, aInner, aOuter, iInner, iOuter)
}
}
// If hasNextTurnInfo() is false some details are not available
boolean hasNextTurnInfo = navigationInstruction.hasNextTurnInfo();
if (hasNextTurnInfo) {
// The next turn instruction
String nextTurnInstruction = navigationInstruction.getNextTurnInstruction();
// The next turn details
TurnDetails turnDetails = navigationInstruction.getNextTurnDetails();
if (turnDetails != null) {
// Turn event type (continue straight, turn right, turn left, etc.)
Object event = turnDetails.getEvent();
// Roundabout exit number (-1 if not a roundabout)
int roundaboutExitNumber = turnDetails.getRoundaboutExitNumber();
// Get turn image using GemUtilImages
int turnImageSize = 300;
Bitmap turnImage = getNextTurnImage(navigationInstruction, turnImageSize, turnImageSize);
}
}
// Helper method to get turn image (based on sample code)
private Bitmap getNextTurnImage(
NavigationInstruction navInstr,
int width,
int height
) {
return SdkCall.execute(() -> {
if (!navInstr.hasNextTurnInfo()) return null;
TurnDetails details = navInstr.getNextTurnDetails();
Object image = details != null ? details.getAbstractGeometryImage() : null;
Rgba aInner = new Rgba(255, 255, 255, 255);
Rgba aOuter = new Rgba(0, 0, 0, 255);
Rgba iInner = new Rgba(128, 128, 128, 255);
Rgba iOuter = new Rgba(128, 128, 128, 255);
return GemUtilImages.asBitmap(image, width, height, aInner, aOuter, iInner, iOuter);
});
}
See the TurnDetails guide for more details about the fields within the TurnDetails class.
Next next turn details
Details about the subsequent turn (i.e., the turn following the next one) can be crucial for certain use cases, such as providing a preview of upcoming maneuvers. These details can be accessed in a similar manner:
- Kotlin
- Java
// If hasNextNextTurnInfo() is false some details are not available
val hasNextNextTurnInfo = navigationInstruction.hasNextNextTurnInfo()
if (hasNextNextTurnInfo) {
val nextNextTurnInstruction = navigationInstruction.nextNextTurnInstruction
val nextNextTurnDetails = navigationInstruction.nextNextTurnDetails
// Get next next turn image similarly to next turn image
nextNextTurnDetails?.let {
val nextNextTurnImage = getNextNextTurnImage(navigationInstruction, 300, 300)
}
}
// Helper function for next next turn image
private fun getNextNextTurnImage(
navInstr: NavigationInstruction,
width: Int,
height: Int
): Bitmap? {
return SdkCall.execute {
if (!navInstr.hasNextNextTurnInfo()) return@execute null
val image = navInstr.nextNextTurnDetails?.abstractGeometryImage
val aInner = Rgba(255, 255, 255, 255)
val aOuter = Rgba(0, 0, 0, 255)
val iInner = Rgba(128, 128, 128, 255)
val iOuter = Rgba(128, 128, 128, 255)
GemUtilImages.asBitmap(image, width, height, aInner, aOuter, iInner, iOuter)
}
}
// If hasNextNextTurnInfo() is false some details are not available
boolean hasNextNextTurnInfo = navigationInstruction.hasNextNextTurnInfo();
if (hasNextNextTurnInfo) {
String nextNextTurnInstruction = navigationInstruction.getNextNextTurnInstruction();
TurnDetails nextNextTurnDetails = navigationInstruction.getNextNextTurnDetails();
// Get next next turn image similarly to next turn image
if (nextNextTurnDetails != null) {
Bitmap nextNextTurnImage = getNextNextTurnImage(navigationInstruction, 300, 300);
}
}
// Helper method for next next turn image
private Bitmap getNextNextTurnImage(
NavigationInstruction navInstr,
int width,
int height
) {
return SdkCall.execute(() -> {
if (!navInstr.hasNextNextTurnInfo()) return null;
TurnDetails details = navInstr.getNextNextTurnDetails();
Object image = details != null ? details.getAbstractGeometryImage() : null;
Rgba aInner = new Rgba(255, 255, 255, 255);
Rgba aOuter = new Rgba(0, 0, 0, 255);
Rgba iInner = new Rgba(128, 128, 128, 255);
Rgba iOuter = new Rgba(128, 128, 128, 255);
return GemUtilImages.asBitmap(image, width, height, aInner, aOuter, iInner, iOuter);
});
}
The hasNextNextTurnInfo() might be false if the next instruction is the destination.
The same operations discussed earlier for the next turn details can also be applied to the subsequent turn details.
Street information
Current street information
The following snippet shows how to get information about the current road:
- Kotlin
- Java
// Current street name
val currentStreetName = navigationInstruction.currentStreetName
// Road info related to the current road
val currentRoadInfo = navigationInstruction.currentRoadInformation
// Country ISO code
val countryCode = navigationInstruction.currentCountryCodeISO
// The drive direction (left or right)
val driveDirection = navigationInstruction.driveSide
// Current street name
String currentStreetName = navigationInstruction.getCurrentStreetName();
// Road info related to the current road
List<RoadInfo> currentRoadInfo = navigationInstruction.getCurrentRoadInformation();
// Country ISO code
String countryCode = navigationInstruction.getCurrentCountryCodeISO();
// The drive direction (left or right)
EDriveSide driveDirection = navigationInstruction.getDriveSide();
It is important to note that some streets may not have an assigned name. In such cases, currentStreetName will return null. The RoadInfo class offers additional details, including the road name and shield type, which correspond to the official codes or names assigned to a road.
For example, the currentStreetName might return "Bloomsbury Street," while the roadname field in the associated RoadInfo instance could provide the official designation, such as "A400." This distinction ensures comprehensive road identification.
Next & next next street information
Information about the next street, as well as the street following it (next-next street), can also be retrieved. These details include the street name, type, and other associated metadata, enabling enhanced navigation and situational awareness:
- Kotlin
- Java
// Street name
val nextStreetName = navigationInstruction.nextStreetName
val nextNextStreetName = navigationInstruction.nextNextStreetName
// Road info
val nextRoadInformation = navigationInstruction.nextRoadInformation
val nextNextRoadInformation = navigationInstruction.nextNextRoadInformation
// Next country iso code
val nextCountryCodeISO = navigationInstruction.nextCountryCodeISO
// Street name
String nextStreetName = navigationInstruction.getNextStreetName();
String nextNextStreetName = navigationInstruction.getNextNextStreetName();
// Road info
List<RoadInfo> nextRoadInformation = navigationInstruction.getNextRoadInformation();
List<RoadInfo> nextNextRoadInformation = navigationInstruction.getNextNextRoadInformation();
// Next country iso code
String nextCountryCodeISO = navigationInstruction.getNextCountryCodeISO();
The fields associated with these streets retain the same meanings as discussed earlier about the current street.
Ensure that hasNextTurnInfo() and hasNextNextTurnInfo() are true before attempting to access the respective fields. This verification prevents errors and ensures the availability of reliable data for the requested information.
Speed limit information
The NavigationInstruction class not only provides information about the current road's speed limit but also offers details about upcoming speed limits within a specified distance, assuming the user adheres to the recommended navigation route.
The following snippet demonstrates how to retrieve these details and handle various scenarios appropriately:
- Kotlin
- Java
SdkCall.execute {
// The current street speed limit in m/s (0.0 if not available)
val currentStreetSpeedLimit = navigationInstruction.currentStreetSpeedLimit
}
SdkCall.execute(() -> {
// The current street speed limit in m/s (0.0 if not available)
double currentStreetSpeedLimit = navigationInstruction.getCurrentStreetSpeedLimit();
});
Lane image
The lane image can be used to more effectively illustrate the correct lane for upcoming turns, providing clearer guidance:
- Kotlin
- Java
val laneImage = navigationInstruction.laneImage
// Helper function to get lane image (based on sample code)
private fun getLaneInfoImage(
navInstr: NavigationInstruction,
width: Int,
height: Int
): Pair<Int, Bitmap?> {
return SdkCall.execute {
var resultWidth = width
if (resultWidth == 0)
resultWidth = (2.5 * height).toInt()
val bkColor = Rgba(118, 99, 200, 255)
val activeColor = Rgba(255, 255, 255, 255)
val inactiveColor = Rgba(24, 33, 21, 255)
val image = navInstr.laneImage
val resultPair = GemUtilImages.asBitmap(
image,
resultWidth,
height,
bkColor,
activeColor,
inactiveColor
)
// resultPair is Pair<Int, Bitmap?> where first is error code, second is bitmap
val errorCode = resultPair.first
val bitmap = resultPair.second
return@execute Pair(errorCode, bitmap)
} ?: Pair(-1, null)
}
// Usage example
val laneImageData = getLaneInfoImage(navigationInstruction, 500, 300)
val errorCode = laneImageData.first
val laneImageBitmap = laneImageData.second
if (errorCode == 0 && laneImageBitmap != null) {
// Successfully obtained lane image bitmap
// Use laneImageBitmap for display
} else {
// Handle error case
println("Failed to get lane image, error code: $errorCode")
}
Object laneImage = navigationInstruction.getLaneImage();
// Helper method to get lane image (based on sample code)
private Pair<Integer, Bitmap> getLaneInfoImage(
NavigationInstruction navInstr,
int width,
int height
) {
Pair<Integer, Bitmap> result = SdkCall.execute(() -> {
int resultWidth = width;
if (resultWidth == 0)
resultWidth = (int) (2.5 * height);
Rgba bkColor = new Rgba(118, 99, 200, 255);
Rgba activeColor = new Rgba(255, 255, 255, 255);
Rgba inactiveColor = new Rgba(24, 33, 21, 255);
Object image = navInstr.getLaneImage();
Pair<Integer, Bitmap> resultPair = GemUtilImages.asBitmap(
image,
resultWidth,
height,
bkColor,
activeColor,
inactiveColor
);
// resultPair is Pair<Integer, Bitmap> where first is error code, second is bitmap
int errorCode = resultPair.first;
Bitmap bitmap = resultPair.second;
return new Pair<>(errorCode, bitmap);
});
if (result != null) {
return result;
}
return new Pair<>(-1, null);
}
// Usage example
Pair<Integer, Bitmap> laneImageData = getLaneInfoImage(navigationInstruction, 500, 300);
int errorCode = laneImageData.first;
Bitmap laneImageBitmap = laneImageData.second;
if (errorCode == 0 && laneImageBitmap != null) {
// Successfully obtained lane image bitmap
// Use laneImageBitmap for display
} else {
// Handle error case
System.out.println("Failed to get lane image, error code: " + errorCode);
}
Below is an example of a rendered lane image:
Change the language of the instructions
The texts used in navigation instructions and related classes follow the language set in the SDK. See the internationalization guide for more details.