百度地圖實(shí)現(xiàn)小車規(guī)劃路線后平滑移動(dòng)功能
文章目的
項(xiàng)目開發(fā)所需,所以結(jié)合百度地圖提供的小車平滑軌跡移動(dòng),自己寫的demo
實(shí)現(xiàn)效果

代碼下載
下面是實(shí)現(xiàn)的關(guān)鍵步驟
集成百度地圖
怎么集成自然是看百度地圖開發(fā)平臺提供的文檔。
規(guī)劃線路
看百度地圖的文檔,寫一個(gè)規(guī)劃線路的工具類(駕車的)
package com.wzhx.car_smooth_move_demo.utils;
import android.util.Log;
import com.baidu.mapapi.search.route.BikingRouteResult;
import com.baidu.mapapi.search.route.DrivingRoutePlanOption;
import com.baidu.mapapi.search.route.DrivingRouteResult;
import com.baidu.mapapi.search.route.IndoorRouteResult;
import com.baidu.mapapi.search.route.MassTransitRouteResult;
import com.baidu.mapapi.search.route.OnGetRoutePlanResultListener;
import com.baidu.mapapi.search.route.PlanNode;
import com.baidu.mapapi.search.route.RoutePlanSearch;
import com.baidu.mapapi.search.route.TransitRouteResult;
import com.baidu.mapapi.search.route.WalkingRouteResult;
import com.wzhx.car_smooth_move_demo.listener.OnGetDrivingResultListener;
public class RoutePlanUtil {
private RoutePlanSearch mRoutePlanSearch = RoutePlanSearch.newInstance();
private OnGetDrivingResultListener getDrivingResultListener;
private OnGetRoutePlanResultListener getRoutePlanResultListener = new OnGetRoutePlanResultListener() {
@Override
public void onGetWalkingRouteResult(WalkingRouteResult walkingRouteResult) {
}
@Override
public void onGetTransitRouteResult(TransitRouteResult transitRouteResult) {
}
@Override
public void onGetMassTransitRouteResult(MassTransitRouteResult massTransitRouteResult) {
}
@Override
public void onGetDrivingRouteResult(DrivingRouteResult drivingRouteResult) {
Log.e("測試", drivingRouteResult.error + ":" + drivingRouteResult.status);
getDrivingResultListener.onSuccess(drivingRouteResult);
}
@Override
public void onGetIndoorRouteResult(IndoorRouteResult indoorRouteResult) {
}
@Override
public void onGetBikingRouteResult(BikingRouteResult bikingRouteResult) {
}
};
public RoutePlanUtil(OnGetDrivingResultListener getDrivingResultListener) {
this.getDrivingResultListener = getDrivingResultListener;
this.mRoutePlanSearch.setOnGetRoutePlanResultListener(this.getRoutePlanResultListener);
}
public void routePlan(PlanNode startNode, PlanNode endNode){
mRoutePlanSearch.drivingSearch((new DrivingRoutePlanOption())
.from(startNode).to(endNode)
.policy(DrivingRoutePlanOption.DrivingPolicy.ECAR_TIME_FIRST)
.trafficPolicy(DrivingRoutePlanOption.DrivingTrafficPolicy.ROUTE_PATH_AND_TRAFFIC));
}
}
規(guī)劃線路后需要將實(shí)時(shí)路況索引保存,為后面畫圖需要
// 設(shè)置路段實(shí)時(shí)路況索引
List<DrivingRouteLine.DrivingStep> allStep = selectedRouteLine.getAllStep();
mTrafficTextureIndexList.clear();
for (int j = 0; j < allStep.size(); j++) {
if (allStep.get(j).getTrafficList() != null && allStep.get(j).getTrafficList().length > 0) {
for (int k = 0; k < allStep.get(j).getTrafficList().length; k++) {
mTrafficTextureIndexList.add(allStep.get(j).getTrafficList()[k]);
}
}
}
要將路線規(guī)劃的路線上的路段再細(xì)分(切割),這樣小車移動(dòng)才會平滑
/**
* 將規(guī)劃好的路線點(diǎn)進(jìn)行截取
* 參考百度給的小車平滑軌跡移動(dòng)demo實(shí)現(xiàn)。(循環(huán)的算法不太懂)
* @param routeLine
* @param distance
* @return
*/
private ArrayList<LatLng> divideRouteLine(ArrayList<LatLng> routeLine, double distance) {
// 截取后的路線點(diǎn)的結(jié)果集
ArrayList<LatLng> result = new ArrayList<>();
mNewTrafficTextureIndexList.clear();
for (int i = 0; i < routeLine.size() - 1; i++) {
final LatLng startPoint = routeLine.get(i);
final LatLng endPoint = routeLine.get(i + 1);
double slope = getSlope(startPoint, endPoint);
// 是不是正向的標(biāo)示
boolean isYReverse = (startPoint.latitude > endPoint.latitude);
boolean isXReverse = (startPoint.longitude > endPoint.longitude);
double intercept = getInterception(slope, startPoint);
double xMoveDistance = isXReverse ? getXMoveDistance(slope, distance) :
-1 * getXMoveDistance(slope, distance);
double yMoveDistance = isYReverse ? getYMoveDistance(slope, distance) :
-1 * getYMoveDistance(slope, distance);
ArrayList<LatLng> temp1 = new ArrayList<>();
for (double j = startPoint.latitude, k = startPoint.longitude;
!((j > endPoint.latitude) ^ isYReverse) && !((k > endPoint.longitude) ^ isXReverse); ) {
LatLng latLng = null;
if (slope == Double.MAX_VALUE) {
latLng = new LatLng(j, k);
j = j - yMoveDistance;
} else if (slope == 0.0) {
latLng = new LatLng(j, k - xMoveDistance);
k = k - xMoveDistance;
} else {
latLng = new LatLng(j, (j - intercept) / slope);
j = j - yMoveDistance;
}
final LatLng finalLatLng = latLng;
if (finalLatLng.latitude == 0 && finalLatLng.longitude == 0) {
continue;
}
mNewTrafficTextureIndexList.add(mTrafficTextureIndexList.get(i));
temp1.add(finalLatLng);
}
result.addAll(temp1);
if (i == routeLine.size() - 2) {
result.add(endPoint); // 終點(diǎn)
}
}
return result;
}
最后是開啟子線程,對小車狀態(tài)進(jìn)行更新(車頭方向和小車位置)
/**
* 循環(huán)進(jìn)行移動(dòng)邏輯
*/
public void moveLooper() {
moveThread = new Thread() {
public void run() {
Thread thisThread = Thread.currentThread();
while (!exit) {
for (int i = 0; i < latLngs.size() - 1; ) {
if (exit) {
break;
}
for (int p = 0; p < latLngs.size() - 1; p++) {
// 這是更新索引的條件,這里總是為true
// 實(shí)際情況可以是:當(dāng)前誤差小于5米 DistanceUtil.getDistance(mCurrentLatLng, latLngs.get(p)) <= 5)
// mCurrentLatLng 這個(gè)小車的當(dāng)前位置得自行獲取得到
if (true) {
// 實(shí)際情況的索引更新 mIndex = p;
mIndex++; // 模擬就是每次加1
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "當(dāng)前索引:" + mIndex, Toast.LENGTH_SHORT).show();
}
});
break;
}
}
// 改變循環(huán)條件
i = mIndex + 1;
if (mIndex >= latLngs.size() - 1) {
exit = true;
break;
}
// 擦除走過的路線
int len = mNewTrafficTextureIndexList.subList(mIndex, mNewTrafficTextureIndexList.size()).size();
Integer[] integers = mNewTrafficTextureIndexList.subList(mIndex, mNewTrafficTextureIndexList.size()).toArray(new Integer[len]);
int[] index = new int[integers.length];
for (int x = 0; x < integers.length; x++) {
index[x] = integers[x];
}
if (index.length > 0) {
mPolyline.setIndexs(index);
mPolyline.setPoints(latLngs.subList(mIndex, latLngs.size()));
}
// 這里是小車的當(dāng)前點(diǎn)和下一個(gè)點(diǎn),用于確定車頭方向
final LatLng startPoint = latLngs.get(mIndex);
final LatLng endPoint = latLngs.get(mIndex + 1);
mHandler.post(new Runnable() {
@Override
public void run() {
// 更新小車的位置和車頭的角度
if (mMapView == null) {
return;
}
mMoveMarker.setPosition(startPoint);
mMoveMarker.setRotate((float) getAngle(startPoint,
endPoint));
}
});
try {
// 控制線程更新時(shí)間間隔
thisThread.sleep(TIME_INTERVAL);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
// 啟動(dòng)線程
moveThread.start();
}
相關(guān)文章
在Android項(xiàng)目中使用AspectJ的詳細(xì)攻詻
AspectJ是實(shí)現(xiàn)AOP的其中一款框架,內(nèi)部通過處理字節(jié)碼實(shí)現(xiàn)代碼注入,文章給大家提到AspectJ基礎(chǔ)語法和集成AspectJ的方式,對AspectJ在android中使用教程感興趣的朋友跟隨小編一起看看吧2021-06-06
Android開發(fā)實(shí)例之多點(diǎn)觸控程序
本文主要介紹 Android開發(fā)多點(diǎn)觸控,這里提供了詳細(xì)的資料和示例代碼,以及實(shí)現(xiàn)效果圖,有開發(fā)Android應(yīng)用需要這樣的功能的小伙伴可以參考下2016-08-08
Android自定義可左右滑動(dòng)和點(diǎn)擊的折線圖
這篇文章主要為大家詳細(xì)介紹了Android自定義可左右滑動(dòng)和點(diǎn)擊的折線圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
Android USB轉(zhuǎn)串口通信開發(fā)實(shí)例詳解
這篇文章主要介紹了 Android USB轉(zhuǎn)串口通信開發(fā)實(shí)例詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android仿支付寶中余額寶的數(shù)字動(dòng)畫效果
最近因?yàn)楣ぷ餍枰叻掠囝~寶數(shù)字動(dòng)畫效果,達(dá)到炫酷的數(shù)字動(dòng)畫效果,所以寫出了分享給大家,有需要的朋友可以直接拿來用,下面一起來看看。2016-08-08
Android BroadcastReceiver廣播注冊方式總結(jié)
這篇文章主要介紹了Android BroadcastReceiver廣播注冊方式總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-01-01
Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案
這篇文章主要為大家詳細(xì)介紹了Android底部導(dǎo)航欄的動(dòng)態(tài)替換方案,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
Android Gradle多渠道打包的實(shí)現(xiàn)方法
這篇文章主要介紹了Android Gradle多渠道打包的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
Android Studio安裝配置方法圖文詳細(xì)教程
這篇文章主要為大家介紹了Android Studio下載和配置圖文詳細(xì)教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
淺談Android實(shí)踐之ScrollView中滑動(dòng)沖突處理解決方案
涉及到了ViewPager,MapView,ListView,就需要ScrollView來做一下支援,這篇文章主要介紹了淺談Android實(shí)踐之ScrollView中滑動(dòng)沖突處理解決方案,有需要的可以來了解一下。2016-12-12

