目录
1.GPX文件解析
2.地图轨迹绘制
3.海拔示意图绘制
4.行进模拟
开始(暂停):
前进实现:
计时模块:
其他参数
1.GPX文件解析
2.地图轨迹绘制
轨迹绘制:
利用GPX解析得到的数据在百度地图SDK上利用折现的绘制方法,绘制出相应的地图路线图。
public void drawroad(){
List<LatLng> points = new ArrayList<LatLng>();
//构建折线点坐标
for(int i=0;i<longitudes.size();i++){
//System.out.println(longitudes.get(i) );
// longitudes.size();
LatLng p = new LatLng(latitudes.get(i), longitudes.get(i));
points.add(p);
}
//设置折线的属性
OverlayOptions mOverlayOptions = new PolylineOptions()
.width(10)
.color(0xAAFF0000)
.points(points);
//在地图上绘制折线
//mPloyline 折线对象
Overlay mPolyline = mBaiduMap.addOverlay(mOverlayOptions);
}
自身点标记:
在行进过程中始终标记自身所处位置(海拔示意图也会始终标记自身位置)。
public void drawpoint(int i){
//定义Maker坐标点
LatLng point = new LatLng(latitudes.get(i), longitudes.get(i));
//构建Marker图标
BitmapDescriptor bitmap = BitmapDescriptorFactory
.fromResource(R.drawable.me3);
//构建MarkerOption,用于在地图上添加Marker
OverlayOptions option = new MarkerOptions()
.position(point)
.icon(bitmap);
//在地图上添加Marker,并显示
//在地图上批量添加
mPolyline2 =mBaiduMap.addOverlay(option);
}
3.海拔示意图绘制
将GPX文件解析得到的海拔数据,按数据的多少不同按比例进行绘制(实质为直线的绘制)
@RequiresApi(api = Build.VERSION_CODES.N)
public void drawele(int me){
imageView=(ImageView) findViewById(R.id.ele);
//确定最高,最低海拔
Double max = Collections.max(elevations);
Double min = Collections.min(elevations);
int max1= Math.toIntExact(Math.round(max));
int min1= Math.toIntExact(Math.round(min));
runOnUiThread(new Runnable() {
@Override
public void run() {
top.setText(String.valueOf(max1)+"M");
low.setText(String.valueOf(min1)+"M");
}
});
if(max1-min1>h){
double y= (max-min)/h;
Log.e("TAG", "drawele: "+y );
yinterval= Math.toIntExact((long) Math.ceil(y)); //得到最大值和高的关系,依此比例绘制高度
}else{
double y= h/(max-min);
Log.e("TAG", "drawele: "+y );
yinterval= Math.toIntExact((long) Math.ceil(y)); //得到最大值和高的关系,依此比例绘制高度
}
Log.e("max min", "drawele: "+max+"__"+min+"___"+yinterval+" ++"+h );
//开始绘制海拔示意图
Bitmap newb = Bitmap.createBitmap(w , h, Bitmap.Config.ARGB_8888);
Canvas canvasTemp = new Canvas(newb);
//Canvas canvasTemp2=new Canvas(newb);
canvasTemp.drawColor(Color.TRANSPARENT);
Paint p = new Paint();
//防锯齿
p.setAntiAlias(true);
p.setStyle(Paint.Style.STROKE);//STROKE,FILL
p.setStrokeWidth(5);
p.setColor(Color.LTGRAY);
p.setTextAlign(Paint.Align.CENTER);
p.setColor(Color.BLACK);
p.setStyle(Paint.Style.FILL);//STROKE,FILL
//判断是否为初始化,将位置置为0
if(me==0){
p.setColor(Color.RED);
canvasTemp.drawCircle(0,(float) (h-(elevations.get(0)-min1)/(max1-min1)*h),10,p);
p.setColor(Color.BLACK);
}
/*
if(flagwidth==1){
}else{
drawele(me);
}*/
//海拔示意图,公式 h-(H-min1)/(max1-min1)*h,H为当前绘制点海拔,h为屏幕高度
if(elevations.size()>w) { //点多宽度少
Log.e(TAG, "drawele:点多宽度少!!!!!!!!!!!!!!!!!!!! " );
flagwidth=0;
// /*double xinterval1=1
xinterval=(float) elevations.size()/(float) w; //大于1,得到手机屏幕显示宽度和需要绘制的点个数关系,以此比例取点
for(int i=1;i<=w;i++){ //i为屏幕宽度点计数, *xinterval得到按比例取得的高度点, XX数组存储每个输出的海拔点的角标(位置,第几个)
// if((int)(i)*xinterval>elevations.size()/2) p.setColor(Color.RED);
// canvasTemp.drawLine(0, (float)( (h-520)/yinterval),1,(float)( (h-100)/yinterval),p);
canvasTemp.drawLine(i-1, (float) (h-((elevations.get((int)((i-1)*xinterval>elevations.size()-1?elevations.size()-1:(i-1)*xinterval))-min1)/(max1-min1))*h),i,(float) (h-((elevations.get((int)((i)*xinterval>elevations.size()-1?elevations.size()-1:(i)*xinterval))-min1)/(max1-min1))*h),p);
//canvasTemp.drawLine(i-1, (float) (h-elevations.get((int)((i-1)*xinterval>elevations.size()-1?elevations.size()-1:(i-1)*xinterval))/yinterval),i,(float) (h-elevations.get((int)(i*xinterval>elevations.size()-1?elevations.size()-1:i*xinterval))/yinterval),p);
XX[i-1]=(int)(i*xinterval>elevations.size()-1?elevations.size()-1:i*xinterval);
//Log.e("000000000000000000", "drawele: "+(i-xinterval)+"__"+(float) (h-elevations.get(i-1)/yinterval) );
if(XX[i-1]==me){ //计数点me 标记当前输出点的位置,等于绘制的海拔的某一点,在这个位置标记红点
p.setColor(Color.RED);
canvasTemp.drawCircle(i,(float) (h-((elevations.get((int)((i-1)*xinterval>elevations.size()-1?elevations.size()-1:(i-1)*xinterval))-min1)/(max1-min1))*h),10,p);
//canvasTemp.drawCircle(i,(float) (h-elevations.get(XX[i-1])/yinterval),10,p);
p.setColor(Color.BLACK);
}
}
}
else if(elevations.size()<w){
Log.e(TAG, "drawele:未修改部分!!!!!!!!!!!!!!!!!!!! " );
flagwidth=1;
xinterval=(float) w/(float) elevations.size();
//
for(int i=1;i<elevations.size()-1;i++){
canvasTemp.drawLine((i-1)*xinterval>w?w:(i-1)*xinterval, (float) (h-(elevations.get(i-1)-min1)/(max1-min1)*h),i*xinterval>w?w:i*xinterval,(float) (h-(elevations.get(i)-min1)/(max1-min1)*h),p);
// canvasTemp.drawLine((i-1)*xinterval>w?w:(i-1)*xinterval, (float) (h-elevations.get(i-1)/yinterval),i*xinterval>w?w:i*xinterval,(float) (h-elevations.get(i)/yinterval),p);
//Log.e("000000000000000000", "drawele: "+(i-xinterval)+"__"+(float) (h-elevations.get(i-1)/yinterval) );
if(i-1==me){
p.setColor(Color.RED);
canvasTemp.drawCircle(i*xinterval>w?w:i*xinterval,(float) (h-(elevations.get(i)-min1)/(max1-min1)*h),10,p);
//canvasTemp.drawCircle(i*xinterval>w?w:i*xinterval,(float) (h-elevations.get(i)/yinterval),10,p);
p.setColor(Color.BLACK);
}
}
}
runOnUiThread(new Runnable() {
@Override
public void run() {
imageView.setImageBitmap(newb);
}
});
}
4.行进模拟
开始(暂停):
此部分实现按钮的功能和信息展示的转换,在停止时按钮显示”开始“,点击按钮后,相应的计时模块开始进行计时,按钮信息显示为”暂停“,点击按钮,计时模块停止计时,按钮又显示为”开始“,再次点击,计时继续,按钮显示为”暂停“,如此循环。
public void startgo(View view) {
if(!stop){
runOnUiThread(new Runnable() {
@Override
public void run() {
startgo.setText("暂停");
}
});
TIME2 = System.currentTimeMillis();
if(Time3!=0) {
Time4+=TIME2-Time3;
}
stop=!stop;
}else{
runOnUiThread(new Runnable() {
@Override
public void run() {
startgo.setText("开始");
}
});
Time3=System.currentTimeMillis();
stop=!stop;
}
if(!Timestart) {
if(!ok){
ok=!ok;
}
Timestart = true;
TIME();
}
}
前进实现:
自身标记点的刷新,各项数据的刷新(重新计算)
@RequiresApi(api = Build.VERSION_CODES.N)
public void StepIn() {
if(true) {
if (me >= latitudes.size()||me>=elevations.size()-1) {
//Toast.makeText(this, "已到达终点", Toast.LENGTH_SHORT).show();
Log.e(TAG, "StepIn: "+"已到达终点" );
} else {
cargo++;
Miles += 2;
runOnUiThread(new Runnable() {
@Override
public void run() {
M.setText(String.valueOf(Miles) + "m");
}
});
if (Miles >= (distance.getDistance(latitudes.get(me), longitudes.get(me), latitudes.get(0), longitudes.get(0)))) {
mPolyline2.remove();
drawpoint(me);
/*//移动距离计算
Miles = Miles + distance.getDistance(latitudes.get(me - 1), longitudes.get(me - 1), latitudes.get(me), longitudes.get(me));
M.setText(String.valueOf(Miles) + "m");*/
me++;
//根据flagwidth的值,选择高度示意点的刷新方式(取样,还是拉伸)
//取样刷新
if (flagwidth == 0 && me > XX[xx] && me <= latitudes.size()) {
Log.e("me%xinterval==0", "stepin: " + me + "++++" + xinterval);
drawele(XX[xx]);
if (xx < w - 1) xx++;
} else if (flagwidth == 1) { //拉伸刷新,所有点都输出
drawele(me);
}
//总和所有爬升的高度
if (elevations.get(me - 1) - elevations.get(me - 2) > 0) {
Hight += elevations.get(me - 1) - elevations.get(me - 2);
String str = String.format("%.2f", Hight);
double Hight2 = Double.parseDouble(str);
runOnUiThread(new Runnable() {
@Override
public void run() {
H.setText(String.valueOf(Hight2) + "m");
}
});
}
//计算坡度,坡度=(高程差/水平距离)x100%。计算下一步设计的坡度,及即将到来的路径坡度
//Miles = Miles + distance.getDistance(latitudes.get(me - 1), longitudes.get(me - 1), latitudes.get(me), longitudes.get(me));
gradient = ((elevations.get(me) - elevations.get(me - 1))) / distance.getDistance(latitudes.get(me - 1), longitudes.get(me - 1), latitudes.get(me), longitudes.get(me));
String str3 = String.format("%.2f", gradient);
//double gr = Double.parseDouble(str3);
Log.e("--------------", "stepin: " + str3);
runOnUiThread(new Runnable() {
@Override
public void run() {
G.setText(str3);
}
});
}
}
}
}
计时模块:
开始与暂停控制的就是此模块的计时功能。
private void TIME(){
if(Timestart) {
TIME1 = System.currentTimeMillis();
new Thread(new Runnable() {
SimpleDateFormat sd = new SimpleDateFormat("HH:mm:ss");
@Override
public void run() {
while (true&&Timestart) {
if (stop) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
TIME2 = System.currentTimeMillis();
TIME = TIME2 - TIME1-Time4;
// Log.e("______", "run: "+hours+":"+minute+":"+second );
sd.setTimeZone(TimeZone.getTimeZone("GMT+0"));//**TimeZone时区,加上这句话就解决啦**
runOnUiThread(new Runnable() {
@Override
public void run() {
Time.setText(sd.format(TIME));
}
});
}/*else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
TIME2 = System.currentTimeMillis();
Time4+=TIME2-Time3;
}*/
}
runOnUiThread(new Runnable() {
@Override
public void run() {
Time.setText("骑行时间");
}
});
}
}).start();
}
}
其他参数
最大速度,最小速度,大致卡路里计算 :
private void bikespeed(){
new Thread(new Runnable() {
@Override
public void run() {
String str1;
String str2;
while(true) {
now = System.currentTimeMillis(); //1000=1s
if(now-last>=1000){
Vnow=(Miles-Mile)*1000/(now-last);
str1 = String.format("%.2f",Vnow);
double two = Double.parseDouble(str1);
//计算卡路里
if(Vnow>0){
Kcal+=wight*((now-last)/120000)/(Vnow/2.22);
Log.e(TAG,"run: "+Kcal+"ppp"+wight*(Miles/1000)*1.036 );
}
//Kcal=wight*(Miles/1000)*1.036;
str2 = String.format("%.2f",Kcal);
double Kcal2=Double.parseDouble(str2);
//更新实时速度
runOnUiThread(new Runnable() {
@Override
public void run() {
V.setText(String.valueOf(two)+"m/s");
K.setText(String.valueOf(Kcal2));
}
});
Log.e(TAG, "run:" +MINV );
//更新最大速度
if(two>MAXV){
MAXV=two;
//第一次计算速度,最大速度等于最小速度
if(first==0){
// first=1;//first=1之后此段代码不再执行,最小速度将始终记录最小值,去掉此段代码可实现短时间内最小速度(可能)
MINV=two;
runOnUiThread(new Runnable() {
@Override
public void run() {
minV.setText(String.valueOf(MINV)+"m/s");
}
});
}
runOnUiThread(new Runnable() {
@Override
public void run() {
maxV.setText(String.valueOf(MAXV)+"m/s");
}
});
}
//更新最小速度,当最小速度显示0的时候表示停止,再移动的时候最小速度不会显示0,为最小运动速度>0
if(two==0){
runOnUiThread(new Runnable() {
@Override
public void run() {
minV.setText(String.valueOf(two)+"m/s");
}
});
}else if(two<MINV&&two!=0){
MINV=two;
runOnUiThread(new Runnable() {
@Override
public void run() {
minV.setText(String.valueOf(MINV)+"m/s");
}
});
}else {
runOnUiThread(new Runnable() {
@Override
public void run() {
minV.setText(String.valueOf(MINV)+"m/s");
}
});
}
last= System.currentTimeMillis();
Mile=Miles;
}
}
}
}).start();
}