Terms Of Use

To use the Favendo SDKs, at least one of the versions of the Android or iOS operating systems supported by Google or Apple is required. With other or older mobile operating systems, correct use of the SDKs is no longer guaranteed. Please find our General Terms and Conditions here.

Setup

To use the Favendo Map in your Android application you have to add our Maven repository to your project’s build.gradle file.

project build.gradle
allprojects {
    repositories {
        google()
        jcenter()
        maven {
          url "http://maven-repository.favendo.de/artifactory/backspin-android-sdk"
          credentials {
              username = "${favendo_artifactory_user}"
              password = "${favendo_artifactory_password}"
          }
          name = "plugins-favendo"
        }
    }
}

In order to use our repository you need to authenticate with a valid username and password. It is a good practice to declare those as variables by adding the following lines to your project’s gradle.properties file.

gradle.properties
favendo_artifactory_user = your_name
favendo_artifactory_password = your_password

Now you have to add the dependency to your module’s build.gradle file.

Map Implementations

Favendo offers two map implementations from which you can choose.

  • Google Maps

  • Favendo MapView

module build.gradle
dependencies {
    implementation 'com.favendo.android:backspin-sdk-googlemap:6.0.0'
       -- or --
    implementation 'com.favendo.android:backspin-sdk-favendomap:6.0.0'
}

No matter if you imported the googlemap or the favendomap dependency it includes the Backspin SDK of the same version. If your app already uses the Backspin SDK you can simply remove it from your build.gradle file.

The Favendo Map uses the Backspin SDK and will only work if the SDK is already initialized and a root venue is loaded. For more information on how to work with the Backspin SDK take a look here.

If you work with the Google Maps implementation don’t forget to add the Google API Key to your app’s manifest. Look here for further instructions.

AndroidManifest.xml
<meta-data
       android:name="com.google.android.geo.API_KEY"
       android:value="YOUR_GOOGLE_MAPS_API_KEY"/>
This library uses Vector Drawables. In order to support Vector Drawables on devices running Android versions prior to version 5.0 (API level 21) you have to add vectorDrawables.useSupportLibrary = true to your app’s build.gradle file. Look here for further information.

Map Fragment

The Favendo Map SDK uses a fragment to show an indoor map of the currently loaded root venue. In order to do this you first have to subclass from the according MapFragment (FavendoMapFragment or GoogleMapFragment) and override its abstract onMapReady() method.

public class MyMapFragment extends FavendoMapFragment {

     @Override
     public void onMapReady() {
         // interact with the map
         // add markers, move camera, etc.
     }

 }

When the map is initialized and the first level plan loaded, onMapReady() gets called. At this point the map is ready to be used. You can add markers, move the camera, switch levels and do various other supported operations on the map fragment.

This fragment retains its instance and can therefore not be added via xml - use a FragmentTransaction instead. It also results in onMapReady() being called only once, regardless of any configuration changes (like rotating the device).

The following code snippet shows an example of an activity that adds the map fragment by a FragmentTransaction. R.id.fragmentContainer refers to an empty FrameLayout that exists inside the activity’s layout xml.

public class MyMapActivity extends AppCompatActivity {

    private static final String TAG_FRAGMENT = "MyMapFragment";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT) == null) {
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.fragmentContainer, new MyMapFragment(), TAG_FRAGMENT)
                    .commit();
        }

    }
}

Configuration

There are several settings that adjust the behavior of the map to your liking. Just override the onConfiguration(MapConfig) method in your map fragment and modify the MapConfig according to your needs.

public class MyMapFragment extends FavendoMapFragment {

    [...]

    @Override
    protected void onConfiguration(MapConfig mapConfig) {
         super.onConfiguration(mapConfig);

         mapConfig.setRenderWorldTiles(true);
         mapConfig.setInitialZoomLevel(17);
         [...]
    }

 }
RenderWorldTiles

Sets whether the map should render any world tiles that don’t belong to the indoor location.

InitialLevelNumber

Sets the initial level which is shown when the map is opened.

InitialZoomLevel

Sets the initial zoom level of the camera. If there is an active position, this value is ignored. If no value is set (-1 per default) the initial camera will show the whole level plan.

InitialCameraTarget

Sets the initial position of the camera. If there is an active position, this value is ignored.

InitialZoomLevelOnPosition

Sets the initial zoom level of the camera in case the map has an active position. If there is no active position, this value is ignored.

FollowModeZoomThreshold

Set the camera zoom level threshold at which pressing the position follow results in a zoom in.

MinZoomLevel

Sets the minimum zoom level of the camera. It is not possible to zoom out further than the zoom level set here.

MaxZoomLevel

Sets the maximum zoom level of the camera. It is not possible to zoom in further than the zoom level set here.

RestoreCamera

Sets whether the camera state should be restored after the map fragment is recreated.

RestoreCameraTimeout

Sets the time in milliseconds that has to pass before the saved camera state is no longer relevant.

PositionPathSnapping

Sets whether the position should be snapped to the navigation graph before showing it on the map.

NavigationEnabled

Sets whether the marker detail will show a navigation action button or not. This button can be used to start a navigation to the selected marker.

PositionDirectionSource

Specifies what direction source is used by the position marker. If set to MapConfig.POSITION_DIRECTION_NONE no direction will be displayed. If set to MapConfig.POSITION_DIRECTION_COMPASS the devices compass / gyroscope is used. If set to MapConfig.POSITION_DIRECTION_MOVEMENT the displayed direction will be calculated from consecutive position updates.

TiltWithSensor

Sets if the map should tilt automatically with the device orientation. Only performs the tilt if the position follow rotate mode is enabled.

AllGesturesEnabled

Sets whether the user can use gestures to control the camera.

ScrollGesturesEnabled

Sets whether the user can use gestures to scroll the camera.

RotateGesturesEnabled

Sets whether the user can use gestures to rotate the camera.

ZoomGesturesEnabled

Sets whether the user can use gestures to zoom the camera.

TiltGesturesEnabled

Sets whether the user can use gestures to tilt the camera.

FollowModeOnNavigationStart

Sets a value which indicates if the camera follow mode should automatically start after a navigation was started.

MarkerClusteringEnabled

This option enables the automatic show and hide of markers which overlap each other on the map.

MarkerClusteringOverlappingRatio

With this value you are able to specify how much the markers must overlap before they get hidden. 0 means that the clustering starts with the first overlapping pixel. 1 means that the markers must overlap to 100% before they will be hidden.

AssetTrackingEnabled

Sets whether the map should enable and visualize asset tracking. Make sure to set a valid user token in the ConnectionConfig or with the BackspinSdk.Assets.setUserToken method, as described in the SDK documentation.

AssetMarkerExpirationMillis

Sets an interval which defines when asset markers which were not updated any more get hidden or grayed.

AssetMarkerExpirationHandling

The expiration handling defines what should happen which asset markers which were not received anymore for some time. You ca choose between a gray out effect, hiding them or simply leave them on the map forever.

Image loading

Some of the UI elements like VenueMarker and MarkerDetailDefault depend on the loading of images from the web. There are several libraries available like Universal Image Loader, Picasso or Glide. To connect the MapFragment with the image loader from your app, you need to override the createImageLoader method, implement the MapImageLoader interface and return that instance. This interface provides methods for asynchronous and synchronous image requests. The following code snippet shows the method which needs to be overridden in the MapFragment and how the methods should be used:

@Override
protected MapImageLoader createImageLoader() {
    return new MapImageLoader() {
        @Override
        public void loadImageAsync(String url, int width, int height, BitmapLoadedListener loadedListener) {
            // use your custom image loader library and start a asynchronous request to get the image
            // call onBitmapLoaded on the listener to pass the loaded bitmap back to the map or call onBitmapFailed in case of an error
        }

        @Override
        public Bitmap loadImageSync(String url, int width, int height) {
            // use your custom image loader library and start synchronous request
            // finally return the loaded bitmap directly
            return bitmap;
        }
    };
}

Indoor Markers

IndoorMarker is the base class of all markers used by the map. It holds information about the geo coordinate and the level it belongs to. Use the following methods of the map fragment to manipulate the markers on the map:


getMarkers()

Returns all previously added IndoorMarker objects.

addMarker(IndoorMarker)

Add an IndoorMarker to the map. It will only be shown if the correct level is selected.

removeMarker(IndoorMarker)

Removes a previously added IndoorMarker from the map.

removeMarkers()

Removes all previously added IndoorMarker from the map.

refreshMarker(IndoorMarker)

Forces an IndoorMarker to redraw itself. Use this method whenever you change its state or visibility.

showMarkers()

Sets the visibility of all markers to true and refreshes them.

hideMarkers()

Sets the visibility of all markers to false and refreshes them.

hideMarkersExcept(IndoorMarker)

Sets the visibility of all markers except the specified ones to false and refreshes them.

showMarkers(VisibilityPredicate)

Control the visibility of each marker separately by passing a custom visibility predicate.

selectMarker(IndoorMarker)

Changes the state of the given marker to STATE_SELECTED and refreshes it. This also shows the marker detail view. Pass null to deselect the currently selected marker.

addVenueMarkers()

Adds a VenueMarker for each venue of the current root venue. By default a VenueCategoryMarker is created for each venue location but you can override createMarkerForVenueLocation(VenueLocation) to adjust this behavior.

An IndoorMarker can have one of the following states:

IndoorMarker.STATE_DEFAULT

This is the default state. The marker can be selected by the user or by calling selectMarker(IndoorMarker).

IndoorMarker.STATE_SELECTED

This is the selected state. It usually results in a highlighted visual representation.

IndoorMarker.STATE_DISABLED

This is the disabled state. Markers of this state can not be selected and are usually grayed out.

You can create your own markers by sub-classing IndoorMarker and implementing the loadBitmap(Context, BitmapLoadedListener) method. Pass a bitmap to the listener according to the markers state. This method is called whenever you add or refresh the marker.

There is also an already implemented VenueCategoryMarker which can be used for a convenient visual representation of a venue. It renders the logo of the first VenueCategory that is associated with the venue, together with its name below.

Marker Clustering

By clustering your markers, you can put a large number of markers on a map without making the map hard to read. If you have a lot of markers shown at nearly the same location the map starts to show and hide every marker automatically depending on your zoom, rotation and tilt. You can enable this feature with the following line:

@Override
protected void onConfiguration(MapConfig mapConfig) {
     super.onConfiguration(mapConfig);

     [...]
     mapConfig.setMarkerClusteringEnabled(true);
}

You can control the behavior of every marker independently by calling marker.setClusteringEnabled with true or false. By default new markers will be clustered automatically but the position and navigation path markers will not.

If the user selects a marker on the map the clustering will temporarily be disabled for that specific marker (it is always visible).

There is also an marker.getClusterCount() method which returns the amount of markers which belong to the current clustered one. The default VenueCircleMarker shows a small indicator at the right top of the image if the cluster count is greater than one. If you wrote your own markers you can use this to adjust the bitmap accordingly.

The marker.getClusterPriority() method is used by the clustering to decide which markers should be shown and which markers will be hidden. Markers with a lower priority will be hidden if a marker with a higher priority overlaps them. By default every VenueMarker sets its priority to the venues area size. All this is achieved by the following Comparator:

public Comparator<IndoorMarker> getClusteringComparator() {
    return new Comparator<IndoorMarker>() {
        @Override
        public int compare(IndoorMarker marker1, IndoorMarker marker2) {
            return Double.compare(marker2.getClusterPriority(), marker1.getClusterPriority());
        }
    };
}

You can always override this method inside the MapFragment if you want a more flexible solution.

Marker Detail View

Whenever a marker is selected, the SDK will show a UI element that provides further information about the selection.

The method createMarkerDetail() of the map fragment determines which marker detail view is used by the map. If you want to customize the existing implementation or inject a custom one you have to override it.

There are several constructor overloads which can be used to customize the colors of the MarkerDetailDefault. The default implementation looks like this:

protected MarkerDetail createMarkerDetail() {
    return new MarkerDetailDefault(this, this);
}

The MarkerDetailDefault has the ability to show information about VenueMarker instances, like the VenueCategoryMarker. If you want to use it to cover other markers in a similar way you have to subclass MarkerDetailDefault and override the apply(Position, IndoorMarker, boolean) method like this:

@Override
public void apply(Position position, IndoorMarker marker, boolean navigationEnabled) {

    if (marker instanceof MyCustomMarker) {
        mTitle.setText(...);
        mSubTitle.setText(...);
        mImageContainer.setVisibility(GONE);
        return;
    }

    super.apply(marker);
}


If you don’t want to use the MarkerDetailDefault you can create a custom class which implements the MarkerDetail interface. The following methods have to be implemented:

getView()

Returns the view that will be added to the map fragment layout.

apply(Position, IndoorMarker, boolean)

This method gets called when a marker is selected initially and every time the position gets updated. The third parameter comes from the MapConfig.isNavigationEnabled() setting. Update your view accordingly.

show()

This method gets called when the view should be shown.

hide()

This method gets called when the view should be hidden.

Camera

Use the following methods to control the camera:

moveCamera(CameraPosition)

Sets the camera to the given camera position.

animateCamera(CameraPosition, int)

Animates the camera to the given camera position. Specify the animation time in milliseconds.

getCameraLocation()

Returns the current camera position.

getCameraZoomLevel()

Returns the current zoom level of the camera.

moveCameraToPosition()

Sets the camera position to the current user’s indoor location, if there is one.

animateCameraToPosition()

Animates the camera position to the current user’s indoor location.

moveCameraAndSelectMarker(IndoorMarker)

Sets the camera position to the given marker’s indoor location and performs a marker selection.

animateCameraAndSelectMarker(IndoorMarker)

Animates the camera position to the given marker’s indoor location and performs a marker selection.

Position Follow Mode

The map has a position follow mode that can be enabled by using enablePositionFollowMode() or disabled by using disablePositionFollowMode(). While the position follow mode is enabled, the camera always animates to center the latest user position. The map will also perform a level switch, if needed. Any user interaction like gestures, a level switch or a marker selection will disable the position follow mode.

The position follow view allows the user to enable the position follow mode. The default implementation is a button that changes its color when the follow mode is enabled or disabled. It will only show itself when a user position is available.

The method createPositionFollow() of the map fragment determines which position follow view is used by the map. If you want to customize the existing position follow or inject a custom one you have to override it.

There is a constructor overload which can be used to customize the icon and colors of the PositionFollowDefault. The default implementation looks like this:

protected PositionFollow createPositionFollow() {
       return new PositionFollowDefault(getContext(), this);
}

If you don’t want to use the PositionFollowDefault you can create a custom class which implements the PositionFollow interface. The following methods have to be implemented:

getView()

Returns the view that will be added to the map fragment layout.

enable()

This method gets called when the position follow mode is enabled. Update your view to represent this accordingly.

disable()

This method gets called when the position follow mode is disabled. Update your view to represent this accordingly. It will also be called initially.

show()

This method gets called when the view should be shown.

hide()

This method gets called when the view should be hidden. It will also be called initially.

Levels

The Favendo Map needs at least one level. You can override the defineLevels() method to decide which levels are available to the map. By default the system gets all levels from the current root venue.

Level Switcher

This SDK also comes with a default level switcher UI element. It tells the user which level is currently displayed and where the user is currently located. When switching through the different levels all IndoorMarker instances will automatically be shown or hidden just as the level plan overlay.

The method createLevelSwitcher() of the map fragment determines which level switcher is used by the map. If you want to customize the existing level switcher or inject a custom one you have to override it.

There are several constructor overloads which can be used to customize the size and colors of the LevelSwitcherDefault. The default implementation looks like this:

protected LevelSwitcher createLevelSwitcher() {
  return new LevelSwitcherDefault(
              getContext(),
              ThemeColorUtil.fetchPrimaryColor(getContext()),
              ContextCompat.getColor(getContext(), R.color.level_switcher_foreground_selected));
}

If you don’t want to use the LevelSwitcherDefault you can create a custom class which implements the LevelSwitcher interface. The following methods have to be implemented:

getView()

Returns the view that will be added to the map fragment layout.

initialize(List<Level>, LevelSwitchListener)

This method gets called when the map initializes. Set up your custom switcher according to the given levels. Use the listener to start a level switch.

requestLevelSwitch(Level)

This method gets called when the map fragment is about to perform a level switch, either through your call on the listener or a switchLevel(Level) call on the map fragment. Update your UI accordingly.

finishedLevelSwitch(Level)

This method gets called when the map fragment finished a level switch. Update your UI accordingly.

addIndicator(LevelIndicator)

This method gets called whenever an indicator should be shown within the LevelSwitcher. Update your UI accordingly.

removeIndicator(LevelIndicator)

This method gets called whenever an indicator should be removed from the LevelSwitcher. Update your UI accordingly.

Level Indicators

Indicators help the user to keep track of several information on different levels, like their position, the navigation destination or associated assets. The LevelSwitcherDefault uses small icons to the left and to the right of the level number, but if you implemented a custom LevelSwitcher you are completely free in their visual representation.

There are two system indicators which are always added by default. One is for the current users position and one for its final navigation destination. If you want to add more indicators you can use the following builder:

LevelIndicator myIndicator = new LevelIndicator.Builder(this)
      .setLevel(1)
      .setAlignment(IndicatorAlignment.RIGHT)
      .setDrawable(R.drawable.custom_icon)
      .setZIndex(1f)
      .add();

The add() method returns a new instance of type LevelIndicator. Keep this instance if you want to remove it later. To achieve this you simply have to call the LevelSwitcher.removeIndicator(LevelIndicator) method.

Every Indicator also has an z-Index which specifies its visual priority. This is needed whenever multiple indicators are added on the same level and the same side. The system indicators for position and the navigation destination have a value of 0.5. If you choose an higher value the system indicator gets hidden and your icon will be shown instead of the system one. A new LevelIndicator will have a z-Index of 0 and therefore the system icons will have a higher priority by default.

Position Marker

The Favendo Map SDK automatically registers for position updates from the Backspin SDK and renders a position marker on the map.

If you want to customize what PositionMarker is used you can simply override the createPositionMarker(Position) method of the map fragment. This method gets called when the first position update is received.

The default implementation looks like this:

protected PositionDirectionMarker createPositionDirectionMarker(Position position, float bearing) {
    return new PositionDirectionMarkerDefault(
            position,
            bearing,
            GuiUtil.dipToPixel(getActivity(), 38),
            ThemeColorUtil.fetchAccentColor(getContext()));
}

Use PositionMarkerBitmap if you want to use a position marker with a custom bitmap.

If a position direction is displayed on the map a PositionDirectionMarker is used in addition to the PositionMaker. If you want to customize this simply override the createPositionDirectionMarker(Position, float) method of the map fragment. This method gets called when the first direction is received.

The default implementation looks like this:

protected PositionMarker createPositionMarker(Position position) {
    return new PositionMarkerDefault(
            position,
            GuiUtil.dipToPixel(getActivity(), 38),
            ThemeColorUtil.fetchAccentColor(getContext()));
}
Remember to start scanning and positioning of the Backspin SDK in order to receive position updates.

Navigation

One of the core features of the map is indoor routing / turn by turn navigation. It uses the navigation API provided by the Backspin SDK and displays the resulting NavigationRoute accordingly. The MarkerDetailDefault view already comes with an action button that triggers turn by turn navigation to the selected venue - if NavigationEnabled of the MapConfig is true. There is no further implementation effort needed if this behaviour suits your use case.

In any case, these methods of the map fragment are used to control the indoor navigation:

startNavigation(IndoorLocation)

Starts a new turn by turn navigation using the provided IndoorLocation as the destination. Stops the currently running navigation, if there is one.

startNavigation(Venue)

Starts a new turn by turn navigation using the provided Venue as the destination. Stops the currently running navigation, if there is one.

startNavigation(IndoorMarker)

Starts a new turn by turn navigation using the provided IndoorMarker as the destination. Stops the currently running navigation, if there is one.

startNavigation(NavigationTask)

Starts a new turn by turn navigation using the provided NavigationTask. Will be called by all other startNavigation() implementations. If you use this method directly, make sure to use the createNavigationTaskBuilder() method to create the NavigationTask.Builder - this is important because the Fragment needs to be set as a listener. Stops the currently running navigation, if there is one.

stopNavigation()

Stops the currently running navigation. NavigationInfo and TurnByTurn are hidden and all paths are cleared.

isNavigating()

Returns true if there is an active navigation.

IndoorPath

There is also an object to manage polylines on the map. An IndoorPath has many indoor locations which are connected via a simple line. This line can be customized in color and width.

IndoorPath myIndoorPath = new IndoorPath(locations, Color.BLACK);

Every path segment will automatically be shown or hidden according to the current level which is selected. You can also attach IndoorMarkers which are added and removed together with the whole IndoorPath object. To do so use the addPath(IndoorPath) method of your map fragment. You can also remove and refresh previously added paths via the according methods.

There are several methods which are involved when showing the path for a navigation route. By default the system creates one IndoorPath for an navigation which is started via the startNavigation(NavigationTask) method. A new path will be created and added inside the onRouteCalculated callback.

To customize the navigation path you can simply override the createNavigationPath() method. The default implementation looks like this:

protected IndoorPath createNavigationPath() {
    return new IndoorPath(
        GuiUtil.dipToPixel(getContext(), 4),
        ThemeColorUtil.fetchAccentColor(getContext()));
}

If you want to manually add an IndoorPath for any NavigationRoute you can simply call the addRoute(NavigationRoute) which then returns the created an added path.

I you changed locations or other fields of the IndoorPath you have to call refreshPaths() afterwards.

Navigation Info

The navigation info view provides the user information about the currently active navigation. The default implementation displays the name of the navigation target together with distance and time to destination. The view also includes a button that is used to stop the current navigation.

The method createNavigationInfo() of the map fragment determines which navigation info view is used by the map. If you want to customize the existing navigation info or inject a custom one you have to override it.

There is a constructor overload which can be used to customize the foreground and background color of the NavigationInfoDefault. The default implementation looks like this:

protected NavigationInfo createNavigationInfo() {
    return new NavigationInfoDefault(getContext(), this);
}

If you don’t want to use the NavigationInfoDefault you can create a custom class which implements the NavigationInfo interface. The following methods have to be implemented:

getView()

Returns the view that will be added to the map fragment layout.

apply(Position, String, NavigationRoute)

This method gets called when a new route has been calculated. Update the view with the information about the latest NavigationRoute. The second parameter is the name of the navigation target.

show()

This method gets called when the view should be shown.

hide()

This method gets called when the view should be hidden.

Turn By Turn

The turn by turn view provides the user information about the next directions of the currently active navigation.

The method createTurnByTurn() of the map fragment determines which turn by turn view is used by the map. If you want to customize the existing turn by turn or inject a custom one you have to override it and return the corresponding instance. By default the NoTurnByTurn instance is created which means that no turn by turn directions are shown.

If you want to show the turn by turn instructions you can use the default implementation which is called TurnByTurnDefault. This will display an arrow icon together with a short text to describe the next two directions. There are several constructor overloads which can be used to customize the colors. The default implementation looks like this:

protected TurnByTurn createTurnByTurn() {
       return new TurnByTurnDefault(getContext());
}

The following strings are used by the turn by turn UI but you can provide your own translations if needed.

strings.xml
<string name="direction_straight">Straight ahead</string>
<string name="direction_bear_left">Slightly left</string>
<string name="direction_left">Turn left</string>
<string name="direction_bear_right">Slightly right</string>
<string name="direction_right">Turn right</string>
<string name="direction_level_up">Change to %s</string>
<string name="direction_level_down">Change to %s</string>
<string name="direction_destination_reached">Destination reached</stri

If you don’t want to use the TurnByTurnDefault you can create a custom class which implements the TurnByTurn interface. The following methods have to be implemented:

getView()

Returns the view that will be added to the map fragment layout.

apply(NavigationDirection, NavigationDirection)

This method gets called when a new route has been calculated. Update the view with the information about the next two navigation directions.

show()

This method gets called when the view should be shown.

hide()

This method gets called when the view should be hidden.

Asset Tracking

This feature is deprecated and should not be used anymore.

When enabled in the MapConfig the map takes care of displaying assets and receiving live position updates. Everything that is asset tracking related is controlled by the AssetMapComponent. The default implementation inside the map fragment looks like this:

protected AssetMapComponent createAssetComponent() {
     return new AssetMapComponent(this);
}
Make sure to set a valid user token in the ConnectionConfig or with the BackspinSdk.Assets.setUserToken method.

If you want to customize the asset tracking inside the map you have to extend the AssetMapComponent class and return it by overriding the createAssetComponent() method in the map fragment:

@Override
protected AssetMapComponent createAssetComponent() {
    return new CustomAssetMapComponent(this);
}

A custom map AssetMapComponent that changes the visualization of the asset markers could look like this:

public class CustomAssetMapComponent extends AssetMapComponent {

    public CustomAssetMapComponent(BaseMapFragment mapFragment) {
        super(mapFragment);
    }

    @Override
    public AssetMarker createAssetMarker(@NotNull Asset asset) {
        return new CustomAssetMarker();
    }
}