Android百度地图(四):百度地图运动轨迹纠偏、去噪、绑路之百度鹰眼

转载、引用请标明出处
http://www.jianshu.com/p/3c3d9e92739d
本文出自zhh_happig的简书博客,谢谢

Android百度地图(一):百度地图定位sdk 类方法参数、定位原理详细介绍
Android百度地图(二):百度地图sdk显示位置点、图层绘制
Android百度地图(三):百度地图画运动轨迹及图层点击事件处理
Android百度地图(五):百度地图鹰眼sdk监控进出地理围栏(区域)
Android百度地图(六):百度地图POI检索,行政区边界、公交、线路规划查询,地理编码介绍

上一篇文章介绍了地图画轨迹的基本原理和实现。不难发现,当位置处于建筑物密集区、桥梁、高架桥下,gps信号较差时,画出来的轨迹效果会比较差。即使是在空旷地带,也难免会出现gps漂移的情况而造成轨迹的偏差。这时就需要我们对位置点进行纠偏、去噪、抽稀、绑路操作。百度鹰眼sdk则提供了相应的api,本篇文章将介绍如何使用百度鹰眼sdk画出效果相对较好的轨迹。

先来看看效果图
1.百度地图(三)文章中demo取得原始位置点画出来的轨迹图:


原始位置轨迹图a

可以发现轨迹大致能反应用户所经过的路劲,效果还是不错的,说明gps信号较好,精度较高。但是除了西边的轨迹较为平滑之外,其他方位的轨迹都出现了锯齿形状,原因是gps位置有一定精度差,所以不一定会准确的定位到所行走的路劲上。如果运动范围较大(需缩小地图显示整个轨迹,轨迹将在视觉上变得平滑),而且轨迹精度要求不高,能确定用户在户外,个人觉得图a就能满足要求了

2.使用百度鹰眼sdk处理后的轨迹图:


经处理后的轨迹图

可以发现经处理的轨迹已经没有锯齿形状了,位置都落到了路劲上,这就是我们想要的效果。如果无法确定用户gps信号的优良,可能会进行网络定位,并且轨迹的精度要求很高,那么位置必须通过百度鹰眼sdk处理后再画出运动轨迹,这样才能达到图b的效果。

下面将介绍如何使用百度鹰眼sdk画出效果相对较好的轨迹,包括驾车、骑行、步行。

一 配置工程

1.申请key
2.创建鹰眼轨迹服务空间并获取 service_id
3.在Application标签中声明SERVICE组件,每个APP拥有自己独立的鹰眼追踪service

<service
     android:name="com.baidu.trace.LBSTraceService"
     android:enabled="true"
     android:exported="true"
     android:process=":remote" />

二 百度鹰眼sdk关键api介绍

1.轨迹数据处理流程图


流程图

2.初始化

//以下都是伪代码
/**
*  轨迹服务:通过serviceId对应服务端创建的鹰眼sdk服务,用于存储、访问和管理自己的终端和轨迹。注:个人最多创建10个鹰眼sdk服务。
   serviceId:轨迹服务id,这就是配置工程申请的service_id
   entityName:服务监控的对象的标识,最好是手机设备唯一标识。一个轨迹服务可监控最多100万个对象。
   isNeedObjectStorage:是否需要对象存储服务,比如在某个点存一个图层图片,显示这里有超速摄像头,
                        获取轨迹的时候,也可以获取这个图层图片显示在轨迹的相应位置上.
                        这里默认为:一般为false,关闭对象存储服务。
                        注:鹰眼 Android SDK v3.0以上版本支持随轨迹上传图像等对象数据,
                        若需使用此功能,该参数需设为 true,且需导入bos-android-sdk-1.0.2.jar。
*/
Trace mTrace = new Trace(serviceId, entityName,isNeedObjectStorage);

/**
*  轨迹客户端LBSTraceClient,主要功能:
   (1)内部具有定位功能,能采集定位位置点,百度定位sdk不清楚的可以阅读篇头百度文章(一)
   (2)将采集数据打包发给服务端
   (3)请求服务端,查询经过轨迹、位置、围栏、图像等信息
*/
LBSTraceClient mClient = new LBSTraceClient(mContext);

3.定位当前位置显示在地图上

//定位请求参数类
LocRequest locRequest = new LocRequest(serviceId);
//时时定位设备当前位置,定位信息不会存储在轨迹服务端,即不会形成轨迹信息,只用于在MapView显示当前位置
mClient.queryRealTimeLoc(locRequest, entityListener);//这里只会一次定位,多次定位使Handler.postDelayed(Runnable, interval)实现;

//Entity监听器(用于接收实时定位回调)
private OnEntityListener entityListener = new OnEntityListener() {
     @Override
     public void onReceiveLocation(TraceLocation location) {
          //将回调的当前位置location显示在地图MapView上,地图显示位置不清楚的可以篇头阅读百度文章(二)
          //这里位置点的返回间隔时间为Handler.postDelayed的延时时间
     }   
};

/**
* 当轨迹服务开启,且采集数据开启之后,显示在地图上的位置点可以用服务端纠偏后的最新点,
  因为通过mClient.queryRealTimeLoc获取的点可能不精确,出现漂移等情况。
*/
//查询服务端纠偏后的最新轨迹点请求参数类
LatestPointRequest request = new LatestPointRequest(getTag(), serviceId, entityName);
ProcessOption processOption = new ProcessOption();//纠偏选项
processOption.setRadiusThreshold(50);//设置精度过滤,0为不需要;精度大于50米的位置点过滤掉
processOption.setTransportMode(TransportMode.walking);
processOption.setNeedDenoise(true);//去噪处理
processOption.setNeedMapMatch(true);//绑路处理
request.setProcessOption(processOption);//设置参数
mClient.queryLatestPoint(request, trackListener);//请求纠偏后的最新点

//轨迹监听器(用于接收纠偏后实时位置回调)
private OnTrackListener trackListener = new OnTrackListener() {
     @Override
     public void onLatestPointCallback(LatestPointResponse response) {
          //将纠偏后实时位置显示在地图MapView上
          //这里位置点的返回间隔时间为数据打包上传的频率;数据发送到服务端,才会更新最新的纠偏位置
     }
};

4.开启服务,开始采集数据

//设置定位模式
mClient.setLocationMode(LocationMode.High_Accuracy);

/**
* 设置采集频率:这里的采集频率指的是轨迹数据的采集频率,和上面显示当前位置的定位频率要区分开
  最小为2秒,最大为5分钟,否则设置不成功,默认值为5s
* 打包上传频率:mClient每隔packInterval时间会自动打包上传
* 打包时间间隔必须为采集时间间隔的整数倍,且最大不能超过5分钟,否则设置不成功,默认为30s
*/
mClient.setInterval(gatherInterval, packInterval);

/**
* 开启轨迹服务
*/
mClient.startTrace(mTrace, traceListener);
//开启位置点采集
mClient.startGather(traceListener);
startTime = System.currentTimeMillis()/1000;//记录开始采集时间

//轨迹服务监听器
private OnTraceListener traceListener = new OnTraceListener() {

    /**
    * 绑定com.baidu.trace.LBSTraceService服务回调接口
    * @param errorNo  状态码,0:成功,1:失败
    * @param message 消息
    */
    @Override
    public void onBindServiceCallback(int errorNo, String message) {

    }

    /**
    * 开启服务回调接口
    * @param errorNo 状态码
    * 0:成功,10000:请求发送失败,10001:服务开启失败,10002:参数错误,10003:网络连接失败
      10004:网络未开启,10005:服务正在开启,10006:服务已开启
    * @param message 消息
    */
    @Override
    public void onStartTraceCallback(int errorNo, String message) {

    }

     /**
     * 停止服务回调接口
     * @param errorNo 状态码
     * 0:成功,11000:请求发送失败,11001:服务停止失败,11002:服务未开启,11003:服务正在停止
     * @param message 消息
     */
     @Override
     public void onStopTraceCallback(int errorNo, String message) {

     }

     /**
     * 开启采集回调接口
     * @param errorNo 状态码
     * 0:成功,12000:请求发送失败,12001:采集开启失败,12002:服务未开启
     * @param message 消息             
     */
     @Override
     public void onStartGatherCallback(int errorNo, String message) {

     }

     /**
     * 停止采集回调接口
     * @param errorNo 状态码
     * 0:成功,13000:请求发送失败,13001:采集停止失败,13002:服务未开启
     * @param message 消息              
     */
     @Override
     public void onStopGatherCallback(int errorNo, String message) {

     }

     /**
     * 推送消息回调接口
     * @param messageType 状态码
     * 0x01:配置下发,0x02:语音消息,0x03:服务端围栏报警消息,0x04:本地围栏报警消息    
     */
     @Override
     public void onPushCallback(byte messageType, PushMessage pushMessage) {
        //这个回调其实是比较重要的,本篇主要讲的是画轨迹,所以就不详细讲了
        /**
        * 那么这里能实现什么功能呢,我想到的两个例子
        * 1.到达目的地提示用户,在目的地画个圈,进入提醒
           实现:CreateFenceRequest创建围栏:圆形围栏、多边形围栏、线型围栏、行政区围栏,一旦进出则推送报警,
           pushMessage.getFenceAlarmPushInfo().getMonitoredAction()可以知道进或出,
           MonitoredAction.enter:进围栏,MonitoredAction.exit:出围栏
        * 2.用户出一定区域发出提醒,在当前位置地画个圈,出去了则提醒
        *  比如孩子在小区玩,拿着手机和小伙伴在树底下玩王者农药,我们为了孩子的安全,
           只允许他在小区范围内活动,一旦离开了小区的范围就给大人发个短信什么的,哈哈
        */
     }
};  

5.停止服务,停止采集数据

mClient.stopGather(traceListener);
endTime = System.currentTimeMillis()/1000;//记录停止采集时间
mClient.stopTrace(mTrace, traceListener);

6.请求服务端处理后的位置数据

/**
 * 历史轨迹请求类
 */
HistoryTrackRequest historyTrackRequest = new HistoryTrackRequest();
ProcessOption processOption = new ProcessOption();//纠偏选项
processOption.setRadiusThreshold(50);//精度过滤
processOption.setTransportMode(TransportMode.walking);//交通方式,默认为驾车
processOption.setNeedDenoise(true);//去噪处理,默认为false,不处理
processOption.setNeedVacuate(true);//设置抽稀,仅在查询历史轨迹时有效,默认需要false
processOption.setNeedMapMatch(true);//绑路处理,将点移到路径上,默认不需要false
historyTrackRequest.setProcessOption(processOption);

/**
* 设置里程补偿方式,当轨迹中断5分钟以上,会被认为是一段中断轨迹,默认不补充
* 比如某些原因造成两点之间的距离过大,相距100米,那么在这两点之间的轨迹如何补偿
  SupplementMode.driving:补偿轨迹为两点之间最短驾车路线
  SupplementMode.riding:补偿轨迹为两点之间最短骑车路线
  SupplementMode.walking:补偿轨迹为两点之间最短步行路线
  SupplementMode.straight:补偿轨迹为两点之间直线
*/
historyTrackRequest.setSupplementMode(SupplementMode.no_supplement);
historyTrackRequest.setSortType(SortType.asc);//设置返回结果的排序规则,默认升序排序;升序:集合中index=0代表起始点;降序:结合中index=0代表终点。
historyTrackRequest.setCoordTypeOutput(CoordType.bd09ll);//设置返回结果的坐标类型,默认为百度经纬度

/**
*设置是否返回纠偏后轨迹,默认不纠偏
 true:打开轨迹纠偏,返回纠偏后轨迹;
 false:关闭轨迹纠偏,返回原始轨迹。
 打开纠偏时,请求时间段内轨迹点数量不能超过2万,否则将返回错误。
*/
historyTrackRequest.setProcessed(true);

//请求历史轨迹
((BaseRequest)historyTrackRequest).setTag(tag);//设置请求标识,用于唯一标记本次请求,在响应结果中会返回该标识
historyTrackRequest.setServiceId(serviceId);//设置轨迹服务id,Trace中的id
historyTrackRequest.setEntityName(entityName);//Trace中的entityName

/**
* 设置startTime和endTime,会请求这段时间内的轨迹数据;
* 这里查询采集开始到采集结束之间的轨迹数据
*/
historyTrackRequest.setStartTime(startTime);
historyTrackRequest.setEndTime(endTime);

mClient.queryHistoryTrack(historyTrackRequest, mTrackListener);//发起请求,设置回调监听

7.历史轨迹数据回调
注:当我们记录采集的起始时间,然后在查询这段时间内的点,画在地图上,可以实现用户运动结束后,一次性画出整个运动轨迹的功能

//伪代码
 private List<LatLng> trackPoints = new ArrayList<>();//轨迹点集合

/**
* 轨迹监听器(用于接收历史轨迹回调)
*/
private OnTrackListener mTrackListener = new OnTrackListener() {
            
     @Override
     public void onHistoryTrackCallback(HistoryTrackResponse response) {
        //如果觉得轨迹点可能过多,可以多次分页查询,详细代码参见源码
        List<TrackPoint> points = response.getTrackPoints();//获取轨迹点
        for (TrackPoint trackPoint : points) {
               //将轨迹点转化为地图画图层的LatLng类
               trackPoints.add(MapUtil.convertTrace2Map(trackPoint.getLocation()));
        }
        //MapUtil封装了百度地图MapView和BaidumMap中的一些api
        mapUtil.drawHistoryTrack(trackPoints, sortType);//将轨迹点画在地图上,对百度地图画图层不清楚的可以阅读偏头百度地图(三)文章
     }

};

8.里程计算

DistanceRequest distanceRequest = new DistanceRequest(tag, serviceId, entityName);
distanceRequest.setStartTime(startTime);// 设置开始时间
distanceRequest.setEndTime(endTime);// 设置结束时间
distanceRequest.setProcessed(true);// 纠偏
ProcessOption processOption = new ProcessOption();// 创建纠偏选项实例
processOption.setNeedDenoise(true);// 去噪
processOption.setNeedMapMatch(true);// 绑路
processOption.setTransportMode(TransportMode.walking);// 交通方式为步行
distanceRequest.setProcessOption(processOption);// 设置纠偏选项
distanceRequest.setSupplementMode(SupplementMode.no_supplement);// 里程填充方式为无
mTraceClient.queryDistance(distanceRequest, mTrackListener);// 查询里程

// 初始化轨迹监听器
OnTrackListener mTrackListener = new OnTrackListener() {
    // 里程回调
    @Override
    public void onDistanceCallback(DistanceResponse response) {
         double distance = response.getDistance()//里程,单位:米
         double speed = distance/(endTime-startTime);//速度:m/s
    }
};

三 动态时时画运动轨迹

//伪代码
/**
* 使用LatestPointRequest实现:查询服务端纠偏后的最新的点,在3中已经介绍了
* onLatestPointCallback此回调方法不仅可以画出当前的位置点,还可以将每一个最新纠偏后的点加入到位置点集合中,
  每返回一个点,就刷新一次轨迹图,这样就能动态画出轨迹了。
*/

//接收纠偏后最新位置回调
private OnTrackListener trackListener = new OnTrackListener() {
     @Override
     public void onLatestPointCallback(LatestPointResponse response) {
          
         if(first){//返回的第一个点是上一次采集的最后一个点,可能和当前位置距离很大,应该弃用
             first = false;
             return;
         }
         //位置点的返回间隔时间为数据打包上传的频率 
         LatestPoint point = response.getLatestPoint();
         LatLng currentLatLng = mapUtil.convertTrace2Map(point.getLocation());
         trackPoints.add(currentLatLng);
         mapUtil.drawHistoryTrack(trackPoints,false,mCurrentDirection);//显示当前位置,并时时动态的画出运动轨迹
     }
};

动态轨迹效果图


效果图

四 查询被监控者轨迹

现实也很简单,被监控者手机开始采集数据

//开启轨迹服务,注意被监控者与监控者中的serviceId必须一致,mTrace的entityname为被监控手机的deviceId
mClient.startTrace(mTrace, traceListener);
//开启位置点采集
mClient.startGather(traceListener);

监控者查询数据

//查询数据,historyTrackRequest的entityname为被监控手机的deviceId
mClient.queryHistoryTrack(historyTrackRequest, mTrackListener);

好啦,到此,运动轨迹的纠偏、绑路等处理就讲完了,详细实现请参照源码。

如果各位看官觉得文章不错,别忘了点个喜欢。
源码下载地址

以上文章内容,是本人工作中的总结,供大家参考,有误的地方还请指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容