• mapbox 导航记录(release-v2.15分支 纯kotlin)


    一、简单使用示例

    1. 初始化 MapboxNavigation

    初始化时使用 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()
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2. 初始化 LocationObserver,对位置改变做观察

    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()
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    3. 初始化 RoutesObserver,对多条路线的处理

    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()
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    4. 初始化 NavigationSessionStateObserver ,对导航状态的监测

    private val navigationSessionStateObserver = NavigationSessionStateObserver {
        logD("NavigationSessionState=$it", LOG_CATEGORY)
        logD("sessionId=${mapboxNavigation.getNavigationSessionState().sessionId}", LOG_CATEGORY)
    }
    
    • 1
    • 2
    • 3
    • 4

    5. 初始化 RouteProgressObserver ,对导航进度的监测

    最关键的部分,如下是正常导航时的进度处理。但是对于模拟导航,只需要实例化 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))
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    6. 初始化 VoiceInstructionsObserver ,对语音指令的监测

    // 语音播报对象
    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
                        )
                    }
                )
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    7. findRoute并将路线设置给导航模块,然后开启导航

    // 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()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    二、关键类

    类名所属模块作用
    MapboxNavigationlibnavigation-core核心类,要给它配置token,定位引擎等。
    mapboxNavigation.startTripSession()
    registerLocationObserver() 观察位置变化。
    registerRoutesObserver() 对导航路线的处理,比如渲染,箭头等。
    registerNavigationSessionStateObserver()
    registerRouteProgressObserver() 导航进度。
    registerVoiceInstructionsObserver() 语音指令。
    NavigationOptionslibnavigation-base给 MapboxNavigation 配置token,定位引擎等使用此对象。
    还有很多其它的配置项。
    RoutesObserverlibnavigation-core对导航路线改变时,在这个接口方法中实现渲染。
    RouteProgressObserverlibnavigation-core提供状态、进度和其他有关当前逐点路由的信息的回调。
    LocationObserverlibnavigation-core监听位置更新。
    VoiceInstructionsObserverlibnavigation-core语音指令接口。
    ---------------------------------------------------------------------------------------
    MapboxNavigationViewportDataSourcelibnavui-mapsUI相关,需要把 MapView 对象传递给此类。
    NavigationCameralibnavui-mapsUI相关,需要把 MapView,camera,MapboxNavigationViewportDataSource 对象传递给此类。
    NavigationBasicGesturesHandlerlibnavui-mapsUI相关,基础手势。
    MapboxManeuverApilibnavui-maneuverUI相关,顶部显示还有多少米向左向右转等信息。
    MapboxTripProgressApilibnavui-tripprogressUI相关,底部进度,剩余时间,剩余距离,当前时间。
    MapboxSpeechApilibnavui-voiceUI相关,语音部分。
    MapboxVoiceInstructionsPlayerlibnavui-voiceUI相关,语音部分。
    MapboxRouteLineApilibnavui-mapsUI相关,路线上图相关。
    MapboxRouteLineViewlibnavui-mapsUI相关,路线上图相关。
    MapboxRouteArrowViewlibnavui-mapsUI相关,路线上图相关。
    ---------------------------------------------------------------------------------------
    ReplayLocationEnginelibnavigation-core模拟导航相关类,要在NavigationOptions设置这种定位引擎
    MapboxReplayerlibnavigation-core模拟导航相关类,控制模拟导航play,finish等
    ReplayRouteMapperlibnavigation-core模拟导航相关类,利用它里面的方法把要模拟的路线对象DirectionsRoute设置进去
    ReplayProgressObserverlibnavigation-core模拟导航相关类,观察模拟导航的进度,替代正常导航进度观察RouteProgressObserver

    三、NavigationView

    布局文件 mapbox_navigation_view_layout.xml

    init { } 方法块中会创建MapView并添加到布局,利用mapLayoutCoordinator(binding) 实例化 MapLayoutCoordinator

    再利用 MapLayoutCoordinator 里的方法给 core 模块中的 MapboxNavigation 绑定 MapView。

    MapView 的创建使用 MapViewBinder,而对它设置样式使用 MapStyleLoader

    NavigationViewContext
    NavigationViewBinder
    NavigationViewStyles
    NavigationViewOptions
    MapViewOwner
    MapStyleLoader
    _mapViewBinder
    _infoPanelRoutePreviewButtonBinder
    _infoPanelStartNavigationButtonBinder
    _infoPanelEndNavigationButtonBinder
    _infoPanelContentBinder

    四、获取导航路线 requestRoutes() 详细

    MapboxNavigation 中获取导航路线的方法,有两个方法,参数有差异

    fun requestRoutes(
        routeOptions: RouteOptions,
        routesRequestCallback: RouterCallback
    )
    
    fun requestRoutes(
        routeOptions: RouteOptions,
        callback: NavigationRouterCallback
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    RouterCallback 中获取导航路线成功得到的是 DirectionsRoute 集合,在模拟导航中使用 DirectionsRoute 对象。也对应结合 MapboxNavigation.setRoutes(List) 使用,方法内部会利用 toNavigationRoutes() 做转换。

    NavigationRouterCallback 中获取导航路线成功得到的是 NavigationRoute 集合,对应结合 MapboxNavigation.setNavigationRoutes(List) 使用。

    toNavigationRoutes() 是把 DirectionsRoute 集合转为 NavigationRoute 集合,也有 toDirectionsRoutes() 可以把 NavigationRoute 集合转为 DirectionsRoute 集合。

  • 相关阅读:
    【JAVA】总结线程Thread的基本用法
    【深入浅出Spring原理及实战】「原理分析专题」重新回顾一下Spring框架的异步执行调用的原理和实战
    智云通CRM:如何做一个优秀的顾问式销售人员?
    06_es分布式搜索引擎2
    单行函数,聚合函数课后练习
    配置mysql+Navicat+XShell宝塔:Mark
    opencv创建窗口—cv.namedWindow()
    c语言实训心得3篇集合
    深入了解Golang中的反射机制
    <学习笔记>从零开始自学Python-之-web应用框架Django( 九)自定义标签和过滤器
  • 原文地址:https://blog.csdn.net/u012551120/article/details/131824164