Android百度地圖(三):百度地圖畫運(yùn)動軌跡及圖層點擊事件處理

轉(zhuǎn)載、引用請標(biāo)明出處
http://www.itdecent.cn/p/2ad4c2077dfd
本文出自zhh_happig的簡書博客,謝謝

Android百度地圖(一):百度地圖定位sdk 類方法參數(shù)、定位原理詳細(xì)介紹
Android百度地圖(二):百度地圖sdk顯示位置點、圖層繪制
Android百度地圖(四):百度地圖運(yùn)動軌跡糾偏、去噪、綁路之百度鷹眼sdk服務(wù)
Android百度地圖(五):百度地圖鷹眼sdk監(jiān)控進(jìn)出地理圍欄(區(qū)域)
Android百度地圖(六):百度地圖POI檢索,行政區(qū)邊界、公交、線路規(guī)劃查詢,地理編碼介紹

上篇文章講述了如何在地圖顯示位置點,這篇文章主要講述如何在地圖上畫運(yùn)動軌跡,以及地圖圖層點擊事件的處理。

很多運(yùn)動類的app都有畫出跑步者運(yùn)動軌跡的需求,拿咕咚來說,我們看一下它的效果圖:

咕咚運(yùn)動軌跡圖

本篇將要實現(xiàn)的效果
1.跑步結(jié)束后,靜態(tài)的畫出整個運(yùn)動軌跡
2.跑步過程中,時時動態(tài)的畫運(yùn)動軌跡


效果圖

如何實現(xiàn):
1.將點與點連成線,在百度地圖MapView上畫出線條圖層;
2.獲取定位點List<LatLng>:通過百度定位sdk:LocationClient類獲取,戶外運(yùn)動畫運(yùn)動軌跡,要求位置點的精度高,所以我們必須使用gps定位類型的位置結(jié)果。

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

更多百度定位sdk參數(shù)詳解請閱讀篇頭百度地圖(一)文章

一 靜態(tài)畫整個運(yùn)動軌跡

1.畫軌跡

//偽代碼
public void onCreate(){
  // 地圖初始化
  MapView mMapView = (MapView) findViewById(R.id.bmapView);
  BaiduMap mBaiduMap = mMapView.getMap();
  // 開啟定位圖層
  mBaiduMap.setMyLocationEnabled(true);
  
  //獲取運(yùn)動后的定位點
  coordinateConvert();

  //設(shè)置縮放中點LatLng target,和縮放比例       
  MapStatus.Builder builder = new MapStatus.Builder();
  builder.target(target).zoom(18f);

  //地圖設(shè)置縮放狀態(tài)
  mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
  
  /**
  * 配置線段圖層參數(shù)類: 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坐標(biāo)集合Const.googleWGS84,模擬的運(yùn)動后獲取的坐標(biāo)集合,
   所以需要轉(zhuǎn)化成百度坐標(biāo);實際應(yīng)該是將定位sdk返回的位置點加入到位置集合中,
   定位sdk需要設(shè)置返回坐標(biāo)為百度坐標(biāo):mOption.setCoorType("bd09ll"),這樣就直接用,不用轉(zhuǎn)換了。
 */
private void  coordinateConvert(){
  //百度坐標(biāo)轉(zhuǎn)化工具類CoordinateConverter 
  CoordinateConverter converter  = new CoordinateConverter(); 
  
  /**
  * 設(shè)置需要轉(zhuǎn)化的坐標(biāo)類型
    CoordType.COMMON:google地圖、騰訊地圖、高德地圖所用坐標(biāo)
    CoordType.GPS:設(shè)備采集的原始GPS坐標(biāo)
  */
  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);  //需要轉(zhuǎn)化的坐標(biāo)點
    LatLng desLatLng = converter.convert();  //轉(zhuǎn)化成百度坐標(biāo)點
    latLngs.add(desLatLng);//加入定位點集合
    lanSum += desLatLng.latitude;
    lonSum += desLatLng.longitude;
  }
  
  //我這里設(shè)置地圖的縮放中心點為所有點的幾何中心點
  target = new LatLng(lanSum/latLngs.size(), lonSum/latLngs.size());
}

運(yùn)動軌跡效果

軌跡圖

2.添加起始圖標(biāo)圖層、點擊圖層響應(yīng)事件

//始點圖層圖標(biāo)
BitmapDescriptor startBD= BitmapDescriptorFactory
            .fromResource(R.drawable.ic_me_history_startpoint);
//終點圖層圖標(biāo)
BitmapDescriptor finishBD= BitmapDescriptorFactory
            .fromResource(R.drawable.ic_me_history_finishpoint);

//地圖中顯示信息窗口
InfoWindow mInfoWindow;

MarkerOptions oStart = new MarkerOptions();//地圖標(biāo)記類型的圖層參數(shù)配置類 
oStart.position(latLngs.get(0));//圖層位置點,第一個點為起點
oStart.icon(startBD);//設(shè)置圖層圖片
oStart.zIndex(1);//設(shè)置圖層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));

//設(shè)置圖層點擊監(jiān)聽回調(diào)
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);
                    
      //設(shè)置信息窗口點擊回調(diào)
      OnInfoWindowClickListener listener = new OnInfoWindowClickListener() {
        public void onInfoWindowClick() {
          //這里是主線線程,可以實現(xiàn)自己的一些功能
          Toast.makeText(getApplicationContext(),"這里是起點", Toast.LENGTH_SHORT).show();
          mBaiduMap.hideInfoWindow();//隱藏信息窗口
        }
      };

      LatLng latLng = marker.getPosition();//信息窗口顯示的位置點

      /**
      * 通過傳入的 bitmap descriptor 構(gòu)造一個 InfoWindow
      * bd - 展示的bitmap
        position - InfoWindow顯示的位置點
        yOffset - 信息窗口會與圖層圖標(biāo)重疊,設(shè)置Y軸偏移量可以解決
        listener - 點擊監(jiān)聽者

        另外,Projection類可以將地理坐標(biāo)和屏幕坐標(biāo)進(jìn)行相互轉(zhuǎn)換。
        Point point = mBaiduMap.getProjection().toScreenLocation(latLng)是
        將地理坐標(biāo)轉(zhuǎn)換為屏幕上的坐標(biāo),其中point.y是地理位置點相對于MapView左上角的y軸坐標(biāo)。
        這里就可以將mInfoWindow放在你指定的y軸位置了,
        如果將InfoWindow第三個參數(shù)設(shè)置為mMapView.getHeight()-point.y,
        那么mInfoWindow是不是就放著屏幕底部了呢,哈哈。注意mMapView.getHeight()的取值時機(jī)。
      */
      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 構(gòu)造一個 InfoWindow, 此時只是利用該view生成一個Bitmap繪制在地圖中,監(jiān)聽事件由自己實現(xiàn)。
        view - 展示的 view
        position - 顯示的地理位置
        yOffset - Y軸偏移量
      */
      mInfoWindow = new InfoWindow(button, latLng, -47);
      mBaiduMap.showInfoWindow(mInfoWindow);
    } 
    return true;
 }
});

//也可以給運(yùn)動軌跡添加點擊事件
mBaiduMap.setOnPolylineClickListener(new BaiduMap.OnPolylineClickListener() {
  @Override
  public boolean onPolylineClick(Polyline polyline) {
    if (polyline.getZIndex() == mPolyline.getZIndex()) {
      Toast.makeText(getApplicationContext(),"點數(shù):" + polyline.getPoints().size() + ",width:" 
                                         + polyline.getWidth(), Toast.LENGTH_SHORT).show();
    }
    return false;
  }
});

運(yùn)動軌跡效果,點擊圖標(biāo)彈出信息窗口


點擊起始圖標(biāo)

點擊圖標(biāo)彈出信息窗口彈出Toast

彈出Toast

到這里,運(yùn)動結(jié)束后畫出整個軌跡圖和圖層添加點擊事件就介紹完了。

二 時時動態(tài)的畫運(yùn)動軌跡

時時動態(tài)畫運(yùn)動軌跡效果


運(yùn)動軌跡:箭頭為當(dāng)前位置和方向

關(guān)鍵在于取點:gps剛接收到信號時返回的一些點精度不高,容易造成位置偏移,如何取點很重要。

//偽代碼
public void onCreate() {
  mMapView = (MapView) findViewById(R.id.bmapView);
  mBaiduMap = mMapView.getMap();
  // 開啟定位圖層
  mBaiduMap.setMyLocationEnabled(true);
  
  /**添加地圖縮放狀態(tài)變化監(jiān)聽,當(dāng)手動放大或縮小地圖時,拿到縮放后的比例,然后獲取到下次定位,
  *  給地圖重新設(shè)置縮放比例,否則地圖會重新回到默認(rèn)的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) {
    }
  });

  //設(shè)置定位圖標(biāo)類型為跟隨模式
  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"); // 設(shè)置坐標(biāo)類型
  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();
        }
    }
  });
}

//位置回調(diào),取點很重要
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接收衛(wèi)星信號少則十幾秒鐘,多則幾分鐘,
                * 如果長時間手機(jī)收不到gps,退出,重啟手機(jī)再試,這是硬件的原因
                */
                LatLng ll = null;

                //選一個精度相對較高的起始點
                ll = getMostAccuracyLocation(location);
                if(ll == null){
                    return;
                }
                isFirstLoc = false;
                points.add(ll);//加入集合
                last = ll;
                    
                //顯示當(dāng)前定位點,縮放地圖
                locateAndZoom(location, ll);
                    
                //標(biāo)記起點圖層位置
                MarkerOptions oStart = new MarkerOptions();// 地圖標(biāo)記覆蓋物參數(shù)配置類
                oStart.position(points.get(0));// 覆蓋物位置點,第一個點為起點
                oStart.icon(startBD);// 設(shè)置覆蓋物圖片
                mBaiduMap.addOverlay(oStart); // 在地圖上添加此圖層
                return;//畫軌跡最少得2個點,首地定位到這里就可以返回了
            }

            //從第二個點開始
            LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());

            /*
            * sdk回調(diào)gps位置的頻率是1秒1個,位置點太近動態(tài)畫在圖上不是很明顯,
              可以設(shè)置點之間距離大于為5米才添加到集合中
            */
           if (DistanceUtil.getDistance(last, ll) < 5) {
                return;
            }

            points.add(ll);//如果要運(yùn)動完成后畫整個軌跡,位置點都在這個集合中

            last = ll;
                
            //顯示當(dāng)前定位點,縮放地圖
            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信號弱,說明過濾的標(biāo)準(zhǔn)過高了,
  你可以將location.getRadius()>25中的過濾半徑調(diào)大,比如>50,
  并且將連續(xù)5個點之間的距離DistanceUtil.getDistance(last, ll ) > 5也調(diào)大一點,比如>10,
  這里不是固定死的,你可以根據(jù)你的需求調(diào)整。
*/
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個連續(xù)的點之間的距離小于5,認(rèn)為gps已穩(wěn)定,以最新的點為起始點
    if(points.size() >= 5){
        points.clear();
        return ll;
    }
    return null;
}

//顯示當(dāng)前定位點,縮放地圖
private void locateAndZoom(BDLocation location, LatLng ll) {
    /**
    * 記錄當(dāng)前經(jīng)緯度,當(dāng)位置不變,手機(jī)轉(zhuǎn)動,取得方向傳感器的方向,
      給地圖重新設(shè)置位置參數(shù),在跟隨模式下可使地圖箭頭隨手機(jī)轉(zhuǎn)動而轉(zhuǎn)動
    */
    mCurrentLat = location.getLatitude();
    mCurrentLon = location.getLongitude();
    locData = new MyLocationData.Builder().accuracy(0)//去掉精度圈
            //此mCurrentDirection為自己獲取到的手機(jī)傳感器方向信息,順時針0-360
            .direction(mCurrentDirection).latitude(location.getLatitude())
            .longitude(location.getLongitude()).build();
    mBaiduMap.setMyLocationData(locData);//顯示當(dāng)前定位位置點
    
    //給地圖設(shè)置縮放中心點,和縮放比例值
    builder = new MapStatus.Builder();
    builder.target(ll).zoom(mCurrentZoom);
    mBaiduMap.animateMapStatus(MapStatusUpdateFactory.newMapStatus(builder.build()));
}

//運(yùn)動結(jié)束增加終點圖標(biāo)
finish.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {

        if (mLocClient != null && mLocClient.isStarted()) {
            mLocClient.stop();//停止定位

            if(points.size() <= 0){
                return;
            }
                    
            //運(yùn)動結(jié)束記得標(biāo)記終點圖標(biāo)
            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();
  // 關(guān)閉定位圖層
  mBaiduMap.setMyLocationEnabled(false);
  mMapView.getMap().clear();
  mMapView.onDestroy();
  mMapView = null;
  startBD.recycle();
  finishBD.recycle();
}

注:我們畫運(yùn)動軌跡要求定位sdk返回的位置精度很高,軌跡的效果才會好,因而必須接受gps位置點。但是gps位置的在剛開始收到信號時精度不高,會出現(xiàn)位置漂移的情況,所以要選取一個精度較好的點。在建筑物、橋梁、大樹、隧道里面,gps信號不好,精度不高,所以在開闊地帶,運(yùn)動軌跡效果更好。

當(dāng)運(yùn)動軌跡效果不佳時,可以利用百度鷹眼sdk采集位置數(shù)據(jù),然后查詢糾偏后軌跡,這樣效果會好很多,詳情請閱讀:
Android百度地圖(四):百度地圖運(yùn)動軌跡糾偏、去噪、綁路之百度鷹眼sdk服務(wù)

更多百度地圖、定位sdk參數(shù)詳解請閱讀篇頭百度地圖(一、二)文章

如果各位看官覺得文章不錯,別忘了點個喜歡。
源碼下載地址

以上文章內(nèi)容,是本人工作中的總結(jié),供大家參考,有誤的地方還請指正。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容