百度地图绘制运动轨迹技巧

很多运动类的app都有画出跑步者运动轨迹的需求,拿咕咚来说,我们看一下它的效果图:


本篇将要实现的效果1.跑步结束后,静态的画出整个运动轨迹2.跑步过程中,时时动态的画运动轨迹


如何实现:
1.将点与点连成线,在百度地图MapView上画出线条图层;
2.获取定位点List<LatLng>:通过百度定位sdk:LocationClient类获取,户外运动画运动轨迹,要求位置点的精度高,所以我们必须使用gps定位类型的位置结果。

//允许使用gps定位mOption.setOpenGps(true);

一 静态画整个运动轨迹
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<LatLng> 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 - 点击监听者
  */
  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;
    }

    if (location.getLocType() == BDLocation.TypeGpsLocation) {//只要gps点

        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);
    }
}
}

//首次定位很重要,选一个精度相对较高的起始点
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信号不好,精度不高,所以在开阔地带,运动轨迹效果更好。

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

推荐阅读更多精彩内容