最近在做一款跑步机的APP,需求是在地图上选定起点和终点,然后根据跑步机上设定的速度模拟用户在地图上沿着起点至终点运动。一般的地图开发,实时位置可通过手机GPS获取,然而本项目用户在跑步机上运动,用户在地图上的位置要根据跑步机设定的速度来模拟。对比了下百度,高德和腾讯地图,冲着高德地图的Demo和文档比较齐全,项目中选定高德地图作为地图开发。
转载请注明出处:www.facex.xyz,如有发现错误或有其他更好的实现方式可与我联系。
意外发现高德3D地图在12月9日更新了点平滑移动的api,细看之下非常符合本次需要,感谢高德开发人员,不然有得折腾了!
下文中的点指代用户在地图中的位置。
开撸之前,先上无码图。
1. 获取轨迹点
首先为简单起见,我事先准备好了起点和终点的经纬度,由于是模拟跑步路径,我选用步行出行路线规划获取跑步轨迹。规划成功后我们在onWalkRouteSearched的回调WalkRouteResult中取得轨迹点,只不过返回的轨迹点是一段一段的,为迎合点平滑移动的api,需要每一段的steps合并成一个list。
1.1 规划路径
根据起点和重点,调用高德api规划路径。
private void getRoute() {
RouteSearch routeSearch = new RouteSearch(this);
routeSearch.setRouteSearchListener(this);
RouteSearch.FromAndTo fromAndTo = new RouteSearch.FromAndTo(startPoint, endPoint);
RouteSearch.WalkRouteQuery query = new RouteSearch.WalkRouteQuery(fromAndTo, RouteSearch.WalkDefault);
routeSearch.calculateWalkRouteAsyn(query);
}
1.2 处理回调数据
取出每一段点得经纬度,合并成一个list。
@Override
public void onWalkRouteSearched(WalkRouteResult walkRouteResult, int i) {
mLatLngs = new ArrayList<>();
mMap.clear();
if (i == 1000) {
if (walkRouteResult != null && walkRouteResult.getPaths() != null && walkRouteResult.getPaths().size() > 0) {
WalkPath walkPath = walkRouteResult.getPaths().get(0);
mAllDistance = walkPath.getDistance();
List walkSteps = walkPath.getSteps();
for (int j = 0; j < walkSteps.size(); j++) {
List latLonPoints = walkSteps.get(j).getPolyline();
for (int k = 0; k < latLonPoints.size(); k++) {
LatLonPoint latLonPoint = latLonPoints.get(k);
mLatLngs.add(new LatLng(latLonPoint.getLatitude(), latLonPoint.getLongitude()));
}
}
/**
* 在地图上显示规划路径
*/
addPolylineInPlayGround();
}
}
}
2. 显示路径
简单地在地图上显示路径
private void addPolylineInPlayGround() {
List list = new ArrayList<>(mLatLngs);
mMap.addPolyline(new PolylineOptions().addAll(list).width(10).color(Color.RED));
/**
* 改变地图视口
*/
LatLngBounds bounds = new LatLngBounds(list.get(0), list.get(list.size() - 2));
mMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 200));
}
3. 开始移跑步
用户选择开始跑步时,地图中的点要根据跑步机上设定的速度模拟在地图中运动。根据高德地图点平滑移动开发文档,初始化一些数据和设置,开始移动点。高德已经为我们做好了处理,按我们设置的总移动时间从起点平滑地移动至终点。
private void startRun() {
mPoints = new ArrayList<>(mLatLngs);
mSmoothMoveMarker = new SmoothMoveMarker(mMap);
/**
* 设置滑动的图标
*/
mSmoothMoveMarker.setDescriptor(BitmapDescriptorFactory.fromResource(R.drawable.ic_navigation_green_a700_24dp));
LatLng drivePoint = mPoints.get(0);
Pair pair = SpatialRelationUtil.calShortestDistancePoint(mPoints, drivePoint);
mPoints.set(pair.first, drivePoint);
List subList = mPoints.subList(pair.first, mPoints.size());
/**
* 设置滑动的轨迹左边点
*/
mSmoothMoveMarker.setPoints(subList);
/**
* 设置滑动的总时间
*/
mSmoothMoveMarker.setTotalDuration((int) (mAllDistance / mSpeed));
mSmoothMoveMarker.startSmoothMove();
mSmoothMoveMarker.setMoveListener(new SmoothMoveMarker.MoveListener() {
@Override
public void move(double v) {
/**
* 最后一次取到的值即为暂停时的数据,getIndex()方法取到的是当前list的下标,v为剩余距离
*/
mPauseIndex = mSmoothMoveMarker.getIndex();
mDistanceRemain = v;
}
});
}
总移动时间=轨迹长度/运动速度
4. 暂停跑步
用户选择暂停跑步时,我们需要获取到暂停的剩余的轨迹长度和剩余的轨迹点。暂停时的数据已经在上一步取到了,这里只是停止点得移动。
private void pauseRun() {
mSmoothMoveMarker.stopMove();
}
5. 继续跑步
用户选择选择继续跑步时,我们需要在原先暂停的位置继续移动点。
private void continueRun() {
LatLng drivePoint = mPoints.get(mPauseIndex + 1);
Pair pair = SpatialRelationUtil.calShortestDistancePoint(mPoints, drivePoint);
mPoints.set(pair.first, drivePoint);
mPoints = mPoints.subList(pair.first, mPoints.size());
/**
* 重新设置剩余的轨迹点
*/
mSmoothMoveMarker.setPoints(mPoints);
/**
* 重新设置滑动时间
*/
mSmoothMoveMarker.setTotalDuration((int) (mDistanceRemain / mSpeed));
mSmoothMoveMarker.startSmoothMove();
}
6. 结束跑步
当点滑动到达终点时,结束跑步。
7. 总结
通过以上步骤可以较好的实现需求,以上只是简单的记录实现步骤,具体的细节美化可以按照各自的需求处理。
项目Demo:SimulateRunning。
参考文献:高德地图点平滑移动api