Skip to content

Images

The images are represented in an abstract form which is not directly drawable on the UI. The responsible classes are UnlImage, AbstractGeometryImage, LaneImage, SignpostImage, and RoadInfoImage.

These classes provide the following methods and properties:

  • uid: Gets the unique ID of the image. If two images are the same, then they have the same UID. This allows the user to optimize UI redraws and trigger an update only when the image changes.
  • isValid(): Verifies if the image data is valid. If the image is not valid, then asBitmap() and render() methods will return null or fail.
  • asBitmap(width, height): Returns an Android Bitmap which can be displayed on the UI using ImageView.setImageBitmap(). It returns null if the image is invalid.
  • render(bitmap): Renders the image into the provided IBitmap object. Returns error codes for validation.

Plain images

The UnlImage class corresponds to plain (usually non-vector) images. These images have a recommended size and aspect ratio but can be resized to any dimension in order to be drawn (with possible loss of quality).

The user can instantiate an UnlImage class:

  • Using the UnlImage.produceWithDataBuffer() method which takes a DataBuffer containing the image data and an EUnlImageFileFormat.
  • Using the UnlImage.produceWithPath() method which takes a file path to an image file.
  • Using the UnlImageDatabase to retrieve predefined SDK images by ID.

Special images

The AbstractGeometryImage, LaneImage, SignpostImage, and RoadInfoImage classes correspond to vector images generated by the SDK based on internal data. They provide customizable options for rendering. They do not have a default size or aspect ratio.

The LaneImage, SignpostImage, and RoadInfoImage classes provide specialized asBitmap() methods that handle resizing automatically based on the image content. These images can also be configured with custom render settings for colors and appearance.

The particularities of each image type are presented in the table below:

Class Size and aspect ratio Customizable render options The size of the bitmap returned by the asBitmap() methods Instantiable by the user
UnlImage Usually fixed and retrievable via the size and aspectRatio properties. Not available Will always render at the size specified by the user if provided, or the recommended size for the particular image otherwise. Yes, via the provided factory methods UnlImage.produceWithDataBuffer() and UnlImage.produceWithPath(). It can also be provided by the SDK.
AbstractGeometryImage Generated by the SDK. Size and aspect ratio not retrievable. Yes, via AbstractGeometryImageRenderSettings Will always render at the size specified by the user if provided, or the SDK default image size otherwise. No. Can only be provided by the SDK.
LaneImage Generated by the SDK. Size and aspect ratio not retrievable. Yes, via LaneImageRenderSettings Returns a Pair<Int, Bitmap?> where the first value is the actual width used (when width is 0, auto-calculated), and the second is the bitmap. No. Can only be provided by the SDK.
SignpostImage Generated by the SDK. Size and aspect ratio not retrievable. Yes, via SignpostImageRenderSettings Returns a Pair<Int, Bitmap?> where the first value is the actual width used (when width is 0, auto-calculated), and the second is the bitmap. No. Can only be provided by the SDK.
RoadInfoImage Generated by the SDK. Size and aspect ratio not retrievable. Background color customizable via Rgba Returns a Pair<Int, Bitmap?> where the first value is the actual width used (when width is 0, auto-calculated), and the second is the bitmap. No. Can only be provided by the SDK.

💡 Tip

For debugging purposes, image bitmaps can be saved to files or viewed directly on Android devices. You can use the Bitmap.compress() method to save bitmaps to PNG format for inspection during development.

Approaches

The different approaches for getting images as Android Bitmap objects from the SDK are illustrated below using the lane image contained within UnlNavigationInstruction. The general principles are the same for all types of images.

Approach 1

  • Kotlin
  • Java
// Kotlin
val laneImage = instruction.laneImage
val bitmap = laneImage?.asBitmap(200, 100)

// Usage example
if (bitmap != null) {
    // Successfully obtained lane image bitmap
    imageView.setImageBitmap(bitmap)
} else {
    // Handle error case
    println("Failed to get lane image bitmap")
}

// Java
LaneImage laneImage = instruction.getLaneImage();
Bitmap bitmap = null;
if (laneImage != null) {
    bitmap = laneImage.asBitmap(200, 100);
}

// Usage example
if (bitmap != null) {
    // Successfully obtained lane image bitmap
    imageView.setImageBitmap(bitmap);
} else {
    // Handle error case
    System.out.println("Failed to get lane image bitmap");
}

This approach is simple and straightforward, with minimal boilerplate code. However, it comes with a few limitations:

  • You don't have access to custom color settings for the lane image.
  • There is no way to retrieve the actual image size when using auto-sizing.
  • The image UID is not directly accessible through this method.

The asBitmap method returns null if the image is invalid.

Approach 2

  • Kotlin
  • Java
// Kotlin
val laneImage = instruction.laneImage
val (actualWidth, bitmap) = laneImage?.asBitmap(200, 100) ?: Pair(0, null)

// Get more data about the lane image
val uid = laneImage?.uid ?: 0L

// Java
LaneImage laneImage = instruction.getLaneImage();
int actualWidth = 0;
Bitmap bitmap = null;
if (laneImage != null) {
    Pair<Integer, Bitmap> result = laneImage.asBitmap(200, 100);
    if (result != null) {
        actualWidth = result.first;
        bitmap = result.second;
    }
}

// Get more data about the lane image
long uid = laneImage != null ? laneImage.getUid() : 0L;

This approach provides the actual width used when rendering the image, which is particularly useful for lane images that auto-calculate their width based on content.

The asBitmap method returns a Pair<Int, Bitmap?> where:

  • The first value is the actual width used (especially when you pass 0 for width to auto-calculate)
  • The second value is the rendered bitmap or null if invalid

You can efficiently use the image UID to optimize redraws by checking if the image has changed before re-rendering.

Approach 3

  • Kotlin
  • Java
// Kotlin
val laneImage = instruction.laneImage
val backgroundColor = Rgba(118, 99, 200, 255)
val activeColor = Rgba(255, 255, 255, 255)
val inactiveColor = Rgba(24, 33, 21, 255)

val (actualWidth, bitmap) = laneImage?.asBitmap(
    200, 100,
    backgroundColor,
    activeColor,
    inactiveColor
) ?: Pair(0, null)

// Get more data about the lane image
val uid = laneImage?.uid ?: 0L

// Java
LaneImage laneImage = instruction.getLaneImage();
Rgba backgroundColor = new Rgba(118, 99, 200, 255);
Rgba activeColor = new Rgba(255, 255, 255, 255);
Rgba inactiveColor = new Rgba(24, 33, 21, 255);

int actualWidth = 0;
Bitmap bitmap = null;
if (laneImage != null) {
    Pair<Integer, Bitmap> result = laneImage.asBitmap(
        200, 100,
        backgroundColor,
        activeColor,
        inactiveColor
    );
    if (result != null) {
        actualWidth = result.first;
        bitmap = result.second;
    }
}

// Get more data about the lane image
long uid = laneImage != null ? laneImage.getUid() : 0L;

This approach provides the greatest flexibility by allowing custom color configuration:

  • backgroundColor: The background color of the lane image
  • activeColor: Color for active/highlighted lanes
  • inactiveColor: Color for inactive/non-highlighted lanes

The returned Pair contains the actual dimensions and the rendered bitmap with your custom colors applied.

Approach 4 - Using GemUtilImages

  • Kotlin
  • Java
// Kotlin
val laneImage = instruction.laneImage
val backgroundColor = Rgba(118, 99, 200, 255)
val activeColor = Rgba(255, 255, 255, 255)
val inactiveColor = Rgba(24, 33, 21, 255)

val resultPair = SdkCall.execute {
    GemUtilImages.asBitmap(
        laneImage,
        200,
        100,
        backgroundColor,
        activeColor,
        inactiveColor
    )
}

val errorCode = resultPair?.first ?: -1
val bitmap = resultPair?.second

if (errorCode == 0 && bitmap != null) {
    // Successfully obtained lane image bitmap
    imageView.setImageBitmap(bitmap)
} else {
    // Handle error case
    println("Failed to get lane image, error code: $errorCode")
}

// Java
LaneImage laneImage = instruction.getLaneImage();
Rgba backgroundColor = new Rgba(118, 99, 200, 255);
Rgba activeColor = new Rgba(255, 255, 255, 255);
Rgba inactiveColor = new Rgba(24, 33, 21, 255);

Pair<Integer, Bitmap> resultPair = SdkCall.execute(() ->
    GemUtilImages.asBitmap(
        laneImage,
        200,
        100,
        backgroundColor,
        activeColor,
        inactiveColor
    )
);

int errorCode = resultPair != null ? resultPair.first : -1;
Bitmap bitmap = resultPair != null ? resultPair.second : null;

if (errorCode == 0 && bitmap != null) {
    // Successfully obtained lane image bitmap
    imageView.setImageBitmap(bitmap);
} else {
    // Handle error case
    System.out.println("Failed to get lane image, error code: " + errorCode);
}

This approach uses the GemUtilImages utility class directly and provides error codes for more detailed error handling. The method returns a Pair<Int, Bitmap?> where the first value is an error code (0 for success) and the second is the bitmap.

This method should be called within SdkCall.execute for thread safety.

Working with Render Settings

Different image types provide customizable render settings to control their appearance:

Lane Image Settings

  • Kotlin
  • Java
// Kotlin
val laneRenderSettings = LaneImageRenderSettings().apply {
    backgroundColor = Rgba(118, 99, 200, 255)  // Purple background
    activeColor = Rgba(255, 255, 255, 255)     // White for active lanes
    inactiveColor = Rgba(128, 128, 128, 255)   // Gray for inactive lanes
}

// Use the settings when rendering
val (width, bitmap) = laneImage?.asBitmap(200, 100,
    laneRenderSettings.backgroundColor,
    laneRenderSettings.activeColor,
    laneRenderSettings.inactiveColor
) ?: Pair(0, null)

// Java
LaneImageRenderSettings laneRenderSettings = new LaneImageRenderSettings();
laneRenderSettings.setBackgroundColor(new Rgba(118, 99, 200, 255));  // Purple background
laneRenderSettings.setActiveColor(new Rgba(255, 255, 255, 255));     // White for active lanes
laneRenderSettings.setInactiveColor(new Rgba(128, 128, 128, 255));   // Gray for inactive lanes

// Use the settings when rendering
int width = 0;
Bitmap bitmap = null;
if (laneImage != null) {
    Pair<Integer, Bitmap> result = laneImage.asBitmap(200, 100,
        laneRenderSettings.getBackgroundColor(),
        laneRenderSettings.getActiveColor(),
        laneRenderSettings.getInactiveColor()
    );
    if (result != null) {
        width = result.first;
        bitmap = result.second;
    }
}

Signpost Image Settings

  • Kotlin
  • Java
// Kotlin
val signpostSettings = SignpostImageRenderSettings().apply {
    borderSize = 10                // Border size in pixels
    borderRoundCorners = true      // Use rounded corners
    maxRows = 3                    // Maximum rows of details
    smallMode = false              // Use large mode
}

// Apply settings when rendering signpost images
val (width, bitmap) = signpostImage?.asBitmap(300, 150) ?: Pair(0, null)

// Java
SignpostImageRenderSettings signpostSettings = new SignpostImageRenderSettings();
signpostSettings.setBorderSize(10);                // Border size in pixels
signpostSettings.setBorderRoundCorners(true);      // Use rounded corners
signpostSettings.setMaxRows(3);                    // Maximum rows of details
signpostSettings.setSmallMode(false);              // Use large mode

// Apply settings when rendering signpost images
int width = 0;
Bitmap bitmap = null;
if (signpostImage != null) {
    Pair<Integer, Bitmap> result = signpostImage.asBitmap(300, 150);
    if (result != null) {
        width = result.first;
        bitmap = result.second;
    }
}

Abstract Geometry UnlImage Settings

  • Kotlin
  • Java
// Kotlin
val geometrySettings = AbstractGeometryImageRenderSettings().apply {
    activeInnerColor = Rgba(255, 255, 255, 255)    // White inner active
    activeOuterColor = Rgba(0, 0, 0, 255)          // Black outer active
    inactiveInnerColor = Rgba(128, 128, 128, 255)  // Gray inner inactive
    inactiveOuterColor = Rgba(64, 64, 64, 255)     // Dark gray outer inactive
}

// Use custom colors for turn arrows
val turnBitmap = abstractGeometryImage?.asBitmap(80, 80,
    geometrySettings.activeInnerColor,
    geometrySettings.activeOuterColor,
    geometrySettings.inactiveInnerColor,
    geometrySettings.inactiveOuterColor
)

// Java
AbstractGeometryImageRenderSettings geometrySettings = new AbstractGeometryImageRenderSettings();
geometrySettings.setActiveInnerColor(new Rgba(255, 255, 255, 255));    // White inner active
geometrySettings.setActiveOuterColor(new Rgba(0, 0, 0, 255));          // Black outer active
geometrySettings.setInactiveInnerColor(new Rgba(128, 128, 128, 255));  // Gray inner inactive
geometrySettings.setInactiveOuterColor(new Rgba(64, 64, 64, 255));     // Dark gray outer inactive

// Use custom colors for turn arrows
Bitmap turnBitmap = null;
if (abstractGeometryImage != null) {
    turnBitmap = abstractGeometryImage.asBitmap(80, 80,
        geometrySettings.getActiveInnerColor(),
        geometrySettings.getActiveOuterColor(),
        geometrySettings.getInactiveInnerColor(),
        geometrySettings.getInactiveOuterColor()
    );
}

Common Use Cases

Displaying Lane Instructions

  • Kotlin
  • Java
// Kotlin
// From navigation instruction
val instruction: UnlNavigationInstruction = // ... obtained from navigation
val laneImage = instruction.laneImage

// Display lane guidance with custom colors
laneImage?.let { image ->
    val (actualWidth, bitmap) = image.asBitmap(
        0, 60,  // Auto-calculate width, 60px height
        Rgba(30, 30, 30, 255),    // Dark background
        Rgba(0, 255, 0, 255),     // Green for active lanes
        Rgba(100, 100, 100, 255)  // Gray for inactive lanes
    )

    if (bitmap != null) {
        laneImageView.setImageBitmap(bitmap)
        // Optionally adjust view width based on actualWidth
    }
}

// Java
// From navigation instruction
UnlNavigationInstruction instruction = // ... obtained from navigation
LaneImage laneImage = instruction.getLaneImage();

// Display lane guidance with custom colors
if (laneImage != null) {
    Pair<Integer, Bitmap> result = laneImage.asBitmap(
        0, 60,  // Auto-calculate width, 60px height
        new Rgba(30, 30, 30, 255),    // Dark background
        new Rgba(0, 255, 0, 255),     // Green for active lanes
        new Rgba(100, 100, 100, 255)  // Gray for inactive lanes
    );

    if (result != null && result.second != null) {
        int actualWidth = result.first;
        Bitmap bitmap = result.second;
        laneImageView.setImageBitmap(bitmap);
        // Optionally adjust view width based on actualWidth
    }
}

Displaying Turn Instructions

  • Kotlin
  • Java
// Kotlin
// From navigation instruction
val turnImage = instruction.nextTurnDetails?.abstractGeometryImage

turnImage?.let { image ->
    val bitmap = image.asBitmap(80, 80,
        Rgba(255, 255, 255, 255), // Active inner: white
        Rgba(0, 0, 0, 255),       // Active outer: black
        Rgba(128, 128, 128, 255), // Inactive inner: gray
        Rgba(64, 64, 64, 255)     // Inactive outer: dark gray
    )

    if (bitmap != null) {
        turnArrowImageView.setImageBitmap(bitmap)
    }
}

// Java
// From navigation instruction
AbstractGeometryImage turnImage = null;
if (instruction.getNextTurnDetails() != null) {
    turnImage = instruction.getNextTurnDetails().getAbstractGeometryImage();
}

if (turnImage != null) {
    Bitmap bitmap = turnImage.asBitmap(80, 80,
        new Rgba(255, 255, 255, 255), // Active inner: white
        new Rgba(0, 0, 0, 255),       // Active outer: black
        new Rgba(128, 128, 128, 255), // Inactive inner: gray
        new Rgba(64, 64, 64, 255)     // Inactive outer: dark gray
    );

    if (bitmap != null) {
        turnArrowImageView.setImageBitmap(bitmap);
    }
}

Creating Custom Images

  • Kotlin
  • Java
// Kotlin
// Load an image from assets
val customImage = SdkCall.execute {
    UnlImage.produceWithPath("/path/to/image.png")
}

// Or from a data buffer
val imageData: ByteArray = // ... load your image data
val dataBuffer = DataBuffer().apply {
    // Populate with image data
}
val customImage2 = UnlImage.produceWithDataBuffer(dataBuffer, EUnlImageFileFormat.Png)

// Convert to bitmap for display
val bitmap = customImage?.asBitmap(100, 100)

// Java
// Load an image from assets
UnlImage customImage = SdkCall.execute(() ->
    UnlImage.produceWithPath("/path/to/image.png")
);

// Or from a data buffer
byte[] imageData = // ... load your image data
DataBuffer dataBuffer = new DataBuffer();
// Populate with image data

UnlImage customImage2 = UnlImage.produceWithDataBuffer(dataBuffer, EUnlImageFileFormat.Png);

// Convert to bitmap for display
Bitmap bitmap = null;
if (customImage != null) {
    bitmap = customImage.asBitmap(100, 100);
}

Using Predefined SDK Images

  • Kotlin
  • Java
// Kotlin
// Access predefined images from the SDK
val tollImage = UnlImageDatabase.tollImage
val ferryImage = UnlImageDatabase.ferryImage
val searchPin = UnlImageDatabase.searchResultsPin

// Convert to bitmaps for display
val tollBitmap = tollImage?.asBitmap(32, 32)
val ferryBitmap = ferryImage?.asBitmap(32, 32)

// Java
// Access predefined images from the SDK
UnlImage tollImage = UnlImageDatabase.getTollImage();
UnlImage ferryImage = UnlImageDatabase.getFerryImage();
UnlImage searchPin = UnlImageDatabase.getSearchResultsPin();

// Convert to bitmaps for display
Bitmap tollBitmap = tollImage != null ? tollImage.asBitmap(32, 32) : null;
Bitmap ferryBitmap = ferryImage != null ? ferryImage.asBitmap(32, 32) : null;