2017-8-20
前言
又到了周六,打完球测蹲,吃完飯莹捡,閑來無事鬼吵,便把這周干的事情總結(jié)一下扣甲,順便寫個(gè)博客,分享給大家齿椅。
本來說好這周研究三維GIS的琉挖,但是看完官方文檔發(fā)現(xiàn)只有最新的100版本推出了三維GIS,在加上小組長臨時(shí)讓我在2DGIS上研究一下軌跡回放涣脚,經(jīng)過多次改動(dòng)示辈,最終在周四完成了這個(gè)功能,然后周五摸了一天的魚(心里絞痛)遣蚀。
整個(gè)功能通過你給的點(diǎn)的集合(小組長要求每個(gè)點(diǎn)相隔十分鐘矾麻,當(dāng)然,你們可以隨便間隔)芭梯,在地圖上一步一步通過動(dòng)畫繪制出路線险耀,每次到達(dá)一個(gè)點(diǎn)時(shí),會(huì)顯示出在這個(gè)點(diǎn)的時(shí)間玖喘,如果前后2個(gè)點(diǎn)沒有太大的變化(也就是原地不動(dòng))甩牺,每次會(huì)暫停一秒,然后繼續(xù)繪制累奈。
一些ARCGIS基礎(chǔ)的開發(fā)贬派,我就不說了,源碼我也上傳到GItHub上了澎媒,有興趣的童鞋可以去看看搞乏。
好了,閑話就扯到這戒努,開始吧请敦。
效果實(shí)現(xiàn)
源碼在這里:
https://github.com/Hyyzt/DrawTrack/tree/master
讓我們先看看效果圖
從動(dòng)圖中我們可以看到3個(gè)效果,下面我們依次分析下怎么實(shí)現(xiàn)。
首先軌跡回放的動(dòng)畫冬三,是通過handler發(fā)送消息繪制Polyline來實(shí)現(xiàn)的匀油,將所有的點(diǎn)分段繪制,正在走的為一種顏色勾笆,走完的為另一種顏色敌蚜,沒2個(gè)點(diǎn)為一段動(dòng)畫,通過你要求的所有的動(dòng)畫時(shí)間窝爪,計(jì)算每一段動(dòng)畫的繪制速度(100毫秒)弛车,每當(dāng)發(fā)送一個(gè)消息(每100毫秒發(fā)送一次),便增加polyline.lineto()的坐標(biāo)蒲每,以實(shí)現(xiàn)線移動(dòng)的效果纷跛。
每一段動(dòng)畫中一共有2個(gè)polyline,第一個(gè)是正在走的polyline(臨時(shí)線段)邀杏,沒100毫秒畫一次(根據(jù)計(jì)算出的速度)贫奠,當(dāng)每一段動(dòng)畫結(jié)束時(shí),移除第一個(gè)polyline,繪制第二個(gè)動(dòng)畫結(jié)束的polyline(最終線段)望蜡,這樣就實(shí)現(xiàn)了我們軌跡回訪時(shí)得繪制動(dòng)畫唤崭。還有一個(gè)就是移動(dòng)的圖標(biāo),我們通過PictureMarkerSymbol來構(gòu)建自己的移動(dòng)圖標(biāo)脖律,同樣通過handler發(fā)送消息谢肾,根據(jù)速度增加坐標(biāo),每發(fā)送一次小泉,移除上一個(gè)圖片芦疏,重新繪制,實(shí)現(xiàn)移動(dòng)的效果微姊。
顯示這個(gè)點(diǎn)的時(shí)間酸茴,無非就是判斷一下這個(gè)點(diǎn)的經(jīng)緯度時(shí)候是否和增加后的經(jīng)緯度一致,之后再彈出一個(gè)顯示框就可以了柒桑。
以上就是我們實(shí)現(xiàn)的思路了弊决,下面我們來看看代碼和難點(diǎn)。
代碼
在代碼之前魁淳,要說幾個(gè)MapView的繪制時(shí)的特性飘诗。
- 我們繪制時(shí),無論是線路還是圖標(biāo)界逛,我們都是通過底圖的投影坐標(biāo)繪制的昆稿,所以我們需要將經(jīng)緯度坐標(biāo)轉(zhuǎn)換為底圖的投影坐標(biāo),否則無論你的點(diǎn)在哪里息拜,都只會(huì)繪制在一個(gè)地方溉潭。
要將WGS-84轉(zhuǎn)換為投影坐標(biāo)净响,我們可以通過
Point mapPoint = (Point) GeometryEngine.project(wgsPoint ,SpatialReference.create(4326),map.getSpatialReference());
但有時(shí)候(沒錯(cuò),我就是那個(gè)但是)喳瓣,mapview是拿不到SpatialReference的馋贤,所以我們需要通過查閱自己的SpatialReference來轉(zhuǎn)換投影坐標(biāo)。
Point mapPoint = (Point) GeometryEngine.project(wgsPoint ,SpatialReference.create(4326),SpatialReference.create(你的投影坐標(biāo)系參數(shù)));
每次的繪制時(shí)的點(diǎn)都必須轉(zhuǎn)換畏陕,否則會(huì)出現(xiàn)上面的錯(cuò)誤配乓。
我們傳入的數(shù)據(jù)格式是一個(gè)Point(ArcGIS)和一個(gè)表示時(shí)間的字符串的集合,通過下列方式構(gòu)造惠毁,構(gòu)造完成后加入集合犹芹。
new Data("2017-08-17 13:56:00", new Point(116.37489, 40.06644));
-
線和圖片的移動(dòng)時(shí)的動(dòng)畫
之前說過無論是線還是圖片的動(dòng)畫鞠绰,都是通過handler每100毫秒發(fā)送一個(gè)消息腰埂,然后動(dòng)過重繪來實(shí)現(xiàn)的動(dòng)畫,而這2個(gè)動(dòng)畫是同時(shí)完成的蜈膨,所以使用一個(gè)handler來實(shí)現(xiàn)就可以了屿笼。
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
time++;//每一段動(dòng)畫繪制了多少秒
draw();
}
};
private void draw() {
if (!isPause) {//是否暫停
if (index >= pointList.size() - 1)
return;
if (Math.abs(pointList.get(index).getX() - pointList.get(index + 1).getX()) < precision
&& Math.abs(pointList.get(index).getY() - pointList.get(index + 1).getY()) < precision) {
//前一個(gè)點(diǎn)和后一個(gè)點(diǎn)沒有變化,原地跳動(dòng)
index++;
myCallOut.show(wgs2(pointList.get(index)), list.get(index).data);
time = 0;
ClickList.add(pointList.get(index));
drawLines(index);
handler.sendEmptyMessageDelayed(0, 1000);
} else {
if (Math.abs(point.getX() - pointList.get(index + 1).getX()) < 0.000001
&& Math.abs(point.getY() - pointList.get(index + 1).getY()) < 0.000001) {
//上一段動(dòng)畫完成
showResult(pointList.get(index), pointList.get(index + 1));
index++;
myCallOut.show(wgs2(pointList.get(index)), list.get(index).data);
time = 0;
ClickList.add(pointList.get(index))丈挟;
setOnClickListener();//添加點(diǎn)擊事件的監(jiān)聽
drawPoint(index);//畫點(diǎn)
if (index == pointList.size() - 1) {
//路程結(jié)束
Toast.makeText(mainActivity, "路程結(jié)束", Toast.LENGTH_SHORT).show();
onDraw.onFinish();//提供給外部的接口
return;
}
}
drawLines(index);
handler.sendEmptyMessageDelayed(0, 100);
}
}
}
-
在每一段動(dòng)畫結(jié)束后刁卜,移除之前的線段
//畫動(dòng)畫的線,在每一段完成后曙咽,移除 public void drawLines(int index) { carLayer.removeAll(); //計(jì)算每100毫秒的速度 speedX = (pointList.get(index + 1).getX() - pointList.get(index).getX()) / a; speedY = (pointList.get(index + 1).getY() - pointList.get(index).getY()) / a; Log.e("TAG", speedX + "," + speedY); SimpleLineSymbol lineSymbol = new SimpleLineSymbol(color1, 5, SimpleLineSymbol.STYLE.SOLID); point = new Point(pointList.get(index).getX(), pointList.get(index).getY()); Log.e("TAG", "drawLines: " + point.toString()); point.setX(point.getX() + speedX * time); point.setY(point.getY() + speedY * time); if (isFellow) {//是否開啟跟隨模式 mMapView.centerAt(wgs2(point), true); } carGraphic = new Graphic(wgs2(point), car); Polyline polyline = new Polyline(); polyline.startPath(wgs2(pointList.get(index))); Log.e("TAG1", pointList.get(index).toString()); polyline.lineTo(wgs2(point)); Log.e("Po", "drawLines: " + point.toString()); Graphic graphic = new Graphic(polyline, lineSymbol); drawLayer.addGraphic(graphic); carLayer.addGraphic(carGraphic);
}
-
展示最終的結(jié)果
private void showResult(Point start, Point end) { drawLayer.removeAll();//移除之前的臨時(shí)線段 SimpleLineSymbol lineSymbol = new SimpleLineSymbol(color2, 5, SimpleLineSymbol.STYLE.SOLID); Polyline polyline = new Polyline(); polyline.startPath(wgs2(start)); polyline.lineTo(wgs2(end)); Graphic graphic = new Graphic(polyline, lineSymbol); resultLayer.addGraphic(graphic); }
-
設(shè)置點(diǎn)擊事件
public void setOnClickListener() { mMapView.setOnSingleTapListener(new OnSingleTapListener() { @Override public void onSingleTap(float v, float v1) { Point clickPoint = mMapView.toMapPoint(v, v1); Point wgsPoint = (Point) GeometryEngine.project(clickPoint, spatialReference, SpatialReference.create(4326)); for (int i = 0; i < ClickList.size(); i++) { Point p = ClickList.get(i); if (Math.abs(p.getX() - wgsPoint.getX()) < 0.001 && Math.abs(p.getY() - wgsPoint.getY()) < 0.001) { String timeInterval = getTimeInterval(i);//計(jì)算前后2個(gè)點(diǎn)不動(dòng)時(shí)的時(shí)間間隔 myCallOut.show(wgs2(ClickList.get(i)), timeInterval); break; } } } }); } //計(jì)算時(shí)間間隔 private String getTimeInterval(int index) { //通過棧來獲取時(shí)間間隔,每次判斷時(shí)挑辆,當(dāng)前點(diǎn)先入棧例朱,若后一個(gè)點(diǎn)不相同,則清空棧鱼蝉,若相同洒嗤,則將后一個(gè)點(diǎn)入棧,隨后繼續(xù)判斷魁亦,當(dāng)有一個(gè)點(diǎn)與前一個(gè)點(diǎn)不同時(shí)渔隶,拿到棧頂,清空整個(gè)棧洁奈,這樣就拿到了整個(gè)時(shí)間間隔间唉。 String startTime = list.get(index).data; String endTime; //開始遍歷 Stack<Data> stack = new Stack<>(); stack.add(list.get(index)); for (int i = index; i < list.size() - 1; i++) { if (Math.abs(pointList.get(i).getX() - pointList.get(i + 1).getX()) < 0.0001 && Math.abs(pointList.get(i).getY() - pointList.get(i + 1).getY()) < 0.0001) { //前后兩個(gè)點(diǎn)相同 stack.add(list.get(i + 1)); } else { //前后2個(gè)點(diǎn)不同 break; } } if (stack.size() > 1) { endTime = stack.pop().data; stack.removeAllElements(); return startTime + "---" + endTime; } else { return startTime; } }
這樣和邏輯相關(guān)的東西就結(jié)束了,至于彈出時(shí)間的文本框利术,過于簡單也就不多說了呈野,大家可以去源碼中查看。
使用
根據(jù)小組長的要求印叁,要我將整個(gè)過程集成被冒,只暴露出幾個(gè)方法供他使用(>唷!W虻俊)蝗锥,于是變成了最終版本,只需要拷貝2個(gè)類到你的項(xiàng)目率触,在初始化一下然后start就可以使用了玛追。2個(gè)類是:
- MoveHelper,處理整個(gè)邏輯和動(dòng)畫的繪制
- MyCallOut闲延,彈出的文本框
當(dāng)你將2個(gè)類拷貝后痊剖,你直接進(jìn)行初始化
//mapView一定要初始化結(jié)束在傳入進(jìn)去
//初始化MoveHelper時(shí)的參數(shù) Context,MapView,List<Bean>
moveHelper = new MoveHelper(this, mMapView, list);
//移動(dòng)時(shí)的圖標(biāo)(***必須設(shè)置),正在移動(dòng)時(shí)軌跡的顏色,移動(dòng)結(jié)束后軌跡的顏色
moveHelper.setIconAndColor(R.drawable.point, Color.YELLOW, Color.GRAY);
//判斷是否移動(dòng)的精度垒玲,默認(rèn)0.0001
moveHelper.setPrecision(0.0001);
//是否跟隨陆馁,默認(rèn)false
moveHelper.setFellow(false);
//設(shè)置整個(gè)動(dòng)畫的時(shí)間,***必須設(shè)置
moveHelper.setTime(40);
//設(shè)置底圖的空間參考,默認(rèn)為3857,若參數(shù)和底圖不一致合愈,繪制會(huì)出現(xiàn)偏差
moveHelper.setSpatialReference(SpatialReference.create(3857));
//在整個(gè)動(dòng)畫結(jié)束后的邏輯叮贩,在OnFinish()里完成
moveHelper.setOnDraw(new MoveHelper.onDraw() {
@Override
public void onFinish() {
}
});
//注意:開始動(dòng)畫時(shí),你必須要在之前初始化所有的必要參數(shù)佛析,如:動(dòng)畫時(shí)間益老,移動(dòng)時(shí)的圖標(biāo)等等。
moveHelper.start();//開始動(dòng)畫
moveHelper.pause();//暫停動(dòng)畫
這樣就可以開始自己的動(dòng)畫了寸莫。
后記
可能講的不是很清楚捺萌,望大家多見諒。(滑稽.jpg)但是功能和源碼都已經(jīng)實(shí)現(xiàn)了膘茎,可以直接去看源碼桃纯,注釋的也很清楚,也可以直接拿去使用披坏。
到這也就基本結(jié)束了态坦,下次見。