百度地图实现小车规划路线后平滑移动功能
文章目的
项目开发所需,所以结合百度地图提供的小车平滑轨迹移动,自己写的demo
实现效果
代码下载
下载链接
下面是实现的关键步骤
集成百度地图
怎么集成自然是看百度地图开发平台提供的文档。
文档连接
规划线路
看百度地图的文档,写一个规划线路的工具类(驾车的)
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)); } }
规划线路后需要将实时路况索引保存,为后面画图需要
// 设置路段实时路况索引 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]); } } }
要将路线规划的路线上的路段再细分(切割),这样小车移动才会平滑
/** * 将规划好的路线点进行截取 * 参考百度给的小车平滑轨迹移动demo实现。(循环的算法不太懂) * @param routeLine * @param distance * @return */ private ArrayList<LatLng> divideRouteLine(ArrayList<LatLng> routeLine, double distance) { // 截取后的路线点的结果集 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); // 是不是正向的标示 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); // 终点 } } return result; }
最后是开启子线程,对小车状态进行更新(车头方向和小车位置)
/** * 循环进行移动逻辑 */ 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 // 实际情况可以是:当前误差小于5米 DistanceUtil.getDistance(mCurrentLatLng, latLngs.get(p)) <= 5) // mCurrentLatLng 这个小车的当前位置得自行获取得到 if (true) { // 实际情况的索引更新 mIndex = p; mIndex++; // 模拟就是每次加1 runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(mContext, "当前索引:" + mIndex, Toast.LENGTH_SHORT).show(); } }); break; } } // 改变循环条件 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())); } // 这里是小车的当前点和下一个点,用于确定车头方向 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 { // 控制线程更新时间间隔 thisThread.sleep(TIME_INTERVAL); } catch (InterruptedException e) { e.printStackTrace(); } } } } }; // 启动线程 moveThread.start(); }