Android高德猎鹰轨迹

作者: 慕涵盛华 | 来源:发表于2019-06-20 21:39 被阅读8次

    先上一张效果图

    一.在高德控制台创建应用获取key

    这一步比较简单,没什么可说的。如何申请 Key

    二.接入工程

    1.通过Android Studio引入相关的包,官方文档说的比较明确,这里不再阐述,附上相关的链接:Android Studio 配置工程
    2.配置清单文件,官网文档这里没有细说,具体可以查看示例代码中的配置,这里直接贴出相关配置。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              package="com.gfd.demo">
        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
        <!--用于访问GPS定位-->
        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
        <!--用于获取运营商信息,用于支持提供运营商信息相关的接口-->
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <!--用于访问wifi网络信息,wifi信息会用于进行网络定位-->
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
        <!--用于获取wifi的获取权限,wifi信息会用来进行网络定位-->
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
        <!--用于访问网络,网络定位需要上网-->
        <uses-permission android:name="android.permission.INTERNET"/>
        <!--用于读取手机当前的状态-->
        <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
        <!--用于写入缓存数据到扩展存储卡-->
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
        <!--用于申请调用A-GPS模块-->
        <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
        <!--用于申请获取蓝牙信息进行室内定位-->
        <uses-permission android:name="android.permission.BLUETOOTH"/>
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
       
        <application
            android:name=".app.AppApplication"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:resizeableActivity="true"
            android:maxAspectRatio="2.4"
            android:theme="@style/AppTheme"
            tools:ignore="GoogleAppIndexingWarning">
          
            <!--高德地图配置-->
            <service android:name="com.amap.api.location.APSService"/>
            <service android:name="com.amap.api.track.AMapTrackService"/>
    
            <meta-data
                android:name="com.amap.api.v2.apikey"
                android:value="请输入您的用户Key"/>
          
        </application>
    </manifest>
    

    注意:Android6.0的动态权限申请。

    三.获取Service ID

    • Service

    即猎鹰轨迹服务。一个service对应一个轨迹管理系统,通过一个service可管理多个终端设备(即terminal),service的唯一标识符是sid。举例说明,针对某网约车公司的轨迹管理系统,可以创建一个service,在创建的service中管理所有营运车辆的轨迹。

    • Terminal

    一个terminal代表现实中一个终端设备,它可以是个体、也可以是一辆车或者任何运动的物体。在同一个service中,terminaltid作为唯一标识。

    要使用轨迹服务,必须先创建一个service,通过请求相应的接口来获取servie id

    fun getTrackServiceId(name: String) {
         OkGo.post<String>("https://tsapi.amap.com/v1/track/service/add")
             .params("key", "2451a7584eb9aee041e0439bd3cdd51e")
             .params("name", name)
             .execute(object : StringCallback() {
                 override fun onSuccess(response: Response<String>?) {
                     val json = response?.body().toString()
                     Logger.e("服务id:$json")
                     val data = Gson().fromJson(json, TrackServiceEntity::class.java)
                     AccountConfig.SERVICE_ID = data.data.sid.toLong()
                 }
             })
    }
    

    有关接口的参数和返回值的详细说明见:接口说明

    这里重点说一下参数key这个参数值。它是用户在高德地图官网申请Web服务API类型KEY,官网文档在这说的不是很详细。我们怎么获取这个key呢?在我们上面创建的应用下,去添加一个web类型key

    输入自定义的名字后提交即可获取web类型的key:


    这一步获取的key就是就是上面请求中key对应的值。

    注意:service id 只需要获取一次,不需要每次使用的时候都去请求获取,除非添加新的service。(每个 Key 下最多注册15个 Service)

    通过以上步骤,轨迹相关的配置已经完成,下面就具体看一下代码的实现。

    四.相关代码

    使用轨迹服务,首先需要初始化猎鹰sdk服务类以及一些参数的配置:

    fun init(context: Context) {
        aMapTrackClient = AMapTrackClient(context)
        //将定位信息采集周期设置为2s,上报周期设置为20s,注意定位信息采集周期的范围应该是1s~60s,上报周期的范围是采集周期的5~50倍。
        aMapTrackClient.setInterval(2, 20)
        //设置轨迹点缓存大小为20M,最大为50M,默认值为50
        aMapTrackClient.setCacheSize(20)
        //设置定位模式,默认为高精度
        aMapTrackClient.setLocationMode(LocationMode.HIGHT_ACCURACY)
    }
    

    AMapTrackClient API文档

    定位模式:
    • HIGHT_ACCURACY(高精度定位模式):

    在这种定位模式下,将同时使用高德网络定位和卫星定位,优先返回精度高的定位

    • BATTERY_SAVING(低功耗定位模式):

    在这种模式下,将只使用高德网络定位。使用网络定位就是使用基站定位。

    • DEVICE_SENSORS(仅设备定位模式):

    在这种模式下,将只使用卫星定位。

    其次需要创建用于接收猎鹰sdk服务启停状态的监听器:

    //SimpleOnTrackLifecycleListener是实现接口OnTrackLifecycleListener的一个类,我们继承它只需要重写需要的方法即可。
    onTrackLifecycleListener = object : SimpleOnTrackLifecycleListener() {
    
                override fun onBindServiceCallback(status: Int, msg: String?) {
                    Logger.e("onBindServiceCallback, status: $status, msg: $msg")
                }
                
                //轨迹上报服务启动回调
                override fun onStartTrackCallback(status: Int, msg: String?) {
                    if (status == ErrorCode.TrackListen.START_TRACK_SUCEE || status == ErrorCode.TrackListen.START_TRACK_SUCEE_NO_NETWORK) {
                        //启动动轨迹上报服务成功,开始轨迹采集
                        startGather()
                        isServiceRunning = true
                    } else if (status == ErrorCode.TrackListen.START_TRACK_ALREADY_STARTED) {
                        //轨迹上报服务已经启动,重复启动
                        isServiceRunning = true
                    } else {
                        //"error onStartTrackCallback, status: $status, msg: $msg"
                    }
                }
    
                //轨迹上报服务停止回调
                override fun onStopTrackCallback(status: Int, msg: String?) {
                    if (status == ErrorCode.TrackListen.STOP_TRACK_SUCCE) {
                        //停止轨迹上报服务成功
                        isServiceRunning = false
                        isGatherRunning = false
                    } else {
                        //"error onStopTrackCallback, status: $status, msg: $msg"
                    }
                }
    
                //定位采集开启回调
                override fun onStartGatherCallback(status: Int, msg: String?) {
                    when (status) {
                        ErrorCode.TrackListen.START_GATHER_SUCEE -> {
                            //定位采集开启成功
                            isGatherRunning = true
                        }
                        ErrorCode.TrackListen.START_GATHER_ALREADY_STARTED -> {
                            //定位采集已经开启,重复启动
                            isGatherRunning = true
                        }
                        else -> {
                            //"error onStartGatherCallback, status: $status, msg: $msg"
                        }
                    }
                }
    
                //停止定位采集回调
                override fun onStopGatherCallback(status: Int, msg: String?) {
                    if (status == ErrorCode.TrackListen.STOP_GATHER_SUCCE) {
                        //定位采集停止成功,停止轨迹上报服务
                        stopTrack()
                        isGatherRunning = false
                    } else {
                        //"error onStopGatherCallback, status: $status, msg: $msg"
                    }
                }
            }
    

    注意:要开启定位采集,需要首先启动轨迹上报服务,等服务启动成功后才能开启定位采集。

    OnTrackLifecycleListener API文档

    1.轨迹采集

    轨迹采集需要提供服务id及终端id,服务id在上面通过请求相关接口已经获取到;终端id需要在该终端第一次使用轨迹采集的时候创建,所以,在使用轨迹采集的时候,需要先查询该终端id,如果没有创建过就创建一个新的,如果创建过就使用查询得到的终端id。

    fun startTrack() {
        //查询终端id
        aMapTrackClient.queryTerminal(QueryTerminalRequest(AccountConfig.SERVICE_ID, AccountConfig.TERMINAL_NAME),
            object : SimpleOnTrackListener() {
                override fun onQueryTerminalCallback(queryTerminalResponse: QueryTerminalResponse?) {
                    if (queryTerminalResponse?.isSuccess!!) {
                        if (queryTerminalResponse.isTerminalExist) {
                            // 当前终端已经创建过,直接使用查询到的terminal id
                            collectionTrack(queryTerminalResponse.tid)
                        } else {
                            // 当前终端是新终端,还未创建过,创建该终端并使用新生成的terminal id
                            aMapTrackClient.addTerminal(
                                AddTerminalRequest(
                                    AccountConfig.TERMINAL_NAME,
                                    AccountConfig.SERVICE_ID
                                ), object : SimpleOnTrackListener() {
                                    override fun onCreateTerminalCallback(addTerminalResponse: AddTerminalResponse?) {
                                        if (addTerminalResponse?.isSuccess!!) {
                                            collectionTrack(addTerminalResponse.tid)
                                        }
                                    }
                                })
                        }
                    } else {
                        //"网络请求失败," + queryTerminalResponse.errorMsg
                    }
                }
            })
    }
    

    获取对应的终端id后,开始轨迹收集:

    private fun collectionTrack(terminalId: Long) {
        this.terminalId = terminalId
        if (uploadToTrack) {
            newTrack()
        } else {
            appendTrack()
        }
    }
    //不创建新的轨迹
    private fun appendTrack() {
        // 不指定track id,上报的轨迹点是该终端的散点轨迹
        val trackParam = TrackParam(AccountConfig.SERVICE_ID, terminalId)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            trackParam.notification = createNotification()
        }
        aMapTrackClient.startTrack(trackParam, onTrackLifecycleListener)
    }
    //创建一个新的轨迹
    private fun newTrack() {
        aMapTrackClient.addTrack(AddTrackRequest(AccountConfig.SERVICE_ID, terminalId),
            object : SimpleOnTrackListener() {
                override fun onAddTrackCallback(addTrackResponse: AddTrackResponse?) {
                    if (addTrackResponse?.isSuccess!!) {
                        // trackId需要在启动服务后设置才能生效,因此这里不设置,而是在startGather之前设置了track id
                        trackId = addTrackResponse.trid
                        currentTrackId = trackId
                        Logger.e("轨迹id = $trackId")
                        val trackParam = TrackParam(AccountConfig.SERVICE_ID, terminalId)
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            trackParam.notification = createNotification()
                        }
                        aMapTrackClient.startTrack(trackParam, onTrackLifecycleListener)
                    } else {
                        //"网络请求失败," + addTrackResponse.errorMsg
                    }
                }
            })
    }
    

    如果创建一个新的轨迹,那么每一个行程对应着一条轨迹。

    TrackParam API文档
    AddTrackRequest API文档

    2.查询轨迹

    //查询轨迹,不包含散点轨迹
    fun queryTrack(trackId:Long) {
        aMapTrackClient.queryTerminal(QueryTerminalRequest(AccountConfig.SERVICE_ID, AccountConfig.TERMINAL_NAME),
            object : SimpleOnTrackListener() {
                override fun onQueryTerminalCallback(queryTerminalResponse: QueryTerminalResponse?) {
                    if (queryTerminalResponse?.isSuccess!!) {
                        if (queryTerminalResponse.isTerminalExist) {
                            val tid = queryTerminalResponse.tid
                            //设置轨迹查询的条件
                            val queryTrackRequest = QueryTrackRequest(
                                AccountConfig.SERVICE_ID,//轨迹服务id
                                tid, //终端id
                                trackId, // 轨迹id,不指定的话传入-1,查询所有轨迹,注意分页仅在查询特定轨迹id时生效,查询所有轨迹时无法对轨迹点进行分页
                                TimeUtils.getUnixTimeFromDate(track.startTime, TimeUtils.FORMAT_YMD_HMS),//开始时间
                                TimeUtils.getUnixTimeFromDate(track.endTime, TimeUtils.FORMAT_YMD_HMS),//结束时间
                                1,      // 启用去噪
                                if (isBindRoad) 1 else 0,// 是否绑路
                                0,      // 不进行精度过滤
                                DriveMode.DRIVING,  // 当前仅支持驾车模式
                                if (isRecoup) 1 else 0,     // 距离补偿
                                1000,   // 距离补偿,只有超过1km的点才启用距离补偿
                                1,  // 结果应该包含轨迹点信息
                                1,  // 返回第1页数据,如果未指定轨迹,分页将失效
                                100 // 一页不超过100条
                            )
                            aMapTrackClient.queryTerminalTrack(queryTrackRequest,
                                object : SimpleOnTrackListener() {
                                    override fun onQueryTrackCallback(queryTrackResponse: QueryTrackResponse?) {
                                        if (queryTrackResponse?.isSuccess!!) {
                                            //如果指定了轨迹id,返回一条,如果不指定的话,返回指定时间段内的所有轨迹
                                            val tracks = queryTrackResponse.tracks
                                            if (tracks.isNotEmpty()) {
                                                var allEmpty = true
                                                for (track in tracks) {
                                                    val points = track.points //轨迹点集合
                                                    if (points != null && points.size > 0) {
                                                        allEmpty = false
                                                        //将轨迹绘制到地图上
                                                        drawTrackOnMap(points, textureMapView)
                                                    }
                                                }
                                                if (allEmpty) run {
                                                    //所有轨迹都无轨迹点,请尝试放宽过滤限制,如:关闭绑路模式
                                                } else {
                                                    val sb = StringBuilder()
                                                    sb.append("共查询到").append(tracks.size).append("条轨迹,每条轨迹行驶距离分别为:")
                                                    for (track in tracks) {
                                                        sb.append(track.distance).append("m,")
                                                    }
                                                    sb.deleteCharAt(sb.length - 1)
                                                }
                                            } else {
                                                //未获取到轨迹                                     
                                            }
                                        } else {
                                            //查询历史轨迹失败
                                        }
                                    }
                                })
                        } else {
                            //Terminal不存在
                        }
                    } else {
                        //"网络请求失败,错误原因: " + queryTerminalResponse.errorMsg
                    }
                }
            })
    }
    

    注意:查询轨迹设置的时间段不能超过24小时,如果超过24小时,直接查询失败。

    QueryTrackRequest API文档
    QueryTerminalResponse API文档

    3.绘制轨迹

    通过轨迹id查询获取到该轨迹对应的所有轨迹点,可以将这些轨迹点绘制到地图上,行成一条轨迹,为了显示的轨迹更加的平滑,需要对这些轨迹点进行平滑处理(对真实轨迹进行处理,实现去噪、平滑和抽稀):官方示例

    /**
     * 在地图上绘制轨迹绘制到地图上
     * @param points List<Point>:轨迹点集合
     * @param textureMapView:地图View
     */
    private fun drawTrackOnMap(points: List<Point>, textureMapView: TextureMapView) {
        //轨迹平滑处理
        val pathoptimizeList = trackSmoothHandle(points)
        val boundsBuilder = LatLngBounds.Builder()
        val polylineOptions = PolylineOptions()
        //设置轨迹的颜色
        polylineOptions.color(mContext.resources.getColor(R.color.baseColorBlue)).width(20f)
        var endIndex = pathoptimizeList.size - 1
        if (pathoptimizeList.isNotEmpty()) {
            // 起点
            val latLng = pathoptimizeList[0]
            val p = Point()
            val markerOptions = MarkerOptions()
                    .position(latLng)
                    .title("起点") //弹窗标题
                    .infoWindowEnable(true) //显示弹窗
                    .snippet("点击起点图标弹窗显示的内容")
                    .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)) //起点图标
                endMarkers.add(textureMapView.map.addMarker(markerOptions))
        }
        if (pathoptimizeList.size > 1) {
            // 终点
            val latLng = pathoptimizeList[endIndex]
            val p = Point()
            p.lat = latLng.latitude
            p.lng = latLng.longitude
            val markerOptions = MarkerOptions()
                    .position(latLng)
                    .title("终点")
                    .infoWindowEnable(true)
                    .snippet(snippet)
                    //.icon(BitmapDescriptorFactory.fromBitmap(MarkerView(mContext).getBitmap())) 自定义图标
                    .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))
                endMarkers.add(textureMapView.map.addMarker(markerOptions))
        }
        for (latLng in pathoptimizeList.subList(0, endIndex + 1)) {
            polylineOptions.add(latLng)
            boundsBuilder.include(latLng)
        }
        val polyline = textureMapView.map.addPolyline(polylineOptions)
        polylines.add(polyline)
        val height = ScreenUtil.getScreenHeight(mContext)
        val width = ScreenUtil.getScreenWidth(mContext)
        //设置轨迹显示的区域  animateCamera方法:以动画方式按照传入的CameraUpdate参数更新地图状态,默认动画耗时250毫秒。
        textureMapView.map.animateCamera(
            CameraUpdateFactory.newLatLngBoundsRect(
                boundsBuilder.build(),
                ScreenUtil.dip2px(40f, mContext),
                ScreenUtil.dip2px(40f, mContext),
                height / 8,
                height / 5 * 3
            )
        )
    }
    
    /**
     * 轨迹平滑处理
     * @param points List<Point>:原轨迹点集合
     * @return List<LatLng>:平滑处理后的轨迹点集合
     */
    fun trackSmoothHandle(points: List<Point>):List<LatLng>{
         val mpathSmoothTool = PathSmoothTool()
        //设置平滑处理的等级
        mpathSmoothTool.intensity = 4
        val mOriginList = ArrayList<LatLng>()
        points.forEach {
            mOriginList.add(LatLng(it.lat, it.lng))
        }
        return mpathSmoothTool.pathOptimize(mOriginList)
    }
    

    MarkerOptions API文档
    轨迹点Point包含的
    设置轨迹显示的区域API文档

    实战

    相关文章

      网友评论

        本文标题:Android高德猎鹰轨迹

        本文链接:https://www.haomeiwen.com/subject/rwzqqctx.html