mapbox / mapbox-navigation-android

Mapbox Navigation SDK for Android
https://docs.mapbox.com/android/navigation/overview/
Other
622 stars 319 forks source link

Rendering the route line requires layer specification #4474

Open kmadsen opened 3 years ago

kmadsen commented 3 years ago

Opening this issue as there are a few places where it does not work by default, and it seems there are many approaches to get the same answer. Each of which needs to specify relative layers.

  private val maneuverArrowBinder: ManeuverArrowBinder by lazy {
    val routeArrowOptions = RouteArrowOptions.Builder(this.requireContext())
            .withAboveLayerId("road-exit-shield")
            .build()
    val routeArrowView = MapboxRouteArrowView(routeArrowOptions)
    val routeArrowApi = MapboxRouteArrowApi()
    ManeuverArrowBinder(
            this,
            routeArrowApi,
            routeArrowView,
            routesViewModel
    )
  }
  private val options: MapboxRouteLineOptions by lazy {
    MapboxRouteLineOptions.Builder(activity)
            .withRouteLineResources(routeLineResources)
            .withRouteLineBelowLayerId("road-label")
            .build()
  }
    private val routeArrowOptions by lazy {
        RouteArrowOptions.Builder(mainCarContext.carContext)
            .withAboveLayerId(PRIMARY_ROUTE_TRAFFIC_LAYER_ID)
            .build()
    }

cc: @cafesilencio

cafesilencio commented 3 years ago

It was an intentional decision to require the developer to specify the layer ID's. There's no reliable default as there was in 1.x. If the specified layer ID is not present at the time of layer initialization the layer will get placed at the top of the stack. This is done to make it clear to the developer that they need to specify the layer ID and initialize the layers at an appropriate time in their activity or fragment.

kmadsen commented 3 years ago

It was an intentional decision to require the developer to specify the layer ID's. There's no reliable default as there was in 1.x. If the specified layer ID is not present at the time of layer initialization the layer will get placed at the top of the stack. This is done to make it clear to the developer that they need to specify the layer ID and initialize the layers at an appropriate time in their activity or fragment.

It's only clear that we need to specify the layer, because it doesn't work otherwise. The issue is, the default configuration doesn't work. We're discussing if a configuration is correct, but it was copied from the examples. Considering that a fair reason to offer feedback on the api design.

For example, it seems like the route arrow should always be on top of the route line. Should the route arrow detect if there is no route line? We could support drawing the route arrow below the route line, but you need to specify that yourself. Optimizing for, change configuration to break it. Instead of, change configuration to make it work.

sandyscoffable commented 3 years ago

I'm going to slightly hijack this issue as it's related to a nuisance problem that I currently have.

I've got route line code like:

    MapboxRouteLineOptions.Builder(requireContext())
            .withRouteLineBelowLayerId("road-label")
            .build()

Which you'd expect to render the route line below the road labels, but I get the following:

I've tried the following:

But the position of the route line in the layer stack doesn't seem to change.

It's a pain in the chops as I can't find any documentation to determine what the layer stack is. I tried using the Mapbox style editor and collected the labels from that (I'm just using vanilla Mapbox traffic styles).

Does this actually work?

cafesilencio commented 3 years ago

Since maps styles are customizable and are often customized by developers the Navigation SDK can't make any assumptions about what layers are present and where the route line layer should be placed.

The typical Mapbox maps used for navigation apps will have a "road-label" layer. However timing is important. When the route line API initializes it will look for the layer specified in options and if it's not present the layer gets put on the top of the stack. It's important to initialize the maps style first so that all of the map related layers are present before initializing the route line API.

Here is a code snippet to enumerate all of the map layers:

        mapboxMap.loadStyleUri(
            Style.MAPBOX_STREETS,
            { style: Style ->

                style.styleLayers.forEach {
                    Log.e("someTag", it.id)
                }
...

There are two ways the route line related layers are initialized. The first is any call to one of the MapboxRouteLineView render methods will check if the route line related layers are present and if not it will create them. If one of these calls is made before mapboxMap.loadStyleUri then it's very likely the aboveLayerId won't be present and the route line related layers will be put at the top of the stack. The other way to initialize the route line related layers is to call MapboxRouteLineView::initializeLayers. Some developers will call this inside of the the callback for `MapboxMap::loadStyleUri' in order to have better control over the timing.

sandyscoffable commented 3 years ago

Thanks, I was triggering the style load and then immediately initialising the route line 👍 . The label I was looking for was road-label-primary. road-label doesn't seem to exist 🤷

Thanks again for your help 👍