mapbox / mapbox/mapbox-navigation-android
routeArrowView not showing; no route progress; camera is incorrectly centered (on fragment)
Nessuno ha ancora preso questa issue.
- Lingua principale
- Kotlin
- Stelle
- 651
- Fork
- 321
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Descrizione
Android API: - 28
Mapbox Navigation SDK version: - 'com.mapbox.navigation:android:2.0.0-rc.4'
I am implementing navigation on fragment and after migrating from version 1.x to version 2.x. I'm having trouble with the new routeArrowView and routeLineView:
1.routeArrowView not showing on map
2.the route line does not disappear ignoring the parameter: withVanishingRouteLineEnabled (true)
3.camera is incorrectly centered

I attach all the parts of the code related to the Mapbox configuration.
Click to expand the code
` class NavigationViewFragment : Fragment() {private lateinit var maneuverApi: MapboxManeuverApi
// Mapbox Maps components
private lateinit var mapboxMap: MapboxMap
// Mapbox Navigation components
private lateinit var mapboxNavigation: MapboxNavigation
// camera
private lateinit var navigationCamera: NavigationCamera
private lateinit var viewportDataSource: MapboxNavigationViewportDataSource
private val pixelDensity = Resources.getSystem().displayMetrics.density
private val overviewPadding: EdgeInsets by lazy {
EdgeInsets(
140.0 * pixelDensity,
40.0 * pixelDensity,
120.0 * pixelDensity,
40.0 * pixelDensity
)
}
private val landscapeOverviewPadding: EdgeInsets by lazy {
EdgeInsets(
30.0 * pixelDensity,
380.0 * pixelDensity,
20.0 * pixelDensity,
20.0 * pixelDensity
)
}
private val followingPadding: EdgeInsets by lazy {
EdgeInsets(
180.0 * pixelDensity,
40.0 * pixelDensity,
150.0 * pixelDensity,
40.0 * pixelDensity
)
}
private val landscapeFollowingPadding: EdgeInsets by lazy {
EdgeInsets(
30.0 * pixelDensity,
380.0 * pixelDensity,
110.0 * pixelDensity,
40.0 * pixelDensity
)
}
// trip progress bottom view
private lateinit var tripProgressApi: MapboxTripProgressApi
// route line
private lateinit var routeLineAPI: MapboxRouteLineApi
private lateinit var routeLineView: MapboxRouteLineView
private lateinit var routeArrowView: MapboxRouteArrowView
private val routeArrowAPI: MapboxRouteArrowApi = MapboxRouteArrowApi()
private val navigationLocationProvider = NavigationLocationProvider()
/* ----- Location and route progress callbacks ----- */
private val locationObserver = object : LocationObserver {
var firstLocationUpdateReceived = false
override fun onNewRawLocation(rawLocation: Location) {
// not handled
}
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
val enhancedLocation = locationMatcherResult.enhancedLocation
currentPosition = LatLng(enhancedLocation.latitude, enhancedLocation.longitude)
currentBearing = enhancedLocation.bearing
// update location puck's position on the map
navigationLocationProvider.changePosition(
location = enhancedLocation,
keyPoints = locationMatcherResult.keyPoints,
)
// update camera position to account for new location
viewportDataSource.onLocationChanged(enhancedLocation)
viewportDataSource.evaluate()
// if this is the first location update the activity has received,
// it's best to immediately move the camera to the current user location
if (!firstLocationUpdateReceived) {
firstLocationUpdateReceived = true
navigationCamera.requestNavigationCameraToOverview(
stateTransitionOptions = NavigationCameraTransitionOptions.Builder()
.maxDuration(0) // instant transition
.build()
)
}
}
}
private val routeProgressObserver = RouteProgressObserver { routeProgress ->
Log.d("test_nav", "routeProgressObserver: ${routeProgress.currentState.name}")
// update the camera position to account for the progressed fragment of the route
viewportDataSource.onRouteProgressChanged(routeProgress)
viewportDataSource.evaluate()
// draw the upcoming maneuver arrow on the map
val style = mapboxMap.getStyle()
if (style != null) {
val maneuverArrowResult = routeArrowAPI.addUpcomingManeuverArrow(routeProgress)
routeArrowView.renderManeuverUpdate(style, maneuverArrowResult)
}
// update top banner with maneuver instructions
val maneuvers = maneuverApi.getManeuvers(routeProgress)
maneuvers.fold(
{ error ->
Toast.makeText(
activity,
error.errorMessage,
Toast.LENGTH_SHORT
).show()
},
{
maneuverView.visibility = View.VISIBLE
maneuverView.renderManeuvers(maneuvers)
}
)
// update bottom trip progress summary
tripProgressView.render(
tripProgressApi.getTripProgress(routeProgress)
)
}
private val routesObserver = RoutesObserver { routes ->
Log.d("test_nav", "routesObserver: ${routes.size}")
if (routes.isNotEmpty()) {
// generate route geometries asynchronously and render them
val routeLines = routes.map { RouteLine(it, null) }
routeLineAPI.setRoutes(
routeLines
) { value ->
mapboxMap.getStyle()?.apply {
routeLineView.renderRouteDrawData(this, value)
}
}
// update the camera position to account for the new route
viewportDataSource.onRouteChanged(routes.first())
viewportDataSource.evaluate()
} else {
// remove the route line and route arrow from the map
val style = mapboxMap.getStyle()
if (style != null) {
routeLineAPI.clearRouteLine { value ->
routeLineView.renderClearRouteLineValue(
style,
value
)
}
routeArrowView.render(style, routeArrowAPI.clearArrows())
}
// remove the route reference from camera position evaluations
viewportDataSource.clearRouteData()
viewportDataSource.evaluate()
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mapboxMap = mapView.getMapboxMap()
activity?.let { activity ->
init(activity)
}
}
private fun initNavigation(context: Context) {
mapboxNavigation = if (MapboxNavigationProvider.isCreated()) {
MapboxNavigationProvider.retrieve()
} else {
MapboxNavigationProvider.create(
NavigationOptions.Builder(context)
.accessToken(getString(R.string.mapbox_access_token))
.build()
)
}
}
private fun moveCameraToCurrentLocationFirst() {
mapboxNavigation.registerLocationObserver(object : LocationObserver {
override fun onNewRawLocation(rawLocation: Location) {
val point = Point.fromLngLat(rawLocation.longitude, rawLocation.latitude)
val cameraOptions = CameraOptions.Builder()
.center(point)
.zoom(INITIAL_ZOOM)
.build()
mapboxMap.setCamera(cameraOptions)
mapboxNavigation.unregisterLocationObserver(this)
}
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
// not handled
}
})
}
private fun initNavigationCamera() {
viewportDataSource = MapboxNavigationViewportDataSource(mapboxMap)
navigationCamera = NavigationCamera(
mapboxMap,
mapView.camera,
viewportDataSource
)
mapView.camera.addCameraAnimationsLifecycleListener(
NavigationBasicGesturesHandler(navigationCamera)
)
navigationCamera.registerNavigationCameraStateChangeObserver { navigationCameraState ->
// shows/hide the recenter button depending on the camera state
when (navigationCameraState) {
NavigationCameraState.TRANSITION_TO_FOLLOWING,
NavigationCameraState.FOLLOWING -> recenter.visibility = View.INVISIBLE
NavigationCameraState.TRANSITION_TO_OVERVIEW,
NavigationCameraState.OVERVIEW,
NavigationCameraState.IDLE -> recenter.visibility = View.VISIBLE
}
}
if (this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
viewportDataSource.overviewPadding = landscapeOverviewPadding
} else {
viewportDataSource.overviewPadding = overviewPadding
}
if (this.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
viewportDataSource.followingPadding = landscapeFollowingPadding
} else {
viewportDataSource.followingPadding = followingPadding
}
}
private fun initTopManeuverView(context: Context) {
maneuverApi = MapboxManeuverApi(
MapboxDistanceFormatter(DistanceFormatterOptions.Builder(context).build())
)
}
private fun initBottomProgressView(context: Context) {
tripProgressApi = MapboxTripProgressApi(
TripProgressUpdateFormatter.Builder(context)
.distanceRemainingFormatter(
DistanceRemainingFormatter(
mapboxNavigation.navigationOptions.distanceFormatterOptions
)
)
.timeRemainingFormatter(TimeRemainingFormatter(context))
.percentRouteTraveledFormatter(PercentDistanceTraveledFormatter())
.estimatedTimeToArrivalFormatter(
EstimatedTimeToArrivalFormatter(context, TimeFormat.NONE_SPECIFIED)
)
.build()
)
}
private fun initRouteLine(context: Context) {
val mapboxRouteLineOptions = MapboxRouteLineOptions.Builder(context)
.withRouteLineBelowLayerId("road-label")
.withVanishingRouteLineEnabled(true)
.build()
routeLineAPI = MapboxRouteLineApi(mapboxRouteLineOptions)
routeLineView = MapboxRouteLineView(mapboxRouteLineOptions)
val routeArrowOptions = RouteArrowOptions.Builder(context).build()
routeArrowView = MapboxRouteArrowView(routeArrowOptions)
}
@SuppressLint("MissingPermission")
private fun init(context: Context) {
initNavigation(context)
moveCameraToCurrentLocationFirst()
initNavigationCamera()
initTopManeuverView(context)
initBottomProgressView(context)
initRouteLine(context)
loadMapStyle()
initViewInteractions()
// start the trip session to being receiving location updates in free drive
// and later when a route is set also receiving route progress updates
mapboxNavigation.startTripSession()
}
private fun loadMapStyle() {
mapboxMap.loadStyleUri(
Style.MAPBOX_STREETS,
{
Log.d("test_nav", "onStyleLoaded -> ")
getRoute()
},
object : OnMapLoadErrorListener {
override fun onMapLoadError(mapLoadErrorType: MapLoadErrorType, message: String) {
Log.d("test_nav", "onMapLoadError: ${mapLoadErrorType.name}")
}
}
)
}
private fun initViewInteractions() {
stop.setOnClickListener {
clearRouteAndStopNavigation()
}
recenter.setOnClickListener {
navigationCamera.requestNavigationCameraToFollowing()
}
routeOverview.setOnClickListener {
navigationCamera.requestNavigationCameraToOverview()
recenter.showTextAndExtend(BUTTON_ANIMATION_DURATION)
}
}
private fun getRoute() {
if (settingsData.isCustomNavigationEnabled) {
requestWaypoints()
} else {
requestRoute(buildRouteOptions(null), routesReqCallback)
}
}
override fun onStart() {
super.onStart()
subscribeNavigationObservers()
}
override fun onStop() {
super.onStop()
unsubscribeNavigationObservers()
rerouteJob.cancel()
mapboxNavigation.setRerouteController()
}
override fun onDestroyView() {
super.onDestroyView()
routeLineAPI.cancel()
routeLineView.cancel()
MapboxNavigationProvider.destroy()
}
private fun setRouteAndStartNavigation(routes: List<DirectionsRoute>) {
// set routes, where the first route in the list is the primary route that
// will be used for active guidance
mapboxNavigation.setRoutes(routes)
// set custom rerouteController
mapboxNavigation.setRerouteController(rerouteController)
// show UI elements
routeOverview.visibility = View.VISIBLE
tripProgressCard.visibility = View.VISIBLE
// move the camera to overview when new route is available
navigationCamera.requestNavigationCameraToOverview()
}
private fun clearRouteAndStopNavigation() {
// clear
mapboxNavigation.setRoutes(listOf())
// hide UI elements
maneuverView.visibility = View.INVISIBLE
routeOverview.visibility = View.INVISIBLE
tripProgressCard.visibility = View.INVISIBLE
stopNavigation()
}
private fun requestWaypoints() {
val responseListener = object : OnResponseListener<ResponseNavigationWaypoints> {
override fun onDataChange(data: ResponseNavigationWaypoints) {
requestRoute(buildRouteOptions(data), routesReqCallback)
}
override fun onCancelled(e: Exception) {
requestRoute(buildRouteOptions(null), routesReqCallback)
}
}
apiClient.getNavigationWaypointsToRideEvent(
currentPosition,
rideEventId,
currentBearing,
responseListener
)
}
private fun buildRouteOptions(responseNavigationWaypoints: ResponseNavigationWaypoints?): RouteOptions? {
val originLocation = navigationLocationProvider.lastLocation
val coordinatesList = getCoordinatesList(responseNavigationWaypoints)
val approachesList = getApproachesList(responseNavigationWaypoints)
val waypointIndicesList =
getWaypointIndicesList(coordinatesList, responseNavigationWaypoints)
return RouteOptions.builder()
.applyDefaultNavigationOptions()
.applyLanguageAndVoiceUnitOptions(applicationContext)
.coordinatesList(coordinatesList)
// provide the bearing for the origin of the request to ensure
// that the returned route faces in the direction of the current user movement
.bearingsList(
listOf(
Bearing.builder()
.angle(originLocation?.bearing?.toDouble() ?: 120.0)
.degrees(45.0)
.build(),
null
)
)
.approachesList(approachesList)
.waypointIndicesList(waypointIndicesList)
.continueStraight(
responseNavigationWaypoints?.data?.continueStraight
?: !settingsData.isAllowsUTurnAtWaypoint
)
.alternatives(settingsData.isIncludesAlternativeRoutes)
.steps(settingsData.isIncludesSteps)
.build()
}
private fun requestRoute(
routeOptions: RouteOptions?,
routesRequestCallback: RouterCallback
) {
Log.d("test_nav", "requestRoute: ${routeOptions.toString()}")
if (isAdded && routeOptions != null) {
try {
mapboxNavigation.requestRoutes(
routeOptions,
routesRequestCallback
)
} catch (exception: Exception) {
showErrorAlert(settingsData.generalError)
}
}
}
private fun getCoordinatesList(responseNavigationWaypoints: ResponseNavigationWaypoints?): List<Point> {
val waypoints = arrayListOf<Point>()
responseNavigationWaypoints?.data?.waypoints?.forEach { waypoint ->
waypoints.add(Point.fromLngLat(waypoint.lng, waypoint.lat))
}
val originPoint = Point.fromLngLat(
currentPosition?.longitude ?: 0.0,
currentPosition?.latitude ?: 0.0
)
val destinationPoint = Point.fromLngLat(
directionsPosition?.longitude ?: 0.0,
directionsPosition?.latitude ?: 0.0
)
val routePoints = arrayListOf<Point>()
routePoints.add(originPoint)
routePoints.addAll(waypoints)
routePoints.add(destinationPoint)
return routePoints
}
private fun getApproachesList(responseNavigationWaypoints: ResponseNavigationWaypoints?): List<String?> {
val approachesList = arrayListOf<String?>()
approachesList.add(APPROACH_UNRESTRICTED)
responseNavigationWaypoints?.data?.waypoints?.forEach { waypoint ->
approachesList.add(waypoint.approach)
}
approachesList.add(APPROACH_UNRESTRICTED)
return approachesList
}
private fun getWaypointIndicesList(
coordinates: List<Point>,
responseNavigationWaypoints: ResponseNavigationWaypoints?
): List<Int?> {
val waypointIndicesList = arrayListOf<Int?>()
waypointIndicesList.add(0)
responseNavigationWaypoints?.data?.waypoints?.forEachIndexed { index, waypoint ->
if (waypoint.separatesLegs) {
waypointIndicesList.add(index + 1)
}
}
waypointIndicesList.add(coordinates.lastIndex)
return waypointIndicesList
}
private fun getWaypointNamesList(coordinates: List<Point>): List<String?> {
val waypointNamesList = arrayListOf<String?>()
coordinates.forEachIndexed { index, _ ->
waypointNamesList.add(if (index == coordinates.lastIndex) settingsData.destinationLabel else "")
}
return waypointNamesList
}
private fun subscribeNavigationObservers() {
mapboxNavigation.registerRoutesObserver(routesObserver)
mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
mapboxNavigation.registerLocationObserver(locationObserver)
}
private fun unsubscribeNavigationObservers() {
mapboxNavigation.unregisterRoutesObserver(routesObserver)
mapboxNavigation.unregisterRouteProgressObserver(routeProgressObserver)
mapboxNavigation.unregisterLocationObserver(locationObserver)
}
private val routesReqCallback = object : RouterCallback {
override fun onRoutesReady(routes: List<DirectionsRoute>, routerOrigin: RouterOrigin) {
Log.d("test_nav", "routesReqCallback: ${routes.size}")
setRouteAndStartNavigation(routes)
}
override fun onFailure(reasons: List<RouterFailure>, routeOptions: RouteOptions) {
showErrorAlert(settingsData.noRouteError)
}
override fun onCanceled(routeOptions: RouteOptions, routerOrigin: RouterOrigin) {
showErrorAlert(settingsData.noRouteError)
}
}
}
`
Guida per i contributori
Apri la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Direzione di ricerca
Inizia esaminando il codice di NavigationViewFragment, in particolare routeProgressObserver, routesObserver, initRouteLine, moveCameraToCurrentLocationFirst e initNavigationCamera. Riproduci la configurazione con Android API 28 e Navigation SDK 2.0.0-rc.4, quindi traccia il rendering di route-arrow, gli aggiornamenti di vanishing route-line e le modifiche della fotocamera. Il lavoro è completato quando tutti e tre i comportamenti segnalati funzionano correttamente nel fragment.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- android, kotlin
- Ambito
- mobile-dev
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Ferma
- Chiarezza
- Da chiarire
- Idoneità per principianti
- 35/100