Android高德猎鹰轨迹
先上一张效果图
一.在高德控制台创建应用获取key
这一步比较简单,没什么可说的。
二.接入工程
1.通过Android Studio引入相关的包,官方文档说的比较明确,这里不再阐述
2.配置清单文件,官网文档这里没有细说,具体可以查看示例代码中的配置,这里直接贴出相关配置。
[code]<?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中,terminal以
tid作为唯一标识。
要使用轨迹服务,必须先创建一个service,通过请求相应的接口来获取
servie id。
[code]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 3ff7 :
输入自定义的名字后提交即可获取web类型的key:
这一步获取的key就是就是上面请求中key对应的值。
注意:service id 只需要获取一次,不需要每次使用的时候都去请求获取,除非添加新的service。(每个 Key 下最多注册15个 Service)
通过以上步骤,轨迹相关的配置已经完成,下面就具体看一下代码的实现。
四.相关代码
使用轨迹服务,首先需要初始化猎鹰sdk服务类以及一些参数的配置:
[code]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) }
定位模式:
- HIGHT_ACCURACY(高精度定位模式):
在这种定位模式下,将同时使用高德网络定位和卫星定位,优先返回精度高的定位
- BATTERY_SAVING(低功耗定位模式):
在这种模式下,将只使用高德网络定位。使用网络定位就是使用基站定位。
- DEVICE_SENSORS(仅设备定位模式):
在这种模式下,将只使用卫星定位。
其次需要创建用于接收猎鹰sdk服务启停状态的监听器:
[code]//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" } } }
注意:要开启定位采集,需要首先启动轨迹上报服务,等服务启动成功后才能开启定位采集。
1.轨迹采集
轨迹采集需要提供服务id及终端id,服务id在上面通过请求相关接口已经获取到;终端id需要在该终端第一次使用轨迹采集的时候创建,所以,在使用轨迹采集的时候,需要先查询该终端id,如果没有创建过就创建一个新的,如果创建过就使用查询得到的终端id。
[code]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后,开始轨迹收集:
[code]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 } } }) }
如果创建一个新的轨迹,那么每一个行程对应着一条轨迹。
2.查询轨迹
[code]//查询轨迹,不包含散点轨迹 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小时,直接查询失败。
3.绘制轨迹
通过轨迹id查询获取到该轨迹对应的所有轨迹点,可以将这些轨迹点绘制到地图上,行成一条轨迹,为了显示的轨迹更加的平滑,需要对这些轨迹点进行平滑处理(对真实轨迹进行处理,实现去噪、平滑和抽稀)
[code]/** * 在地图上绘制轨迹绘制到地图上 * @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( 8000 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) }
如果你看到了这里,觉得文章写得不错就给个赞呗!欢迎大家评论讨论!如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足,定期免费分享技术干货。谢谢!
- android基于高德SDK计算实时行驶里程和轨迹
- android绘制触点轨迹的代码
- android开发笔记之高德地图使用
- Android 高德地图定位功能的实现步骤
- android 高德地图系统签名后定位失败解析
- android------之高德地图实现定位和3D地图显示
- Android百度地图实例详解之仿摩拜单车APP(包括附近车辆、规划路径、行驶距离、行驶轨迹记录,轨迹回放,导航等)
- 【原】Android高德地图开发——路况+定位
- android之高德地图学习
- Android如何集成百度、高德、腾讯地图
- Android之高德地图(一)(笔记)
- Android仿微信调用第三方地图应用导航(高德、百度、腾讯)
- Android贝塞尔曲线初步学习第三课 Android实现添加至购物车的运动轨迹
- (转)Android之高德地图SDK配置及简单使用详解
- Android 开发中百度地图运行一段时间地图轨迹不更新的问题
- Android笔记之高德地图定位(通过开启服务发送广播用handle消息机制更新位置信息)
- Android 使用百度LBS SDK(六)轨迹回放
- android 百度地图轨迹回放
- Android自定义view实现车载可调整轨迹线
- 从高德 SDK 学习 Android 动态加载资源