该软件可实现获取用户位置信息、进行运动轨迹的记录及按日期查询轨迹等功能。在进入软件前需要进行登陆注册,获取用户名作为所记录轨迹的标识。源码链接:https://download.csdn.net/download/weixin_39006917/11255470
对软件的相关内容介绍:
一、开发前的准备
1.在进行开发前需要下载Android Studio软件,并搭建好JDK环境。
2.在百度地图开放平台http://lbsyun.baidu.com/apiconsole/key中创建应用。
应用名称的内容可按个人喜好填写,应用类型选择Android SDK,默认情况下启用服务为全选。关于发布版SHA1和开发版SHA1的获取,笔者主要参考博主朽木不朽提供的方法:https://blog.csdn.net/yuzhiqiang_1993/article/details/79307205。PackageName(包名)是Android Studio中对应项目的包名。如图中包名为package后的内容。
全部填写完成后,生成安全码,即AK。如下图中应用编号为16020055的Android应用的AK为ZKKZn....。
获取AK后在Android Studio中的AndroidManifest.xml文件中进行声明。
3.在百度鹰眼轨迹中创建服务。http://lbsyun.baidu.com/trace/admin/service。创建完成后获得唯一服务ID。在后续轨迹相关功能开发时会用到服务ID。
二、运动轨迹记录的开发实现
1.初始化轨迹服务
2.采集定位点
3.绘制轨迹
public class DynamicDemo extends Activity implements SensorEventListener {
// 定位相关
LocationClient mLocClient;
public MyLocationListenner myListener = new MyLocationListenner();
private int mCurrentDirection = 0;
private double mCurrentLat = 0.0;
private double mCurrentLon = 0.0;
MapView mMapView;
BaiduMap mBaiduMap;
private TextView info;
private RelativeLayout progressBarRl;
private Boolean isRunning = false;
boolean isFirstLoc = true; // 是否首次定位
private MyLocationData locData;
float mCurrentZoom = 18f;//默认地图缩放比例值
private SensorManager mSensorManager;
protected OnStartTraceListener startTraceListener;
protected OnStopTraceListener stopTraceListener;
LBSTraceClient client = new LBSTraceClient(DynamicDemo.this);//实例化轨迹服务客户端
protected static Trace trace = null; // 轨迹服务
long serviceId = 213082;//鹰眼服务ID
private int gatherInterval = 5; // 采集周期(单位 : 秒)
private int packInterval = 10;//打包周期(单位 : 秒)
String entityName ;//entity标识
//轨迹服务类型(0 : 不上传位置数据,也不接收报警信息; 1 : 不上传位置数据,但接收报警信息;2 : 上传位置数据,且接收报警信息)
int traceType = 2;
//起点图标
BitmapDescriptor startBD = BitmapDescriptorFactory.fromResource(R.drawable.ic_me_history_startpoint);
//终点图标
BitmapDescriptor finishBD = BitmapDescriptorFactory.fromResource(R.drawable.ic_me_history_finishpoint);
List<LatLng> points = new ArrayList<LatLng>();//位置点集合
Polyline mPolyline;//运动轨迹图层
LatLng last = new LatLng(0, 0);//上一个定位点
MapStatus.Builder builder;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dyn);
Bundle bundle = this.getIntent().getExtras();
String name=bundle.getString("Name2");
entityName = name;
initView();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);// 获取传感器管理服务
// 地图初始化
mMapView = (MapView) findViewById(R.id.bmapView);
mBaiduMap = mMapView.getMap();
// 开启定位图层
mBaiduMap.setMyLocationEnabled(true);
mBaiduMap.setMyLocationConfiguration(new MyLocationConfiguration(
com.baidu.mapapi.map.MyLocationConfiguration.LocationMode.FOLLOWING, true, null));
/**
* 添加地图缩放状态变化监听,当手动放大或缩小地图时,拿到缩放后的比例,然后获取到下次定位,
* 给地图重新设置缩放比例,否则地图会重新回到默认的mCurrentZoom缩放比例
*/
mBaiduMap.setOnMapStatusChangeListener(new BaiduMap.OnMapStatusChangeListener() {
@Override
public void onMapStatusChangeStart(MapStatus arg0) {
// TODO Auto-generated method stub
}
@Override
public void onMapStatusChangeFinish(MapStatus arg0) {
mCurrentZoom = arg0.zoom;
}
@Override
public void onMapStatusChange(MapStatus arg0) {
// TODO Auto-generated method stub
}
});
// 定位初始化
mLocClient = new LocationClient(this);
mLocClient.registerLocationListener(myListener);
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Device_Sensors);//只用gps定位,需要在室外定位。
option.setOpenGps(true); // 打开gps
option.setCoorType("bd09ll"); // 设置坐标类型
option.setScanSpan(1000);
mLocClient.setLocOption(option);
}
private void initView() {
initTrace();
Button start = (Button) findViewById(R.id.buttonStart);
Button finish = (Button) findViewById(R.id.buttonFinish);
info = (TextView) findViewById(R.id.info);
progressBarRl = (RelativeLayout) findViewById(R.id.progressBarRl);
start.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mLocClient != null && !mLocClient.isStarted()) {
mLocClient.start();
startService();
progressBarRl.setVisibility(View.VISIBLE);
info.setText("GPS信号搜索中,请稍后...");
mBaiduMap.clear();
}
}
});
finish.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mLocClient != null && mLocClient.isStarted()) {
mLocClient.stop();
stopService();
progressBarRl.setVisibility(View.GONE);
if (isFirstLoc) {
points.clear();
last = new LatLng(0, 0);
return;
}
MarkerOptions oFinish = new MarkerOptions();// 地图标记覆盖物参数配置类
oFinish.position(points.get(points.size() - 1));
oFinish.icon(finishBD);// 设置覆盖物图片
mBaiduMap.addOverlay(oFinish); // 在地图上添加此图层
//复位
points.clear();
last = new LatLng(0, 0);
isFirstLoc = true;
}
}
});
}
private void initTrace(){
trace = new Trace(getApplicationContext(), serviceId, entityName,traceType);
initStartListener();
initStopListener();
// 设置采集周期
client.setInterval(gatherInterval, packInterval);
}
private void initStartListener() {
//实例化开启轨迹服务回调接口
startTraceListener = new OnStartTraceListener() {
//开启轨迹服务回调接口(arg0 : 消息编码,arg1 : 消息内容,详情查看类参考)
@Override
public void onTraceCallback(int arg0, String arg1) {
Log.i(TAG,"onTraceCallback开启轨迹服务回调" + arg0 + ",消息内容 : " + arg1 + "]");
//showMessage("开启轨迹服务");///添加toast
if (0 == arg0 || 10006 == arg0 || 10008 == arg0) {
isRunning = true;
// startRefreshThread(true);
}
}
//轨迹服务推送接口(用于接收服务端推送消息,arg0 : 消息类型,arg1 : 消息内容,详情查看类参考)
@Override
public void onTracePushCallback(byte arg0, String arg1) {
Log.i(TAG,"轨迹服务推送接口消息 [消息类型 : " + arg0 + ",消息内容 : " + arg1 + "]");
}
};
}
private void initStopListener() {
stopTraceListener = new OnStopTraceListener() {
public void onStopTraceSuccess() {// 轨迹服务停止成功
Log.i(TAG,"onStopTraceSuccess关闭轨迹服务");
// showMessage("停止轨迹服务成功");
// locHander.sendEmptyMessage(RUN_STOP);
isRunning = false;
// startRefreshThread(false);
}
// 轨迹服务停止失败(arg0 : 错误编码,arg1 : 消息内容,详情查看类参考)
public void onStopTraceFailed(int arg0, String arg1) {
}
};
}
private void startService() {
if (! isRunning) {
Log.i(TAG, "Start StepService");
isRunning = true;
client.startTrace(trace, startTraceListener);//开启轨迹服务
}
}
private void stopService() {
Log.i(TAG, "stopService");
client.stopTrace(trace, stopTraceListener);
}
double lastX;
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
double x = sensorEvent.values[SensorManager.DATA_X];
if (Math.abs(x - lastX) > 1.0) {
mCurrentDirection = (int) x;
if (isFirstLoc) {
lastX = x;
return;
}
locData = new MyLocationData.Builder().accuracy(0)
// 此处设置开发者获取到的方向信息,顺时针0-360
.direction(mCurrentDirection).latitude(mCurrentLat).longitude(mCurrentLon).build();
mBaiduMap.setMyLocationData(locData);
}
lastX = x;
}
@Override
public void onAccuracyChanged(Sensor sensor, int i) {
}
/**
* 定位SDK监听函数
*/
public class MyLocationListenner implements BDLocationListener {
@Override
public void onReceiveLocation(final BDLocation location) {
if (location == null || mMapView == null) {
return;
}
//注意这里只接受gps点,需要在室外定位。
if (location.getLocType() == BDLocation.TypeGpsLocation) {
info.setText("GPS信号弱,请稍后...");
if (isFirstLoc) {//首次定位
//第一个点很重要,决定了轨迹的效果,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); // 在地图上添加此图层
progressBarRl.setVisibility(View.GONE);
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 void locateAndZoom(final BDLocation location, LatLng ll) {
mCurrentLat = location.getLatitude();
mCurrentLon = location.getLongitude();
locData = new MyLocationData.Builder().accuracy(0)
// 此处设置开发者获取到的方向信息,顺时针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()));
}
/**
* 首次定位很重要,选一个精度相对较高的起始点
* 注意:如果一直显示gps信号弱,说明过滤的标准过高了,
你可以将location.getRadius()>25中的过滤半径调大,比如>40,
并且将连续5个点之间的距离DistanceUtil.getDistance(last, ll ) > 5也调大一点,比如>10,
这里不是固定死的,你可以根据你的需求调整,如果你的轨迹刚开始效果不是很好,你可以将半径调小,两点之间距离也调小,
gps的精度半径一般是10-50米
*/
private LatLng getMostAccuracyLocation(BDLocation location){
if (location.getRadius()>40) {//gps位置精度大于40米的点直接弃用
return null;
}
LatLng ll = new LatLng(location.getLatitude(), location.getLongitude());
if (DistanceUtil.getDistance(last, ll ) > 10) {
last = ll;
points.clear();//有任意连续两点位置大于10,重新取点
return null;
}
points.add(ll);
last = ll;
//有5个连续的点之间的距离小于10,认为gps已稳定,以最新的点为起始点
if(points.size() >= 5){
points.clear();
return ll;
}
return null;
}
@Override
protected void onPause() {
mMapView.onPause();
super.onPause();
}
@Override
protected void onResume() {
mMapView.onResume();
super.onResume();
// 为系统的方向传感器注册监听器
mSensorManager.registerListener(this, mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION),
SensorManager.SENSOR_DELAY_UI);
}
@Override
protected void onStop() {
// 取消注册传感器监听
mSensorManager.unregisterListener(this);
super.onStop();
}
@Override
protected void onDestroy() {
// 退出时销毁定位
mLocClient.unRegisterLocationListener(myListener);
if (mLocClient != null && mLocClient.isStarted()) {
mLocClient.stop();
}
// 关闭定位图层
mBaiduMap.setMyLocationEnabled(false);
mMapView.getMap().clear();
mMapView.onDestroy();
mMapView = null;
startBD.recycle();
finishBD.recycle();
super.onDestroy();
}
}
三、历史轨迹查询
public class RecordActivity extends Activity {
private String TAG = "RecordActivity1";
private Button btn_record;
private MapView mapView;
private static BaiduMap mBaiduMap;
private static PolylineOptions polyline = null;// 路线覆盖物
Marker startMarker = null;// 起点图标覆盖物
Marker endMarker = null;// 终点图标覆盖物
OverlayOptions option1 = null;
OverlayOptions option2 = null;
protected static OverlayOptions overlay;// 覆盖物
private static BitmapDescriptor bmStart;// 起点图标
private static BitmapDescriptor bmEnd;// 终点图标
protected static MapStatusUpdate msUpdate = null;
LBSTraceClient client = new LBSTraceClient(RecordActivity.this);//实例化轨迹服务客户端
protected static Trace trace = null;// 轨迹服务
long serviceId = 213082;//鹰眼服务ID
String entityName ;//entity标识
int traceType = 2;//轨迹服务类型(0 : 不上传位置数据,也不接收报警信息; 1 : 不上传位置数据,但接收报警信息;2 : 上传位置数据,且接收报警信息)
protected static OnTrackListener trackListener = null;//Track监听器
private int startTime = 0;
private int endTime = 0;
private int year = 0;//查询日期的年月日
private int month = 0;
private int day = 0;
String record_date;
private static int isProcessed = 0;
boolean isfirst = true;
LatLng pouse,restart;
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MsgConfig.SHOW_INFO:
String text = (String) msg.getData().get("content");
btn_record.setText("选择日期");
Toast.makeText(RecordActivity.this, text, Toast.LENGTH_LONG).show();
break;
case MsgConfig.SHOW_HISTORY:
btn_record.setText("查询完毕");
break;
}
}
;
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_record);
Bundle bundle = this.getIntent().getExtras();
String name=bundle.getString("Name1");
entityName = name;
initView();
initTrace();
}
private void initTrace() {
trace = new Trace(RecordActivity.this, serviceId, entityName, traceType);
initOnTrackListener();
client.setOnTrackListener(trackListener);
mapView = (MapView) findViewById(R.id.record_bmapView);
mBaiduMap = mapView.getMap();
mapView.showZoomControls(false);
}
/**
* 初始化OnTrackListener
*/
private void initOnTrackListener() {
trackListener = new OnTrackListener() {
@Override
public void onRequestFailedCallback(String arg0) { // 请求失败回调接口
Looper.prepare();
Looper.loop();
}
@Override
public void onQueryHistoryTrackCallback(String arg0) {// 查询历史轨迹回调接口
Log.i(TAG, "track请求成功回调接口消息 : " + arg0);
showHistoryTrack(arg0);
Message msg = new Message();
msg.what = MsgConfig.SHOW_HISTORY;
mHandler.sendMessage(msg);
super.onQueryHistoryTrackCallback(arg0);
}
};
}
//用来展示历史轨迹
private void showHistoryTrack(String arg0) {
Log.i(TAG, "查询回调展示,拿到回调的数据1");
Log.i(TAG, "查询回调展示,拿到回调的数据2");
HistoryTrackData historyTrackData = GsonService.parseJson(arg0,
HistoryTrackData.class);
List<LatLng> latLngList = new ArrayList<LatLng>();
if (historyTrackData != null && historyTrackData.getStatus() == 0) {
Log.i(TAG, "查询回调展示,拿到回调的数据,数据不为空且数据status为0");
if (historyTrackData.getListPoints() != null) {
latLngList.addAll(historyTrackData.getListPoints());
}
drawHistoryTrack(latLngList, historyTrackData.distance);// 绘制历史轨迹
} else {
Log.i(TAG, "查询回调展示,拿到回调的数据,数据为空或数据status为0");
Message msg = new Message();
Bundle data = new Bundle();
data.putString("content", "当天没有进行走动");
msg.setData(data);
msg.what = MsgConfig.SHOW_INFO;
mHandler.sendMessage(msg);
Log.i(TAG, "查询回调展示,拿到回调的数据,数据为空或数据status为0,在toast后面");
}
}
/**
* 绘制历史轨迹
*/
private void drawHistoryTrack(final List<LatLng> points, final double distance) {
Log.i(TAG, "查询回调展示,拿到回调的数据,画出轨迹");
mBaiduMap.clear();// 绘制新覆盖物前,清空之前的覆盖物
if (points == null || points.size() == 0) {
Looper.prepare();
Message msg = new Message();
Bundle data = new Bundle();
data.putString("content", "当天没有进行走动");
msg.setData(data);
msg.what = MsgConfig.SHOW_INFO;
mHandler.sendMessage(msg);
Looper.loop();
resetMarker();
} else if (points.size() > 1) {
for(int i=0;i<3;i++)
{
points.remove(points.size() - 1);
}
pouse = points.get(0);
restart = points.get(points.size() - 1);
LatLng llC = points.get(0);
LatLng llD = points.get(points.size() - 1);
LatLngBounds bounds = new LatLngBounds.Builder()
.include(llC).include(llD).build();
msUpdate = MapStatusUpdateFactory.newLatLngBounds(bounds);
bmStart = BitmapDescriptorFactory.fromResource(R.drawable.ic_me_history_startpoint);
bmEnd = BitmapDescriptorFactory.fromResource(R.drawable.ic_me_history_finishpoint);
// 添加终点图标
option2 = new MarkerOptions()
.position(restart).icon(bmStart)
.zIndex(9).draggable(true);
option1 = new MarkerOptions().position(pouse)
.icon(bmEnd).zIndex(9).draggable(true);
startMarker = (Marker) mBaiduMap.addOverlay(option2);
// 添加起点图标
endMarker = (Marker) mBaiduMap.addOverlay(option1);
// 添加路线(轨迹)
polyline = new PolylineOptions().width(10)
.color(Color.RED).points(points);
addMarker();
Message msg = new Message();
msg.what = MsgConfig.SHOW_HISTORY;
mHandler.sendMessage(msg);
Looper.prepare();
Looper.loop();
isfirst = false;
pouse=points.get(0);
} else {
Message msg = new Message();
Bundle data = new Bundle();
data.putString("content", "当天的走动量太少,被系统忽略");
msg.setData(data);
msg.what = MsgConfig.SHOW_INFO;
mHandler.sendMessage(msg);
}
}
/**
* 重置覆盖物
*/
private void resetMarker() {
startMarker = null;
endMarker = null;
polyline = null;
}
/**
* 添加地图覆盖物
*/
protected static void addMarker() {
if (null != msUpdate) {
mBaiduMap.setMapStatus(msUpdate);
}
// 路线覆盖物
if (null != polyline) {
mBaiduMap.addOverlay(polyline);
}
// 实时点覆盖物
if (null != overlay) {
mBaiduMap.addOverlay(overlay);
}
}
private void initView() {
btn_record = (Button) findViewById(R.id.record_date);
btn_record.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
btn_record.setText("正在查询历史轨迹,请稍候");
queryTrack();
}
});
}
/**
* 轨迹查询(先选择日期,再根据是否纠偏,发送请求)
*/
private void queryTrack() {
Log.i(TAG, "开始查询轨迹,选择日期");
// 选择日期
int[] date = null;
DisplayMetrics dm = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(dm);
int width = dm.widthPixels;
int height = dm.heightPixels;
if (year == 0 && month == 0 && day == 0) {
String curDate = DateUtils.getCurrentDate();
date = DateUtils.getYMDArray(curDate, "-");
}
if (date != null) {
year = date[0];
month = date[1];
day = date[2];
}
DateDialog dateDiolog = new DateDialog(RecordActivity.this, new PriorityListener() {
public void refreshPriorityUI(String sltYear, String sltMonth,
String sltDay, CallBack back) {
Log.i(TAG, sltYear + sltMonth + sltDay);
year = Integer.parseInt(sltYear);
month = Integer.parseInt(sltMonth);
day = Integer.parseInt(sltDay);
if (month > 9) {
if (day > 9) {
record_date = year + "-" + month + "-" + day;
} else {
record_date = year + "-" + month + "-" + "0" + day;
}
} else {
if (day > 9) {
record_date = year + "-" + "0" + month + "-" + day;
} else {
record_date = year + "-" + "0" + month + "-" + "0" + day;
}
}
String st = year + "年" + month + "月" + day + "日0时0分0秒";
String et = year + "年" + month + "月" + day + "日23时59分59秒";
startTime = Integer.parseInt(DateUtils.getTimeToStamp(st));
endTime = Integer.parseInt(DateUtils.getTimeToStamp(et));
back.execute();
}
}, new CallBack() {
public void execute() {
Log.i(TAG, "执行查询轨迹");
queryHistoryTrack();
}
}, year, month, day, width, height, "选择日期", 1);
Window window = dateDiolog.getWindow();
window.setGravity(Gravity.CENTER); // 此处可以设置dialog显示的位置
dateDiolog.setCancelable(true);
dateDiolog.show();
}
/**
* 查询历史轨迹
*/
private void queryHistoryTrack() {
int simpleReturn = 0;// 是否返回精简的结果(0 : 否,1 : 是)
// 开始时间
if (startTime == 0) {
startTime = (int) (System.currentTimeMillis() / 1000 - 12 * 60 * 60);
}
if (endTime == 0) {
endTime = (int) (System.currentTimeMillis() / 1000);
}
int pageSize = 1000;// 分页大小
int pageIndex = 1; // 分页索引
client.queryHistoryTrack(serviceId, entityName, simpleReturn, startTime, endTime,
pageSize, pageIndex, trackListener);
Log.i(TAG, "调用client开始查询");
}
@Override
protected void onDestroy() {
super.onDestroy();
client.onDestroy();
mapView.onDestroy();
}
}
第二部分的轨迹记录过程将采集的定位点集合上传至百度服务器,历史轨迹查询部分再回调记录的定位点,绘制出轨迹。在百度的Web轨迹管理台中进入对应的已创建服务,可实现轨迹的管理和终端用户的管理。