How do I set icon type from local geojson properties using Mapbox Android SKD

Viewed 647

I have a nice map with icon markers and a recyclerview setup using Mapbox. It is similar to this:

https://docs.mapbox.com/android/maps/examples/recyclerview-interaction

My problem is that I can not figure out how to set the icons according to my local json property. I have in total around 40 different icons I need to use so I thought the best place to set that would be in my geojson properties. I have the png files for my icons in my drawable-xxhdpi folder. Here is what my local geojson file looks like:

{
  "type": "Feature",
  "properties": {
    "name": "Broadway",
    "icon": "route_icon_1",
    "hours": "V1",
    "phone": "One Star Rating",
    "description": "Located at Lower Point Boulders"
  },
  "geometry": {
    "type": "Point",
    "coordinates": [
      -86.311166,
      33.919872
    ]
  }
},
{
  "type": "Feature",
  "properties": {
    "name": "Return of the Jedi",
    "icon": "route_icon_2",
    "hours": "V7",
    "phone": "One Star Rating",
    "description": "Located at Lower Point Boulders"
  },
  "geometry": {
    "type": "Point",
    "coordinates": [
      -86.311174,
      33.919883
    ]
  }
},

I'm accessing the geojson file just fine and everything is running perfectly, but I can not seem to set my icon according to the "icon" property in the geojson file. Is this even possible? I apologize for the length of my code. This is my first time asking a question on here and I wasn't sure how much of the code I should post.

Here is my code in my map activity:

class MapActivity : AppCompatActivity(), OnMapReadyCallback, LocationRecyclerViewAdapter.ClickListener,
MapboxMap.OnMapClickListener {
private var featureCollection: FeatureCollection? = null
private var mapboxMap: MapboxMap? = null
private var mapView: MapView? = null
private var locationsRecyclerView: RecyclerView? = null
private var listOfIndividualLocations: ArrayList<IndividualLocation>? = null
private var styleRvAdapter: LocationRecyclerViewAdapter? = null

private var markerSelected = false
private var markerAnimator: ValueAnimator? = null

private var selectedMarkerIcon: Bitmap? = null
private var unselectedMarkerIcon: Bitmap? = null

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // Configure the Mapbox access token. Configuration can either be called in your application
    // class or in the same activity which contains the mapview.
    Mapbox.getInstance(this, getString(R.string.access_token))

    // Hide the status bar for the map to fill the entire screen
    @Suppress("DEPRECATION")
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
        window.insetsController?.hide(WindowInsets.Type.statusBars())
    } else {
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
    }

    // Inflate the layout with the the MapView. Always inflate this after the Mapbox access token is configured.
    setContentView(R.layout.activity_map)

    // Create a GeoJSON feature collection from the GeoJSON file in the assets folder.
    try {
        featureCollectionFromJson
    } catch (exception: Exception) {
        Log.e("MapActivity", "onCreate: $exception")
        Toast.makeText(this, R.string.failure_to_load_file, Toast.LENGTH_LONG).show()
    }

    // Initialize a list of IndividualLocation objects for future use with recyclerview
    listOfIndividualLocations = ArrayList()

    // Set up the Mapbox map
    mapView = findViewById(R.id.mapView)
    mapView?.onCreate(savedInstanceState)
    mapView?.getMapAsync(this)
}

 override fun onMapReady(mapboxMap: MapboxMap) {
     this.mapboxMap = mapboxMap
     // Initialize the custom class that handles marker icon creation and map styling based on the selected theme



     selectedMarkerIcon = BitmapFactory.decodeResource(
         resources,
         R.drawable.basic_red_marker
     )


     unselectedMarkerIcon = BitmapFactory.decodeResource(
         resources,
         R.drawable.basic_red_marker
     )


     mapboxMap.setStyle(
         Style.MAPBOX_STREETS
     )

     { style ->
         // Setting the returned mapboxMap object (directly above) equal to the "globally declared" one
         this@MapActivity.mapboxMap = mapboxMap

         mapboxMap.addOnMapClickListener(this)

         val quad = LatLngQuad(
             LatLng(33.92307338188975, -86.31129446659145),
             LatLng(33.92307338188975, -86.30688718139171),
             LatLng(33.91948714730282, -86.30688718139171),
             LatLng(33.91948714730282, -86.31129446659145)
         )

         val quad2 = LatLngQuad(
             LatLng(33.921356, -86.307309),
             LatLng(33.921356, -86.307156),
             LatLng(33.921200, -86.307156),
             LatLng(33.921200, -86.307309)
         )

         // Add an ImageSource to the map
         style.addSource(ImageSource(ID_IMAGE_SOURCE, quad, R.drawable.asset14))
         style.addSource(ImageSource(ID_IMAGE_SOURCE_VANDALA, quad2, R.drawable.vandala_overlay_sm))
         // Create a raster layer and use the imageSource's ID as the layer's data. Then add a RasterLayer to the map.
         style.addLayerBelow(
             RasterLayer(ID_IMAGE_LAYER, ID_IMAGE_SOURCE),
             LAYER_ID
         )
         style.addLayerAbove(
             RasterLayer(ID_IMAGE_LAYER_VANDALA, ID_IMAGE_SOURCE_VANDALA),
             ID_IMAGE_LAYER
         )

         // Set bounds for the map camera so that the user can't pan the map outside of the NYC area
         mapboxMap.setLatLngBoundsForCameraTarget(LOCKED_MAP_CAMERA_BOUNDS)

         // Set up the SymbolLayer which will show the icons for each route location
         initRouteLocationIconSymbolLayer()

         // Set up the SymbolLayer which will show the selected route icon
         initSelectedRouteSymbolLayer()

         // Create a list of features from the feature collection
         if (featureCollection != null) {
             val featureList: List<Feature> = featureCollection?.features() as List<Feature>

             // Retrieve and update the source designated for showing the route location icons
             mapboxMap.style?.getSourceAs<GeoJsonSource?>(SOURCE_ID)!!
                 .setGeoJson(FeatureCollection.fromFeatures(featureList))
             for (x in featureList.indices) {
                 val singleLocation: Feature = featureList[x]

                 // Get the single location's String properties to place in its map marker
                 val singleLocationName: String =
                     singleLocation.getStringProperty("name")
                 val singleLocationRouteIcon: String =
                     singleLocation.getStringProperty("icon")
                 Log.e("tag", singleLocationRouteIcon)
                 val singleLocationHours: String =
                     singleLocation.getStringProperty("hours")
                 val singleLocationDescription: String =
                     singleLocation.getStringProperty("description")
                 val singleLocationPhoneNum: String =
                     singleLocation.getStringProperty("phone")


                 // Add a boolean property to use for adjusting the icon of the selected route location
                 singleLocation.addBooleanProperty(PROPERTY_SELECTED, false)

                 // Get the single location's LatLng coordinates
                 val singleLocationPosition: Point = singleLocation.geometry() as Point

                 // Create a new LatLng object with the Position object created above
                 val singleLocationLatLng = LatLng(
                     singleLocationPosition.latitude(),
                     singleLocationPosition.longitude()
                 )

                 // Add the location to the Arraylist of locations for later use in the recyclerview
                 listOfIndividualLocations!!.add(
                     IndividualLocation(
                         singleLocationName,
                         singleLocationRouteIcon,
                         singleLocationDescription,
                         singleLocationHours,
                         singleLocationPhoneNum,
                         singleLocationLatLng
                     )
                 )
             }
         }

         // the Maps SDK's LocationComponent can be used to easily display and customize
         // the device location's puck
         setUpRecyclerViewOfLocationCards()
         Toast.makeText(this@MapActivity, "Click on a card", Toast.LENGTH_SHORT)
             .show()
     }
 }


 override fun onMapClick(point: LatLng): Boolean {
    Log.e("tag", "onMapClick")
    val style = mapboxMap!!.style
    if (style != null) {
        val selectedMarkerSymbolLayer = style.getLayer("selected-route-location-layer-id") as SymbolLayer?
        val pixel = mapboxMap!!.projection.toScreenLocation(point)
        val features = mapboxMap!!.queryRenderedFeatures(pixel, "route-location-layer-id")
        val selectedFeature = mapboxMap!!.queryRenderedFeatures(
            pixel, "selected-route-location-layer-id"
        )
        if (selectedFeature.size > 0 && markerSelected) {
            Log.e(
                "tag",
                "1 onMapClick: selectedFeature.size > 0 && markerSelected ${selectedFeature.size} $markerSelected"
            )
            return false
        }
        if (features.isEmpty()) {
            Log.e("tag", "2 onMapClick: features.isEmpty")
            if (markerSelected) {
                Log.e("tag", "selectMarker = true")
                deselectMarker(selectedMarkerSymbolLayer!!)
            }
            return false
        }
        val source = style.getSourceAs<GeoJsonSource>(ID_SELECTED_ROUTE_ICON)
        source?.setGeoJson(
            FeatureCollection.fromFeatures(
                arrayOf(
                    Feature.fromGeometry(
                        features[0].geometry()
                    )
                )
            )
        )
        if (markerSelected) {
            Log.e("tag", "3 onMapClick: selectMarker")
            deselectMarker(selectedMarkerSymbolLayer!!)
        }
        if (features.size > 0) {
            Log.e("tag", "4 onMapClick: features.size > 0")
            Log.e("tag", "4 onMapClick: features.size = ${features.size}")
            selectMarker(selectedMarkerSymbolLayer!!)
        }
    }
    handleClickIcon(mapboxMap?.projection!!.toScreenLocation(point))
    return true
}


 private fun handleClickIcon(screenPoint: PointF): Boolean {
    Log.e("tag", "handleClickIcon")
    val features: List<Feature> = mapboxMap?.queryRenderedFeatures(
        screenPoint,
        LAYER_ID,
        "route-location-layer-id"
    ) as List<Feature>

    return if (features.isNotEmpty()) {
        Log.e("tag", "1 handleClickIcon: features.isNotEmpty")
        val name: String = features[0].getStringProperty("name")
        val featureList: List<Feature> = featureCollection?.features() as List<Feature>
        for (i in featureList.indices) {
            if (featureList[i].getStringProperty("name") == name) {
                Log.e("tag", "(${featureList[i].getStringProperty("name")} == $name)")
                val selectedFeaturePoint: Point = featureList[i].geometry() as Point
                Log.e("tag", "handleClickIcon: featureSelectStatus = ${featureSelectStatus(i)}")

                if (featureSelectStatus(i)) {
                    Log.e("tag", "${featureSelectStatus(i)}")
                    Log.e(
                        "tag", "2 handleClickIcon: featureSelectStatus = ${
                            featureSelectStatus(
                                i
                            )
                        }"
                    )
                    setFeatureSelectState(featureList[i], true)
                    Log.e(
                        "tag",
                        "2 handleClickIcon: setFeatureSelectState(featureList $i: selectedState set to false)"
                    )

                } else {
                    Log.e(
                        "tag", "3 handleClickIcon: featureSelectStatus = ${
                            featureSelectStatus(
                                i
                            )
                        }"
                    )
                    setSelected(i)
                }
                if (selectedFeaturePoint.latitude() != MOCK_DEVICE_LOCATION_LAT_LNG.latitude) {
                    Log.e(
                        "tag",
                        "selectedFeaturePoint.latitude != MOCK_DEVICE_LOCATION_LAT_LNG.latitude"
                    )
                    for (x in 0 until featureCollection!!.features()!!.size) {
                        if (listOfIndividualLocations!![x].getLocation()?.latitude == selectedFeaturePoint.latitude()) {
                            // Scroll the recyclerview to the selected marker's card. It's "x-1" below because
                            // the mock device location marker is part of the marker list but doesn't have its own card
                            // in the actual recyclerview.
                            locationsRecyclerView!!.smoothScrollToPosition(x)
                            Log.e("tag", "smoothScrollToPosition")
                        }
                    }
                }
            }
            else
            {
                setFeatureSelectState(featureList[i], false)
                Log.e("tag", "4 handleClickIcon: (${featureList[i]}")
            }
        }
        true
    }
    else
    {
        false
    }
}

 private fun selectMarker(iconLayer: SymbolLayer) {
    Log.e("tag", "selectMarker: markerSelect set to true")
    markerAnimator = ValueAnimator()
    markerAnimator!!.setObjectValues(1f, 2f)
    markerAnimator!!.duration = 300
    markerAnimator!!.addUpdateListener { animator ->
        iconLayer.setProperties(
            iconSize(animator.animatedValue as Float)
        )
    }
    markerAnimator!!.start()
    markerSelected = true
}


 private fun deselectMarker(iconLayer: SymbolLayer) {
    Log.e("tag", "deselectMarker: markerSelect set to false")
    markerAnimator!!.setObjectValues(1.5f, 1f)
    markerAnimator!!.duration = 300
    markerAnimator!!.addUpdateListener { animator ->
        iconLayer.setProperties(
            iconSize(animator.animatedValue as Float)
        )
    }
    markerAnimator!!.start()
    markerSelected = false
}

/**
 * The LocationRecyclerViewAdapter's interface which listens to clicks on each location's card
 *
 * @param position the clicked card's position/index in the overall list of cards
 */
override fun onItemClick(position: Int) {
    Log.e("tag", "itemClick")

    // Get the selected individual location via its card's position in the recyclerview of cards
    val selectedLocation: IndividualLocation = listOfIndividualLocations!![position]

    // Evaluate each Feature's "select state" to appropriately style the location's icon
    val featureList: List<Feature> = featureCollection?.features() as List<Feature>
    val selectedLocationPoint: Point =
        featureCollection!!.features()?.get(position)?.geometry() as Point
    for (i in featureList.indices) {
        if (featureList[i].getStringProperty("name") == selectedLocation.name) {
            Log.e(
                "tag",
                "(${featureList[i].getStringProperty("name")} == ${selectedLocation.name})"
            )

            if (featureSelectStatus(i)) {
                Log.e("tag", "1 featureSelectStatus = ${featureSelectStatus(i)}")
                // setFeatureSelectState(featureList[i], false)
                Toast.makeText(
                    applicationContext,
                    "Go to ${selectedLocation.name} Details Activity",
                    Toast.LENGTH_LONG
                ).show()
                Log.e("tag", "Go to ${selectedLocation.name} Details Activity")
            } else {
                Log.e("tag", "2 itemClick: setSelected $i")
                setSelected(i)
            }
        } else {
            setFeatureSelectState(featureList[i], false)
            Log.e("tag", "3 itemClick: featureList $i: selectedState false")
        }
    }

    selectedLocation.getLocation()?.let { onCardClick(it) }
    // Reposition the map camera target to the selected marker
    repositionMapCamera(selectedLocationPoint)
}

    private fun onCardClick(point: LatLng): Boolean {
    Log.e("tag", "onCardClick")
    val style = mapboxMap!!.style
    if (style != null) {
        val selectedMarkerSymbolLayer = style.getLayer("selected-route-location-layer-id") as SymbolLayer?
        val pixel = mapboxMap!!.projection.toScreenLocation(point)
        val features = mapboxMap!!.queryRenderedFeatures(pixel, "route-location-layer-id")
        val selectedFeature = mapboxMap!!.queryRenderedFeatures(
            pixel, "selected-route-location-layer-id"
        )
        if (selectedFeature.size > 0 && markerSelected) {
            Log.e(
                "tag",
                "1 onCardClick: selectedFeature.size > 0 && markerSelected ${selectedFeature.size} $markerSelected"
            )
            return false
        }
        if (features.isEmpty()) {
            Log.e("tag", "2 onCardClick: features.isEmpty")
            if (markerSelected) {
                Log.e("tag", "Do Nothing")
            return false
            }
            else {
                Log.e("tag", "markerSelected = $markerSelected")
                selectMarker(selectedMarkerSymbolLayer!!)
            }
            return false
        }
        val source = style.getSourceAs<GeoJsonSource>(ID_SELECTED_ROUTE_ICON)
        source?.setGeoJson(
            FeatureCollection.fromFeatures(
                arrayOf(
                    Feature.fromGeometry(
                        features[0].geometry()
                    )
                )
            )
        )
        if (markerSelected) {
            Log.e("tag", "3 onCardClick: markerSelected")
            deselectMarker(selectedMarkerSymbolLayer!!)
        }
        if (features.size > 0) {
            Log.e("tag", "4 onCardClick: features.size > 0")
            Log.e("tag", "4 onCardClick: features.size = ${features.size}")
            selectMarker(selectedMarkerSymbolLayer!!)
        }
    }
    return true
}

/**
 * Adds a SymbolLayer which will show all of the location's icons
 */
private fun initRouteLocationIconSymbolLayer() {
    val style: Style? = mapboxMap?.style
    if (style != null) {
        // Add the icon image to the map
        style.addImage(
            ID_ROUTE_ICON, unselectedMarkerIcon as Bitmap
        )

        style.transition = TransitionOptions(0, 0, false)

        // Create and add the GeoJsonSource to the map
        val routeLocationGeoJsonSource = GeoJsonSource(SOURCE_ID)
        style.addSource(routeLocationGeoJsonSource)

        // Create and add the route location icon SymbolLayer to the map
        val routeLocationSymbolLayer = SymbolLayer(
            "route-location-layer-id",
            SOURCE_ID
        )
        routeLocationSymbolLayer.withProperties(
            iconImage(ID_ROUTE_ICON),
            iconAllowOverlap(true),
            iconOffset(arrayOf(0f, -9f))
        )
        style.addLayer(routeLocationSymbolLayer)
    } else {
        Log.d("RouteFinderActivity", "initRouteLocationIconSymbolLayer: Style isn't ready yet.")
        throw IllegalStateException("Style isn't ready yet.")
    }
}

/**
 * Adds a SymbolLayer which will show the selected location's icon
 */
private fun initSelectedRouteSymbolLayer() {
    val style: Style? = mapboxMap?.style
    if (style != null) {

        // Add the icon image to the map
        style.addImage(
            ID_SELECTED_ROUTE_ICON, selectedMarkerIcon as Bitmap
        )

        // Create and add the route location icon SymbolLayer to the map
        val selectedRouteLocationSymbolLayer = SymbolLayer(
            "selected-route-location-layer-id",
            SOURCE_ID
        )
        selectedRouteLocationSymbolLayer.withProperties(
            iconImage(ID_SELECTED_ROUTE_ICON),
            iconOffset(arrayOf(0f, -9f)),
            iconAllowOverlap(true)
        )
        selectedRouteLocationSymbolLayer.withFilter(eq(get(PROPERTY_SELECTED), literal(true)))
        style.addLayer(selectedRouteLocationSymbolLayer)
    } else {
        Log.d("RouteFinderActivity", "initSelectedRouteSymbolLayer: Style isn't ready yet.")
        throw IllegalStateException("Style isn't ready yet.")
    }
}

/**
 * Checks whether a Feature's boolean "selected" property is true or false
 *
 * @param index the specific Feature's index position in the FeatureCollection's list of Features.
 * @return true if "selected" is true. False if the boolean property is false.
 */
private fun featureSelectStatus(index: Int): Boolean {
    return if (featureCollection == null) {
        false
    } else featureCollection!!.features()?.get(index)?.getBooleanProperty(PROPERTY_SELECTED)!!
}

/**
 * Set a feature selected state.
 *
 * @param index the index of selected feature
                 */
private fun setSelected(index: Int) {
    Log.e("tag", "setSelected")
    val feature: Feature = featureCollection?.features()!![index]
    setFeatureSelectState(feature, true)
    Log.e(
        "tag",
        "setSelected: setFeatureSelectedState(feature = ${featureCollection?.features()!![index]}, selectedState set to true"
    )

    refreshSource()
}

/**
 * Selects the state of a feature
 *
 * @param feature the feature to be selected.
 */
private fun setFeatureSelectState(feature: Feature, selectedState: Boolean) {
    feature.properties()?.addProperty(PROPERTY_SELECTED, selectedState)
    refreshSource()
}

/**
 * Updates the display of data on the map after the FeatureCollection has been modified
 */
private fun refreshSource() {
    val source: GeoJsonSource? = mapboxMap?.style?.getSourceAs(SOURCE_ID)
    if (source != null && featureCollection != null) {
        source.setGeoJson(featureCollection)
    }
}

private fun repositionMapCamera(newTarget: Point) {
    val newCameraPosition: CameraPosition = CameraPosition.Builder()
        .target(LatLng(newTarget.latitude(), newTarget.longitude()))
        .zoom(22.0)
        .build()
    mapboxMap?.animateCamera(
        CameraUpdateFactory.newCameraPosition(newCameraPosition),
        CAMERA_MOVEMENT_SPEED_IN_MILSECS
    )
}

// Use fromJson() method to convert the GeoJSON file into a usable FeatureCollection object
@get:Throws(IOException::class)
private val featureCollectionFromJson: Unit
    get() {
        try {
            // Use fromJson() method to convert the GeoJSON file into a usable FeatureCollection object
            featureCollection = "list_of_locations.geojson".loadGeoJsonFromAsset()?.let {
                FeatureCollection.fromJson(
                    it
                )
            }
        } catch (exception: Exception) {
            Log.e("MapActivity", "getFeatureCollectionFromJson: $exception")
        }
    }

private fun String.loadGeoJsonFromAsset(): String? {
    Log.e("tag", "loadGeoJsonFromAsset")
    return try {
        // Load the GeoJSON file from the local asset folder
        val `is`: InputStream = assets.open(this)
        val size: Int = `is`.available()
        val buffer = ByteArray(size)
        `is`.read(buffer)
        `is`.close()
        String(buffer, charset("UTF-8"))
    } catch (exception: Exception) {
        Log.e("MapActivity", "Exception Loading GeoJSON: $exception")
        exception.printStackTrace()
        null
    }
}

private fun setUpRecyclerViewOfLocationCards() {
    Log.e("tag", "seUpRecyclerViewOfLocationCards")
    // Initialize the recyclerview of location cards and a custom class for automatic card scrolling
    locationsRecyclerView = findViewById(R.id.map_layout_rv)
    locationsRecyclerView?.setHasFixedSize(true)
    locationsRecyclerView?.layoutManager = LinearLayoutManagerWithSmoothScroller(this)
    styleRvAdapter = listOfIndividualLocations?.let {
        LocationRecyclerViewAdapter(
            it,
            applicationContext, this
        )
    }
    locationsRecyclerView?.adapter = styleRvAdapter
    val snapHelper: SnapHelper = LinearSnapHelper()
    snapHelper.attachToRecyclerView(locationsRecyclerView)
}

// Add the mapView's lifecycle to the activity's lifecycle methods
public override fun onResume() {
    super.onResume()
    mapView?.onResume()
}

override fun onStart() {
    super.onStart()
    mapView?.onStart()
}

override fun onStop() {
    super.onStop()
    mapView?.onStop()
}

public override fun onPause() {
    super.onPause()
    mapView?.onPause()
}

override fun onLowMemory() {
    super.onLowMemory()
    mapView?.onLowMemory()
}

override fun onDestroy() {
    super.onDestroy()
    mapView?.onDestroy()
}

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    mapView?.onSaveInstanceState(outState)
}

companion object {

    private val LOCKED_MAP_CAMERA_BOUNDS = LatLngBounds.Builder()
        .include(LatLng(33.92307338188975, -86.30688718139171))
        .include(
            LatLng(
                33.91948714730282,
                -86.31129446659145
            )
        ).build()

    private val MOCK_DEVICE_LOCATION_LAT_LNG: LatLng =
        LatLng(33.921466408978034, -86.30824978533974)
    private const val CAMERA_MOVEMENT_SPEED_IN_MILSECS = 1200
    private const val PROPERTY_SELECTED = "selected"
    private const val ID_ROUTE_ICON = "route-icon-id"
    private const val ID_SELECTED_ROUTE_ICON = "selected-route-icon-id"
    private const val LAYER_ID = "LAYER_ID"
    private const val ID_IMAGE_SOURCE = "image_source-id"
    private const val ID_IMAGE_LAYER = "image_layer-id"
    private const val ID_IMAGE_SOURCE_VANDALA = "image_source-id_vandala"
    private const val ID_IMAGE_LAYER_VANDALA = "image_layer-id_vandala"
    private const val SOURCE_ID = "route-location-source-id"
}

}

Thank you in advance.

1 Answers

You are almost there. You need a bit of code more.

On your function initRouteLocationIconSymbolLayer(), you have the following code:

routeLocationSymbolLayer.withProperties(
            iconImage(ID_ROUTE_ICON), // <---- Here is where you have to change
            iconAllowOverlap(true),
            iconOffset(arrayOf(0f, -9f))
        )

You have only set your layers to have one icon. The MapBoxSDK allows you to play with the properties of the geojson file.

So, here we need to change how the icon image is generated for each type of icon

routeLocationSymbolLayer
     .withProperties(
         /* iconImage constructor, with some query-like code */
         iconImage(
              match(
                 get("icon"), // JSON Key to evaluate
                 literal("route_icon_1"), // default
                 stop("route_icon_1", "route_icon_1"), // stop(input,output)
                 stop("route_icon_2", "route_icon_2")
              )
         ),
        /*the remaining properties you declared*/
        iconAllowOverlap(true),
        iconOffset(arrayOf(0f, -9f)
)

Source: MapBox Android SDK Example

Related