2011年1月6日 星期四

Android 開發筆記 - 模擬與繪製 GPS 移動路線

mygpspathwithmarker
上頭紅色的是繪製的路徑,藍色點則是模擬時,顯示的 GPS 座標位置,將隨著時間變動。這是根據此篇 Android 開發教學筆記 - 透過 Google Maps API 畫出 GPS 路徑Android 開發教學筆記 - 調整 GPS 座標個數,以提昇路徑繪圖效率 的筆記所做的延伸練習。


給定一段 GPS 紀錄的路徑,經過個數的縮減壓縮並在地圖上完成路徑的繪製,接著想要了解原始 GPS 移動與繪製的路徑差異有多大,所以就有了這個練習。依照原始 GPS 座標資訊,畫製一個 marker 隨著時間擺放到指定的 GPS 位置,這樣的效果除了能確認原始 GPS 路線與壓縮後的路徑位置的差異,也可以了解實際收集到的 GPS 座標是否出現在 Google Maps 繪製的地圖道路。


實做上在 class MyGPSPath 裡,新增並且改變一些 member 宣告方式:


Handler jobs;
final double srcLogs[]={ 48.138050017878413, 16.481179967522621 /* ,  ... */ };
double logs[] = {};
int gps_at;


其中 jobs 用來更新畫面,srcLogs用來紀錄原始的不重複的 GPS 資訊,而 logs 會是壓縮過後的 GPS 個數,而 gps_at 則是等會模擬時,用來紀錄走到那個 GPS 點。


接著在 onCreate 裡,初始化 gps_at, jobs, logs 變數,以及調整 overlay :


gps_at = 0;
jobs = new Handler();

ArrayList<Double> data = new ArrayList<Double>();
for( int i=0; i<srcLogs.length ; ++i )
    data.add( new Double( srcLogs[i] ) );
data = rebuildGSPLogsByAngleCheck( data , 0 );
        
logs = new double[data.size()];
for( int i=0; i<data.size() ; ++i)
    logs[i] = data.get(i).floatValue();

List<com.google.android.maps.Overlay> ol = mapView.getOverlays();
ol.clear();
ol.add(new MyPathOverlay());

MapController mapController = mapView.getController();
if( mapController != null )
{
    mapController.setCenter(new GeoPoint( (int) (srcLogs[0]* 1000000) , (int)(srcLogs[1]* 1000000) ) );
    mapController.setZoom(15);
}

new Thread( new Runnable(){
    public void run()
    {
        gps_at = 2;
        while( gps_at < srcLogs.length )
        {
            try{
                jobs.post( new Runnable(){
                    public void run(){
                        List<com.google.android.maps.Overlay> ol = mapView.getOverlays();
                        while( ol.size() > 1 )
                            ol.remove(1);
                        ol.add(new MyMarkerOverlay( srcLogs[gps_at] , srcLogs[gps_at+1]));
                        
                        GeoPoint in = new GeoPoint((int) (srcLogs[gps_at] * 1000000) , (int) (srcLogs[gps_at+1] * 1000000) );
                        Point out = new Point();
                        mapView.getProjection().toPixels(in, out);
                        int diff = 100;
                        if( out.x < diff || out.y < diff || out.x > mapView.getWidth() - diff || out.y > mapView.getHeight() - diff )
                        {
                            MapController mc = mapView.getController();
                            mc.animateTo(new GeoPoint( (int)(srcLogs[gps_at]*1000000) , (int)(srcLogs[gps_at+1]*1000000 )) );
                        }
                    }
                });
                Thread.sleep(500);
            }
            catch(Exception e)
            {
            }
            gps_at +=2 ;
        }
    }
}).start();


上頭的 MyPathOverlay 則是之前例子中的 MyMapOverlay,在此僅改變成較有意義的名稱,而 onCreate 最後面使用到的 Thread,就是用來模擬 GPS 變化的過程,每 0.5 秒更動 gps_at 時,則重新擺放 MyMarkerOverlay 的位置,並且稍微計算一下,當 Marker 太靠近可視地圖的邊框時,自動調整地圖位置。


另外,還需實做 MyMarkerOverlay,用來標記 GPS 移動:


class MyMarkerOverlay extends com.google.android.maps.Overlay
{
    double at_lat,at_lng;
    MyMarkerOverlay( double _lat, double _lng )
    {
        super();
        at_lat = _lat;
        at_lng = _lng;
    }
    @Override
    public boolean draw(Canvas canvas, MapView mapView,boolean shadow, long when)
    {
        super.draw(canvas, mapView, shadow);
            
        if( !shadow )
        {
            GeoPoint in = new GeoPoint((int) (at_lat * 1000000) , (int) (at_lng * 1000000) );
            Point out = new Point();
            mapView.getProjection().toPixels(in, out);
                
            int r = 6;
            Paint p = new Paint();
                
            p.setColor(Color.BLUE);
            p.setAntiAlias(true);
            RectF oval=new RectF( out.x - r, out.y - r, out.x + r, out.y + r);

            canvas.drawOval(oval, p);
        }
        return true;
    }
}


也可以實做 ItemizedOverlay<OverlayItem> 的方式,好處是用法接近 Web API 的使用,把你想要新增的點,加到下面的 list 即可:


class MyItemMarkerOverlay extends com.google.android.maps.ItemizedOverlay<OverlayItem>
{
    private ArrayList<OverlayItem> myOverlays = new ArrayList<OverlayItem>();
    private Context myContext;
    public MyItemMarkerOverlay(Drawable defaultMarker, Context context)
    {
        super(boundCenterBottom(defaultMarker));
        myContext = context;
    }
    @Override
    protected OverlayItem createItem(int i)
    {
        return myOverlays.get(i);
    }
    @Override
    public int size()
    {
        return myOverlays.size();
    }
    @Override
    protected boolean onTap(int index)
    {
        OverlayItem item = myOverlays.get(index);
        //AlertDialog dialog = new AlertDialog.Builder(MyGPSPath.this).create();
        AlertDialog dialog = new AlertDialog.Builder(myContext).create();
        dialog.setTitle(item.getTitle());
        dialog.setMessage(item.getSnippet());
        dialog.setButton("OK", new DialogInterface.OnClickListener(){
            public void onClick(DialogInterface dialog, int which)
            {
                dialog.cancel();
            }
        } );
        dialog.show();
        return true;
    }
    public void addOverlay(OverlayItem overlay)
    {
        myOverlays.add(overlay);
        populate();
    }
}


用法:


List<com.google.android.maps.Overlay> olay = mapView.getOverlays();
Drawable drawable = this.getResources().getDrawable(R.drawable.icon);
MyItemMarkerOverlay markersOverlay = new MyItemMarkerOverlay( drawable, this ); // new MyItemMarkerOverlay( drawable, MyPGSPath.this );

markersOverlay.addOverlay(
    new OverlayItem(
        new GeoPoint((int) (srcLogs[gps_at] * 1000000) , (int) (srcLogs[gps_at+1] * 1000000)),
        "Title",
        "Snippet"
    )
);

olay.add(markersOverlay);


此練習的 GPS 變化,也可以透過 DDMS 來處理,例如定時 send 一些 GPS 座標等,但我還沒嘗試,看來下個練習方向可以用用看 DDMS 囉。


沒有留言:

張貼留言