初始化时使用 NavigationOptions 设置一些参数,包括accessToken、appMetaData、LocationEngine等,其它还有很多,具体可以详看 NavigationOptions 类的内部。
下面示例中 LocationEngine 使用的重演定位引擎,可以看到模拟导航的效果,关键的两个类就是 ReplayLocationEngine 和 MapboxReplayer 。
/* ----- Mapbox Navigation components ----- */
private lateinit var mapboxNavigation: MapboxNavigation
private val mapboxReplayer = MapboxReplayer()
// initialize Mapbox Navigation
mapboxNavigation = MapboxNavigationProvider.create(
NavigationOptions.Builder(applicationContext)
.accessToken(getMapboxAccessTokenFromResources())
.eventsAppMetadata(
EventsAppMetadata.Builder(
BuildConfig.APPLICATION_ID,
BuildConfig.VERSION_NAME
).build()
)
.locationEngine(ReplayLocationEngine(mapboxReplayer))
.build()
)
navigationLocationProvider 只是把当前导航的位置点给到 MapView,实现地图移动或者加位置图标等。
MapboxNavigationViewportDataSource 也是一样,移动地图的Camera到一个合适的位置,要结合 NavigationCamera 使用。NavigationCamera 还可以改变导航是 Following 还是 Overview 。
// camera
private lateinit var navigationCamera: NavigationCamera
private lateinit var viewportDataSource: MapboxNavigationViewportDataSource
// initialize Navigation Camera
viewportDataSource = MapboxNavigationViewportDataSource(
binding.mapView.getMapboxMap()
)
navigationCamera = NavigationCamera(
binding.mapView.getMapboxMap(),
binding.mapView.camera,
viewportDataSource
)
/* ----- Location and route progress callbacks ----- */
private val locationObserver = object : LocationObserver {
override fun onNewRawLocation(rawLocation: Location) {
// not handled
}
override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
// update location puck's position on the map
navigationLocationProvider.changePosition(
location = locationMatcherResult.enhancedLocation,
keyPoints = locationMatcherResult.keyPoints,
)
// update camera position to account for new location
viewportDataSource.onLocationChanged(locationMatcherResult.enhancedLocation)
viewportDataSource.evaluate()
}
}
private val routesObserver = RoutesObserver { result ->
if (result.routes.isNotEmpty()) {
// generate route geometries asynchronously and render them
CoroutineScope(Dispatchers.Main).launch {
val result = routeLineAPI.setRoutes(
listOf(RouteLine(result.routes.first(), null))
)
val style = mapboxMap.getStyle()
if (style != null) {
routeLineView.renderRouteDrawData(style, result)
}
}
// update the camera position to account for the new route
viewportDataSource.onRouteChanged(result.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 to change camera position
viewportDataSource.clearRouteData()
viewportDataSource.evaluate()
}
}
private val navigationSessionStateObserver = NavigationSessionStateObserver {
logD("NavigationSessionState=$it", LOG_CATEGORY)
logD("sessionId=${mapboxNavigation.getNavigationSessionState().sessionId}", LOG_CATEGORY)
}
导航进度
的监测最关键的部分,如下是正常导航时的进度处理。但是对于模拟导航,只需要实例化 ReplayProgressObserver 对象。
// 模拟导航使用
private val routeProgressObserver1 = ReplayProgressObserver(mapboxReplayer)
// 正常导航使用
private val routeProgressObserver =
RouteProgressObserver { routeProgress ->
// update the camera position to account for the progressed fragment of the route
viewportDataSource.onRouteProgressChanged(routeProgress)
viewportDataSource.evaluate()
// show arrow on the route line with the next maneuver
val maneuverArrowResult = routeArrowAPI.addUpcomingManeuverArrow(routeProgress)
val style = mapboxMap.getStyle()
if (style != null) {
routeArrowView.renderManeuverUpdate(style, maneuverArrowResult)
}
// update top maneuver instructions
val maneuvers = maneuverApi.getManeuvers(routeProgress)
maneuvers.fold(
{ error ->
Toast.makeText(
this@MapboxNavigationActivity,
error.errorMessage,
Toast.LENGTH_SHORT
).show()
},
{
binding.maneuverView.visibility = VISIBLE
binding.maneuverView.renderManeuvers(maneuvers)
}
)
// update bottom trip progress summary
binding.tripProgressView.render(tripProgressApi.getTripProgress(routeProgress))
}
// 语音播报对象
private lateinit var voiceInstructionsPlayer: MapboxVoiceInstructionsPlayer
voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer(
this,
Locale.US.language
)
// 静音和取消静音
voiceInstructionsPlayer.volume(SpeechVolume(0f))
voiceInstructionsPlayer.volume(SpeechVolume(1f))
/* ----- Voice instruction callbacks ----- */
private val voiceInstructionsObserver =
VoiceInstructionsObserver { voiceInstructions ->
speechAPI.generate(
voiceInstructions,
speechCallback
)
}
// speechCallback 中做 play
private val speechCallback =
MapboxNavigationConsumer<Expected<SpeechError, SpeechValue>> { expected ->
expected.fold(
{ error ->
// play the instruction via fallback text-to-speech engine
voiceInstructionsPlayer.play(
error.fallback,
voiceInstructionsPlayerCallback
)
},
{ value ->
// play the sound file from the external generator
voiceInstructionsPlayer.play(
value.announcement,
voiceInstructionsPlayerCallback
)
}
)
}
// findRoute
mapboxNavigation.requestRoutes()
// 路线获取成功 onRoutesReady 后的处理
private fun setRouteAndStartNavigation(route: List<NavigationRoute>) {
// set route
mapboxNavigation.setNavigationRoutes(route)
// show UI elements
binding.soundButton.visibility = VISIBLE
binding.routeOverview.visibility = VISIBLE
binding.tripProgressCard.visibility = VISIBLE
binding.routeOverview.showTextAndExtend(2000L)
binding.soundButton.unmuteAndExtend(2000L)
// move the camera to overview when new route is available
navigationCamera.requestNavigationCameraToOverview()
}
override fun onStart() {
super.onStart()
mapboxNavigation.registerRoutesObserver(routesObserver)
mapboxNavigation.registerNavigationSessionStateObserver(navigationSessionStateObserver)
// mapboxNavigation.registerRouteProgressObserver(routeProgressObserver)
mapboxNavigation.registerRouteProgressObserver(routeProgressObserver1)
mapboxNavigation.registerLocationObserver(locationObserver)
mapboxNavigation.registerVoiceInstructionsObserver(voiceInstructionsObserver)
// 实际导航只需要注册好上面的观察者,下面时模拟导航的特殊开启方式
mapboxReplayer.pushRealLocation(this, 0.0)
mapboxReplayer.playbackSpeed(1.5)
mapboxReplayer.play()
}
类名 | 所属模块 | 作用 |
---|---|---|
MapboxNavigation | libnavigation-core | 核心类,要给它配置token,定位引擎等。 mapboxNavigation.startTripSession() registerLocationObserver() 观察位置变化。 registerRoutesObserver() 对导航路线的处理,比如渲染,箭头等。 registerNavigationSessionStateObserver() registerRouteProgressObserver() 导航进度。 registerVoiceInstructionsObserver() 语音指令。 |
NavigationOptions | libnavigation-base | 给 MapboxNavigation 配置token,定位引擎等使用此对象。 还有很多其它的配置项。 |
RoutesObserver | libnavigation-core | 对导航路线改变时,在这个接口方法中实现渲染。 |
RouteProgressObserver | libnavigation-core | 提供状态、进度和其他有关当前逐点路由的信息的回调。 |
LocationObserver | libnavigation-core | 监听位置更新。 |
VoiceInstructionsObserver | libnavigation-core | 语音指令接口。 |
----------------------------- | ----------------------------- | ----------------------------- |
MapboxNavigationViewportDataSource | libnavui-maps | UI相关,需要把 MapView 对象传递给此类。 |
NavigationCamera | libnavui-maps | UI相关,需要把 MapView,camera,MapboxNavigationViewportDataSource 对象传递给此类。 |
NavigationBasicGesturesHandler | libnavui-maps | UI相关,基础手势。 |
MapboxManeuverApi | libnavui-maneuver | UI相关,顶部显示还有多少米向左向右转等信息。 |
MapboxTripProgressApi | libnavui-tripprogress | UI相关,底部进度,剩余时间,剩余距离,当前时间。 |
MapboxSpeechApi | libnavui-voice | UI相关,语音部分。 |
MapboxVoiceInstructionsPlayer | libnavui-voice | UI相关,语音部分。 |
MapboxRouteLineApi | libnavui-maps | UI相关,路线上图相关。 |
MapboxRouteLineView | libnavui-maps | UI相关,路线上图相关。 |
MapboxRouteArrowView | libnavui-maps | UI相关,路线上图相关。 |
----------------------------- | ----------------------------- | ----------------------------- |
ReplayLocationEngine | libnavigation-core | 模拟导航相关类,要在NavigationOptions设置这种定位引擎 |
MapboxReplayer | libnavigation-core | 模拟导航相关类,控制模拟导航play,finish等 |
ReplayRouteMapper | libnavigation-core | 模拟导航相关类,利用它里面的方法把要模拟的路线对象DirectionsRoute设置进去 |
ReplayProgressObserver | libnavigation-core | 模拟导航相关类,观察模拟导航的进度,替代正常导航进度观察RouteProgressObserver |
布局文件 mapbox_navigation_view_layout.xml
init { } 方法块中会创建MapView并添加到布局,利用mapLayoutCoordinator(binding)
实例化 MapLayoutCoordinator
。
再利用 MapLayoutCoordinator
里的方法给 core 模块中的 MapboxNavigation
绑定 MapView。
MapView 的创建使用 MapViewBinder
,而对它设置样式使用 MapStyleLoader
。
是 MapboxNavigation
中获取导航路线的方法,有两个方法,参数有差异
fun requestRoutes(
routeOptions: RouteOptions,
routesRequestCallback: RouterCallback
)
fun requestRoutes(
routeOptions: RouteOptions,
callback: NavigationRouterCallback
)
RouterCallback
中获取导航路线成功得到的是 DirectionsRoute
集合,在模拟导航中使用 DirectionsRoute
对象。也对应结合 MapboxNavigation.setRoutes(List
使用,方法内部会利用 toNavigationRoutes()
做转换。
NavigationRouterCallback
中获取导航路线成功得到的是 NavigationRoute
集合,对应结合 MapboxNavigation.setNavigationRoutes(List
使用。
toNavigationRoutes()
是把 DirectionsRoute
集合转为 NavigationRoute
集合,也有 toDirectionsRoutes()
可以把 NavigationRoute
集合转为 DirectionsRoute
集合。