Android百度地图(三):百度地图画运动轨迹及图层点击事件处理
转载、引用请标明出处
http://www.jianshu.com/p/2ad4c2077dfd
本文出自zhh_happig的博客,谢谢
Android百度地图(一):百度地图定位sdk 类方法参数、定位原理详细介绍
Android百度地图(二):百度地图sdk显示位置点、图层绘制
Android百度地图(四):百度地图运动轨迹纠偏、去噪、绑路之百度鹰眼sdk服务
Android百度地图(五):百度地图鹰眼sdk监控进出地理围栏(区域)
Android百度地图(六):百度地图POI检索,行政区边界、公交、线路规划查询,地理编码介绍
上篇文章讲述了如何在地图显示位置点,这篇文章主要讲述如何在地图上画运动轨迹,以及地图图层点击事件的处理。
很多运动类的app都有画出跑步者运动轨迹的需求,拿咕咚来说,我们看一下它的效果图:
咕咚运动轨迹图本篇将要实现的效果
1.跑步结束后,静态的画出整个运动轨迹
2.跑步过程中,时时动态的画运动轨迹
效果图
如何实现:
1.将点与点连成线,在百度地图MapView上画出线条图层;
2.获取定位点List
//允许使用gps定位mOption.setOpenGps(true);
更多百度定位sdk参数详解请阅读篇头百度地图(一)文章
一 静态画整个运动轨迹
1.画轨迹
//伪代码public void onCreate(){ // 地图初始化 MapView mMapView = (MapView) findViewById(R.id.bmapView); BaiduMap mBaiduMap = mMapView.getMap(); // 开启定位图层 mBaiduMap.setMyLocationEnabled(true); //获取运动后的定位点 coordinateConvert(); //设置缩放中点LatLng target,和缩放比例 MapStatus.Builder builder = new MapStatus.Builder(); builder.target(target).zoom(18f); //地图设置缩放状态 mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build())); /** * 配置线段图层参数类: PolylineOptions * ooPolyline.width(13):线宽 * ooPolyline.color(0xAAFF0000):线条颜色红色 * ooPolyline.points(latLngs):List latLngs位置点,将相邻点与点连成线就成了轨迹了 */ OverlayOptions ooPolyline = new PolylineOptions().width(13).color(0xAAFF0000).points(latLngs); //在地图上画出线条图层,mPolyline:线条图层 mPolyline = (Polyline) mBaiduMap.addOverlay(ooPolyline); mPolyline.setZIndex(3);}
/** * 我这里是在google地图取下来的wgs84坐标集合Const.googleWGS84,模拟的运动后获取的坐标集合, 所以需要转化成百度坐标;实际应该是将定位sdk返回的位置点加入到位置集合中, 定位sdk需要设置返回坐标为百度坐标:mOption.setCoorType("bd09ll"),这样就直接用,不用转换了。 */private void coordinateConvert(){ //百度坐标转化工具类CoordinateConverter CoordinateConverter converter = new CoordinateConverter(); /** * 设置需要转化的坐标类型 CoordType.COMMON:google地图、腾讯地图、高德地图所用坐标 CoordType.GPS:设备采集的原始GPS坐标 */ converter.from(CoordType.COMMON); double lanSum = 0; double lonSum = 0; for (int i = 0; i < Const.googleWGS84.length; i++) { //"39.881970,116.456218" String[] ll = Const.googleWGS84[i].split(","); LatLng sourceLatLng = new LatLng(Double.valueOf(ll[0]), Double.valueOf(ll[1])); converter.coord(sourceLatLng); //需要转化的坐标点 LatLng desLatLng = converter.convert(); //转化成百度坐标点 latLngs.add(desLatLng);//加入定位点集合 lanSum += desLatLng.latitude; lonSum += desLatLng.longitude; } //我这里设置地图的缩放中心点为所有点的几何中心点 target = new LatLng(lanSum/latLngs.size(), lonSum/latLngs.size());}
运动轨迹效果
2.添加起始图标图层、点击图层响应事件
//始点图层图标BitmapDescriptor startBD= BitmapDescriptorFactory .fromResource(R.drawable.ic_me_history_startpoint);//终点图层图标BitmapDescriptor finishBD= BitmapDescriptorFactory .fromResource(R.drawable.ic_me_history_finishpoint);//地图中显示信息窗口InfoWindow mInfoWindow;MarkerOptions oStart = new MarkerOptions();//地图标记类型的图层参数配置类 oStart.position(latLngs.get(0));//图层位置点,第一个点为起点oStart.icon(startBD);//设置图层图片oStart.zIndex(1);//设置图层Index//添加起点图层Marker mMarkerA = (Marker) (mBaiduMap.addOverlay(oStart)); //添加终点图层MarkerOptions oFinish = new MarkerOptions().position(latLngs.get(latLngs.size()-1)) .icon(finishBD).zIndex(2);Marker mMarkerB = (Marker) (mBaiduMap.addOverlay(oFinish));//设置图层点击监听回调mBaiduMap.setOnMarkerClickListener(new OnMarkerClickListener() { public boolean onMarkerClick(final Marker marker) { if (marker.getZIndex() == mMarkerA.getZIndex() ) {//如果是起始点图层 TextView textView = new TextView(getApplicationContext()); textView.setText("起点"); textView.setTextColor(Color.BLACK); textView.setGravity(Gravity.CENTER); textView.setBackgroundResource(R.drawable.popup); //设置信息窗口点击回调 OnInfoWindowClickListener listener = new OnInfoWindowClickListener() { public void onInfoWindowClick() { //这里是主线线程,可以实现自己的一些功能 Toast.makeText(getApplicationContext(),"这里是起点", Toast.LENGTH_SHORT).show(); mBaiduMap.hideInfoWindow();//隐藏信息窗口 } }; LatLng latLng = marker.getPosition();//信息窗口显示的位置点 /** * 通过传入的 bitmap descriptor 构造一个 InfoWindow * bd - 展示的bitmap position - InfoWindow显示的位置点 yOffset - 信息窗口会与图层图标重叠,设置Y轴偏移量可以解决 listener - 点击监听者 另外,Projection类可以将地理坐标和屏幕坐标进行相互转换。 Point point = mBaiduMap.getProjection().toScreenLocation(latLng)是 将地理坐标转换为屏幕上的坐标,其中point.y是地理位置点相对于MapView左上角的y轴坐标。 这里就可以将mInfoWindow放在你指定的y轴位置了, 如果将InfoWindow第三个参数设置为mMapView.getHeight()-point.y, 那么mInfoWindow是不是就放着屏幕底部了呢,哈哈。注意mMapView.getHeight()的取值时机。 */ mInfoWindow = new InfoWindow(BitmapDescriptorFactory.fromView(textView), latLng, -47, listener); mBaiduMap.showInfoWindow(mInfoWindow);//显示信息窗口 } else if (marker.getZIndex() == mMarkerB.getZIndex()) {//如果是终点图层 Button button = new Button(getApplicationContext()); button.setText("终点"); button.setOnClickListener(new OnClickListener() { public void onClick(View v) { Toast.makeText(getApplicationContext(),"这里是终点", Toast.LENGTH_SHORT).show(); mBaiduMap.hideInfoWindow(); } }); LatLng latLng = marker.getPosition(); /** * 通过传入的 view 构造一个 InfoWindow, 此时只是利用该view生成一个Bitmap绘制在地图中,监听事件由自己实现。 view - 展示的 view position - 显示的地理位置 yOffset - Y轴偏移量 */ mInfoWindow = new InfoWindow(button, latLng, -47); mBaiduMap.showInfoWindow(mInfoWindow); } return true; }});//也可以给运动轨迹添加点击事件mBaiduMap.setOnPolylineClickListener(new BaiduMap.OnPolylineClickListener() { @Override public boolean onPolylineClick(Polyline polyline) { if (polyline.getZIndex() == mPolyline.getZIndex()) { Toast.makeText(getApplicationContext(),"点数:" + polyline.getPoints().size() + ",width:" + polyline.getWidth(), Toast.LENGTH_SHORT).show(); } return false; }});
运动轨迹效果,点击图标弹出信息窗口
点击起始图标
点击图标弹出信息窗口弹出Toast
弹出Toast到这里,运动结束后画出整个轨迹图和图层添加点击事件就介绍完了。
二 时时动态的画运动轨迹
时时动态画运动轨迹效果
运动轨迹:箭头为当前位置和方向
关键在于取点:gps刚接收到信号时返回的一些点精度不高,容易造成位置偏移,如何取点很重要。
//伪代码public void onCreate() { mMapView = (MapView) findViewById(R.id.bmapView); mBaiduMap = mMapView.getMap(); // 开启定位图层 mBaiduMap.setMyLocationEnabled(true); /**添加地图缩放状态变化监听,当手动放大或缩小地图时,拿到缩放后的比例,然后获取到下次定位, * 给地图重新设置缩放比例,否则地图会重新回到默认的mCurrentZoom缩放比例 */ mCurrentZoom = 18; mBaiduMap.setOnMapStatusChangeListener(new OnMapStatusChangeListener() { @Override public void onMapStatusChangeStart(MapStatus arg0) { } @Override public void onMapStatusChangeFinish(MapStatus arg0) { mCurrentZoom = arg0.zoom;//获取手指缩放地图后的值 } @Override public void onMapStatusChange(MapStatus arg0) { } }); //设置定位图标类型为跟随模式 mBaiduMap.setMyLocationConfiguration(new MyLocationConfiguration( com.baidu.mapapi.map.MyLocationConfiguration.LocationMode.FOLLOWING, true, null)); // 定位初始化 mLocClient = new LocationClient(this); mLocClient.registerLocationListener(myListener); LocationClientOption option = new LocationClientOption(); option.setLocationMode(LocationMode.Device_Sensors);//只接受gps位置,需要在室外定位。 option.setOpenGps(true); // 允许gps定位 option.setCoorType("bd09ll"); // 设置坐标类型 option.setScanSpan(1000);//一秒一个gps mLocClient.setLocOption(option);}//开始获取位置点public void onStart() { start.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mLocClient != null && !mLocClient.isStarted()) { mLocClient.start(); } } });}//位置回调,取点很重要public class MyLocationListenner implements BDLocationListener { @Override public void onReceiveLocation(final BDLocation location) { if (location == null || mMapView == null) { return; } //注意:这里只要gps点,需要在室外定 if (location.getLocType() == BDLocation.TypeGpsLocation) { if (isFirstLoc) {//首次定位 /**第一个点很重要,决定了轨迹的效果,gps刚接收到信号时返回的一些点精度不高, * 尽量选一个精度相对较高的起始点,这个过程大概从gps刚接收到信号后5-10秒就可以完成,不影响效果。 * 注:gps接收卫星信号少则十几秒钟,多则几分钟, * 如果长时间手机收不到gps,退出,重启手机再试,这是硬件的原因 */ LatLng ll = null; //选一个精度相对较高的起始点 ll = getMostAccuracyLocation(location); if(ll == null){ return; } isFirstLoc = false; points.add(ll);//加入集合 last = ll; //显示当前定位点,缩放地图 locateAndZoom(location, ll); //标记起点图层位置 MarkerOptions oStart = new MarkerOptions();// 地图标记覆盖物参数配置类 oStart.position(points.get(0));// 覆盖物位置点,第一个点为起点 oStart.icon(startBD);// 设置覆盖物图片 mBaiduMap.addOverlay(oStart); // 在地图上添加此图层 return;//画轨迹最少得2个点,首地定位到这里就可以返回了 } //从第二个点开始 LatLng ll = new LatLng(location.getLatitude(), location.getLongitude()); /* * sdk回调gps位置的频率是1秒1个,位置点太近动态画在图上不是很明显, 可以设置点之间距离大于为5米才添加到集合中 */ if (DistanceUtil.getDistance(last, ll) < 5) { return; } points.add(ll);//如果要运动完成后画整个轨迹,位置点都在这个集合中 last = ll; //显示当前定位点,缩放地图 locateAndZoom(location, ll); //清除上一次轨迹,避免重叠绘画 mMapView.getMap().clear(); //起始点图层也会被清除,重新绘画 MarkerOptions oStart = new MarkerOptions(); oStart.position(points.get(0)); oStart.icon(startBD); mBaiduMap.addOverlay(oStart); //将points集合中的点绘制轨迹线条图层,显示在地图上 OverlayOptions ooPolyline = new PolylineOptions().width(13).color(0xAAFF0000).points(points); mPolyline = (Polyline) mBaiduMap.addOverlay(ooPolyline); } }}/*** 首次定位很重要,选一个精度相对较高的起始点* 注意:如果一直显示gps信号弱,说明过滤的标准过高了, 你可以将location.getRadius()>25中的过滤半径调大,比如>50, 并且将连续5个点之间的距离DistanceUtil.getDistance(last, ll ) > 5也调大一点,比如>10, 这里不是固定死的,你可以根据你的需求调整。*/private LatLng getMostAccuracyLocation(final BDLocation location){ if (location.getRadius()>25) {//gps位置精度大于25米的点直接弃用, return null; } LatLng ll = new LatLng(location.getLatitude(), location.getLongitude()); if (DistanceUtil.getDistance(last, ll ) > 5) { last = ll; points.clear();//有两点位置大于5,重新来过 return null; } points.add(ll); last = ll; //有5个连续的点之间的距离小于5,认为gps已稳定,以最新的点为起始点 if(points.size() >= 5){ points.clear(); return ll; } return null;}//显示当前定位点,缩放地图private void locateAndZoom(BDLocation location, LatLng ll) { /** * 记录当前经纬度,当位置不变,手机转动,取得方向传感器的方向, 给地图重新设置位置参数,在跟随模式下可使地图箭头随手机转动而转动 */ mCurrentLat = location.getLatitude(); mCurrentLon = location.getLongitude(); locData = new MyLocationData.Builder().accuracy(0)//去掉精度圈 //此mCurrentDirection为自己获取到的手机传感器方向信息,顺时针0-360 .direction(mCurrentDirection).latitude(location.getLatitude()) .longitude(location.getLongitude()).build(); mBaiduMap.setMyLocationData(locData);//显示当前定位位置点 //给地图设置缩放中心点,和缩放比例值 builder = new MapStatus.Builder(); builder.target(ll).zoom(mCurrentZoom); mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));}//运动结束增加终点图标finish.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (mLocClient != null && mLocClient.isStarted()) { mLocClient.stop();//停止定位 if(points.size() <= 0){ return; } //运动结束记得标记终点图标 MarkerOptions oFinish = new MarkerOptions(); oFinish.position(points.get(points.size() - 1)); oFinish.icon(finishBD); mBaiduMap.addOverlay(oFinish); } }});
退出记得释放资源
//伪代码protected void onDestroy() { // 退出时销毁定位 mLocClient.unRegisterLocationListener(myListener); mLocClient.stop(); // 关闭定位图层 mBaiduMap.setMyLocationEnabled(false); mMapView.getMap().clear(); mMapView.onDestroy(); mMapView = null; startBD.recycle(); finishBD.recycle();}
注:我们画运动轨迹要求定位sdk返回的位置精度很高,轨迹的效果才会好,因而必须接受gps位置点。但是gps位置的在刚开始收到信号时精度不高,会出现位置漂移的情况,所以要选取一个精度较好的点。在建筑物、桥梁、大树、隧道里面,gps信号不好,精度不高,所以在开阔地带,运动轨迹效果更好。
当运动轨迹效果不佳时,可以利用百度鹰眼sdk采集位置数据,然后查询纠偏后轨迹,这样效果会好很多,详情请阅读:
Android百度地图(四):百度地图运动轨迹纠偏、去噪、绑路之百度鹰眼sdk服务
更多百度地图、定位sdk参数详解请阅读篇头百度地图(一、二)文章
如果各位看官觉得文章不错,别忘了点个喜欢。
源码下载地址
以上文章内容,是本人工作中的总结,供大家参考,有误的地方还请指正。
更多相关文章
- Android百度地图(五):百度地图鹰眼sdk监控进出地理围栏(区域)
- Android实习笔记----调用拨号器,邮件短信和Google 地图
- Android百度地图(四):百度地图运动轨迹纠偏、去噪、绑路之百度鹰
- Android百度地图(一):百度地图定位sdk 类方法参数、定位原理详细
- Android百度地图导航的那些坑
- Android百度地图(六):百度地图POI检索,行政区边界、公交、线路规
- Android百度地图SDK—地图标记
- 【Arcgis for android】保存地图截图到sd卡
- Android使用百度地图移动到我的位置