47
車輛定位與電子地圖整合服務 Ad id P i E i t Android Programming Environment Nt kO ti i ti Lb Network Optimization Lab Department of Computer Science National Chiao Tung University National Chiao Tung University 1 Outline Outline • Project Objective 主要畫面設計 主要畫面設計 顯示Google Maps 次要畫面設計 次要畫面設計 顯示輸入起終點畫面 查看線上資料 取得繞境資料 取得繞境資料 繞境顯示於Google Maps 2

車輛定位與電子地圖整合服務 Ad idP i E i tAndroid …yi/Courses/NavigationSystems... · –顯示Google Maps •次要畫面設計 –顯示輸入起、終點畫面 •

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

  • 車輛定位與電子地圖整合服務A d id P i E i tAndroid Programming Environment

    N t k O ti i ti L bNetwork Optimization LabDepartment of Computer ScienceNational Chiao Tung UniversityNational Chiao Tung University

    1

    OutlineOutline

    • Project Objective• 主要畫面設計主要畫面設計

    – 顯示Google Maps次要畫面設計• 次要畫面設計– 顯示輸入起、終點畫面

    • 查看線上資料取得繞境資料– 取得繞境資料

    • 繞境顯示於Google Mapsg2

  • Project ObjectiveProject Objective

    在手機上開發車輛導航系統• 在手機上開發車輛導航系統

    • 在Google Maps劃出路徑規畫

    • 提供目前位置服務,並可將目前位置設定• 提供目前位置服務,並可將目前位置設定為起點或終點

    3

    程式架構程式架構

    使用者可按 進入輸入畫面• 使用者可按Menu-Route進入輸入畫面

    • 再輸入畫面輸入起點以及終點

    • 點Search按鈕可在Google Maps上顯示繞境• 點Search按鈕可在Google Maps上顯示繞境

    4

  • 程式執行流程程式執行流程

    5

    顯示Google Maps顯示Google Maps

    新增專案• 新增專案• 取得使用地圖權限取得使用地圖權限

    – 編輯AndroidManifest.xml畫面格式設定• Layout畫面格式設定

    – 編輯main.xml• 建立main class—vnspart1.java建立M L Cl M L j• 建立MapLayout Class—MapLayout.java

    • 在main class將MapLayout 初始化在 將 p y 初始化6

  • 取得使用地圖權限 編輯AndroidManifest xml取得使用地圖權限—編輯AndroidManifest.xml

    • Google Maps選擇性的API,若我們要使用他則必須在application標籤Google Maps選擇性的API 若我們要使用他則必須在application標籤中宣告

  • 建立Main Activity Class(1)建立Main Activity Class(1)

    • Import Head Fileimport android.os.Bundle;import android.view.Menu;p ;import android.view.MenuInflater;import com.google.android.maps.MapActivity;import com.google.android.maps.MapView;

    • extends MapActivity– 此Activity是Google Maps畫面,所以我們必須繼承y g p

    MapActivitypublic class Vnspart1 extends MapActivity{{…}

    9

    建立Main Activity Class(2)建立Main Activity Class(2)

    宣告 物件• 宣告MapView物件– 用來處理地圖資訊

    • 建立Menuprivate MapView mapView;

    – 使用者可以點選Menu來選擇動作 (Route or Quit)protected static final int MENU_SEARCH = Menu.FIRST;

    d i fi l i MENU QUIT M FIRST 1protected static final int MENU_QUIT = Menu.FIRST+1;@Override

    public boolean onCreateOptionsMenu(Menu menu){super.onCreateOptionsMenu(menu);

    dd(0 MENU SEARCH 0 “R ")menu.add(0, MENU_SEARCH , 0, “Route");menu.add(0, MENU_QUIT , 0, “Quit “);return true;}

    10

  • 建立Menu建立Menu

    在 底下建立一資料夾• 在res底下建立一資料夾—menu• 在menu資料夾底下新增 xml檔—menu xml在menu資料夾底下新增.xml檔 menu.xml• 在menu.xml檔中加入

    • 在main class加入下面Code,建立Menu@Override

    bli b l C t O ti M (M ) {public boolean onCreateOptionsMenu(Menu menu) {MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu, menu);return true;}

    11

    }

    建立MapLayout Activity Class(1)建立MapLayout Activity Class(1)• Import DataImport Data

    import com.google.android.maps.MapView;

    import android.content.Context;

    • extends LinearLayout

    p ;import android.util.AttributeSet;import android.widget.LinearLayout;

    extends LinearLayout– 此class是用來顯示地圖畫面,因此必須繼承

    LinearLayoutLinearLayoutpublic class MapLayout extends LinearLayout{…}

    12

  • 建立MapLayout Class(2)建立MapLayout Class(2)

    宣告 物件• 宣告MapView物件– 用來設定地圖資訊用來設定地圖資訊

    必須建立下列函式 並呼叫i it()設定地圖private MapView mapView;

    • 必須建立下列函式,並呼叫init()設定地圖public MapLayout(Context context, AttributeSet attrs) {super(context, attrs);init();}public MapLayout(Context context) {super(context);init();}

    13

    建立MapLayout Activity Class(3)建立MapLayout Activity Class(3)

    建立 函式• 建立init()函式– 用來式定地圖資訊用來式定地圖資訊

    private void init() {…

    – 加入setOrientation(),設定地圖方向,水平或垂}

    直setOrientation(VERTICAL);

    14

  • 建立MapLayout Activity Class(4)建立MapLayout Activity Class(4)

    參數設定– LinearLayout參數設定setLayoutParams(new LinearLayout.LayoutParams(android.view.ViewGroup.LayoutParams.FILL PARENT,

    – 設定金鑰

    y y ( p y _ ,android.view.ViewGroup.LayoutParams.FILL_PARENT));

    – mapView的基本設定this.mapView = new MapView(getContext(),“MAP KEY");

    – 加入mapView至LayOut上

    mapView.setEnabled(true);mapView.setClickable(true);

    加入mapView至LayOut上addView(mapView);

    15

    建立MapLayout Activity Class(5)建立MapLayout Activity Class(5)

    建立 方法• 建立getMapLayout 方法– 使其他Class可使透過此方法取得Google Maps使其他 可使透過此方法取得 g p

    public MapView getMapLayout(){

    return mapView;}

    16

  • 在main class將MapLayout 初始化在main class將MapLayout 初始化

    • 加入宣告MapLayout物件private MapLayout mapLayout;

    • 建立initMap()用來初始化地圖

    private MapLayout mapLayout;

    – 用來初始化地圖private void initMap() {{…}

    17

    在main class將MapLayout 初始化(2)在main class將MapLayout 初始化(2)

    在 加入• 在initMap()加入– 取得MapLayout實體物件取得 p y 實體物件

    – 利用getMapLayout取得地圖mapLayout = (MapLayout)findViewById(R.id.map_layout);

    – 設定地圖為可以觸控移動mapView setBuiltInZoomControls(true);

    mapView = mapLayout.getMapLayout();

    • 在onCreate中加入initMap()mapView.setBuiltInZoomControls(true);

    @Overridepublic void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);setContentView(R.layout.main);initMap();

    18

    }

  • 實驗結果實驗結果

    19

    練習時間練習時間…

    20

  • 建立輸入畫面建立輸入畫面

    在 檔新增• 在AndroidManifesr.xml檔新增search activity• 建立search xml建立search.xml• 建立Search.java• 修改main.xml檔

    – 在地圖上輸出起點以及終點文字在地圖上輸出起點以及終點文字• 顯示目前位置• 設定Menu動作

    21

    輸入畫面設計輸入畫面設計

    輸入方塊

    提示文字

    按鈕

    22

  • 建立search xml建立search.xml

    顯示提示文字;

    • 顯示輸入方塊

    • 再string.xml檔加入文字

    />

    Start pointEnd pointQuery加入文字

    23

    QueryCancel

    設定Menu動作設定Menu動作

    設定 當點下 後會跳至• 設定Menu—Route,當點下Route後會跳至Search Activityy

    • 注意switch case條件設置@Override@public boolean onOptionsItemSelected(MenuItem item){

    switch(item.getItemId()){case R.id.Route :

    Intent intent = new Intent();();intent.setClass(Vnspart2.this , Search.class);startActivityForResult(intent, ACTIVITY_ROUTE);break;

    case R.id.Quit :Qfinish();break;

    }return super.onOptionsItemSelected(item);

    24

    p p ( );}

  • 建立Search java(1)建立Search.java(1)

    • Import Head Fileimport android.app.Activity;import android.content.Intent;p ;import android.content.pm.ActivityInfo;import android.os.Bundle;import android.view.View;import android.widget.Button;

    • 宣告輸入方塊及按鈕

    p g ;import android.widget.EditText;

    private EditText srcText;private EditText destText;private Button buttonSearch;private Button buttonReturn;private Button buttonReturn;

    25

    建立Search java(2)建立Search.java(2)

    畫面設定• 畫面設定setContentView(R.layout.search);

    • 設定畫面顯示方向setRequestedOrientation(ActivityInfo.SCREEN ORIENTATION PORTRAIT);

    • 宣告輸入方塊、方塊初始文字q ( y _ _ );

    T t (EditT t)fi dVi B Id(R id ditT tSt t)srcText =(EditText)findViewById(R.id.editTextStart);srcText.setText("新竹交通大學");destText=(EditText)findViewById(R.id.editTextEnd);destText.setText("新竹清華大學");

    26

  • 建立Search java(3)建立Search.java(3)

    • 宣告按鈕、按鈕文字設定b tt S h (B tt )fi dVi B Id(R id b tt S h)

    • 設定Search按鈕動作

    buttonSearch=(Button)findViewById(R.id.buttonSearch);buttonSearch.setText(R.string.search_button);

    設定Search按鈕動作– 設置OnClickListenerbuttonSearch.setOnClickListener(new Button.OnClickListener(){…});});

    27

    建立Search java(4)建立Search.java(4)• 在OnClickListener中加入下列Code在OnClickListener中加入下列Code

    – 宣告Intent以及Bundle將起點以及終點資料利用 ()– 將起點以及終點資料利用putExtras()

    – 利用sertResult()將資料回傳前個Activity,並設置參數為RESULT_OK

    @Overridepublic void onClick(View v){public void onClick(View v){

    Intent i=new Intent();Bundle b=new Bundle();b.putString("src", srcText.getText().toString());b putString("dest" destText getText() toString());b.putString( dest , destText.getText().toString());i.putExtras(b);setResult(RESULT_OK, i);finish();

    }

    28

    }

  • 建立Search java(3)建立Search.java(3)

    設置 按鈕動作• 設置cancel按鈕動作buttonReturn=(Button)findViewById(R.id.buttonCancel);buttonReturn.setText(R.string.cancel button);( g _ );buttonReturn.setOnClickListener(new Button.OnClickListener(){…});

    • 當按下按鈕時利用setResult(),設置回傳參數RESULT CANCELED

    });

    數RESULT_CANCELED@Override

    public void onClick(View v){setResult(RESULT_CANCELED );finish();}

    29

    修改main xml檔修改main.xml檔

    在 檔內插入 用來顯示• 在main.xml檔內插入TextView用來顯示Search Activity回傳之起點及終點y

    30

  • 修改main java 接收Activity資料修改main.java—接收Activity資料

    再 加入下面 接收• 再main.java加入下面code,接收Search Activity所輸入資料y@Overrideprotected void onActivityResult(int requestCode , int resultCode, Intent data){

    if(requestCode==ACTIVITY ROUTE){if(requestCode ACTIVITY_ROUTE){if(resultCode==RESULT_OK){

    Bundle boude = data.getExtras();view_start.setText(boude.getString("src"));view end setText(boude getString("dest"));view_end.setText(boude.getString( dest ));

    }if(resultCode==RESULT_CANCELED){

    //do nothing}}

    } }

    31

    onActivityResult()說明onActivityResult()說明

    用來接收 回傳資料• 用來接收Activity回傳資料• 參數說明參數說明

    – requestCode:辨識是哪個Activity回傳資料l C d 辨識A i i 回傳的型態– resultCode:辨識Activity回傳的型態

    – Data:儲存上個Activity所回傳的資料

    32

  • 顯示MyLocation顯示MyLocation

    新增 此 繼承• 新增Class-MyOverlayer,此Class繼承MyLocationOverlayy ypublic class MyOverlayer extends MyLocationOverlay{…

    • Override drawMyLocation}

    @Override protected void drawMyLocation(Canvas canvas, MapView mapView, Location lastFix, GeoPoint myLoc, long when) {{ …}

    33

    顯示MyLocation顯示MyLocation• 在drawMyLocation中加入以下判斷式• 在drawMyLocation中加入以下判斷式

    – (lastFix.hasSpeed() && lastFix.getSpeed() > 1)判斷是否移動移動

    if (lastFix.hasSpeed() && lastFix.getSpeed() > 1) {...} else {…}

    • 沒有移動執行下面程式碼}

    – 表示只顯示目前位置,顯示Google內建iconsuper.drawMyLocation(canvas, mapView, lastFix, myLoc, when);

    34

  • 顯示MyLocation—if(l Fi h S d() && l Fi S d() 1)if(lastFix.hasSpeed() && lastFix.getSpeed() > 1)

    • 若有移動則執行下段程式碼– 若無drawable物件,則宣告drawable物件,取得其高度及寬度並新增Point– 若無drawable物件,則宣告drawable物件,取得其高度及寬度並新增Point

    if (drawable == null) { drawable = mapView.getContext().getResources().getDrawable(R.drawable.direction_arrow);accuracyPaint = new Paint();

    id h d bl I i i Wid h()width = drawable.getIntrinsicWidth(); height = drawable.getIntrinsicHeight(); center = new Point();

    }

    • 將目前位置投影至地圖上– 將目前位置投影至地圖上– 旋轉目前畫布,lastFix為上一個位置,旋轉角度為GPS bearing提供g– 設定drawable物件邊界– 將圖形畫置畫布上

    Projection projection = mapView.getProjection(); projection.toPixels(myLoc, center); canvas.rotate(lastFix.getBearing(), center.x , center.y);drawable.setBounds(center.x - width / 2, center.y - height / 2, center.x + width / 2, center.y + height / 2); drawable.draw(canvas);

    35

    super.drawMyLocation(canvas, mapView, lastFix, myLoc, when);

    Override MyLocation程式Override MyLocation程式

    if (lastFix.hasSpeed() && lastFix.getSpeed() > 1) {

    if (drawable == null) {if (drawable == null) { drawable = mapView.getContext().getResources().getDrawable(R.drawable.direction_arrow);accuracyPaint = new Paint(); width = drawable.getIntrinsicWidth(); height = drawable getIntrinsicHeight();height = drawable.getIntrinsicHeight(); center = new Point();

    } Projection projection = mapView.getProjection(); projection toPixels(myLoc center);projection.toPixels(myLoc, center); canvas.rotate(lastFix.getBearing(), center.x , center.y);drawable.setBounds(center.x - width / 2, center.y - height / 2, center.x + width / 2, center.y + height / 2); drawable.draw(canvas);super drawMyLocation(canvas mapView lastFix myLoc when);super.drawMyLocation(canvas, mapView, lastFix, myLoc, when);

    } else {

    super.drawMyLocation(canvas, mapView, lastFix, myLoc, when);}

    36

    }

  • 顯示MyLocation 初始化GPS provider顯示MyLocation—初始化GPS provider

    private void initLocationManager() {locationListener = new LocationListener(){

    public void onLocationChanged(Location location) {// TODO Auto-generated method stubglocationUpdated(location);}public void onProviderDisabled(String provider) {// TODO Auto-generated method stub}public void onProviderEnabled(String provider) {// TODO Auto-generated method stub}public void onStatusChanged(String provider, int status, Bundle extras) {// TODO Auto-generated method stub}

    };locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); locationProvider = LocationManager.GPS_PROVIDER;locationManager.requestLocationUpdates(locationProvider, 0, 0, locationListener);

    }

    37

    在地圖上顯示自己位置在地圖上顯示自己位置

    加入函式如下• 加入函式如下– 新增物件實體MyOverler– 在mapView上加入myOverlay物件

    private void initMyLocation() {myOverlayer = new MyOverlayer(this , mapView);mapView.getOverlays().add(myOverlayer);myOverlayer.enableMyLocation();

    l i i ( bl (){myOverlayer.runOnFirstFix(new Runnable(){public void run(){GeoPoint g = myOverlayer.getMyLocation();mapView.setTraffic(true);

    i C ll () (1 )mapView.getController().setZoom(15);mapView.getController().setCenter(g);}

    }); }

    38

    }

  • 實驗結果實驗結果

    39

    練習時間練習時間…

    40

  • 車輛定位與電子地圖整合服務A d id P i E i tAndroid Programming Environment

    N t k O ti i ti L bNetwork Optimization LabDepartment of Computer ScienceNational Chiao Tung UniversityNational Chiao Tung University

    41

    OUTLINEOUTLINE• 改寫onActivityResult()• MapPoint Class建立• 地名轉經緯度 Route getGeoByAddress• 地名轉經緯度-Route-getGeoByAddress• 取得路徑資料- Route-findRoute• 在地圖上畫上路徑-MapOverlay-draw建立當觸碰到路徑上P i t之方框及事件• 建立當觸碰到路徑上Point之方框及事件– MapOverlay-onTap– MapOverlay-draw– MapOverlay-getHitMapPointMapOverlay getHitMapPoint

    42

  • 改寫vns Acrivity中onActivityResult()

    自 中取得起點以及終點地址• 自Search Acivity中取得起點以及終點地址,呼叫getGeoByAddress()將地址轉經緯度g y ()

    • 利用findRoute取得路徑資料@Override@Overrideprotected void onActivityResult(int requestCode , int resultCode, Intent data){

    if(requestCode==ACTIVITY_ROUTE){if(resultCode==RESULT_OK){

    Bundle b = data.getExtras();T T k T (V h b S (" ") b S ("d ") T LENGTH SHORT)Toast toast = Toast.makeText(Vns.this,b.getString("src")+b.getString("dest"), Toast.LENGTH_SHORT);toast.show();GeoPoint srcGp = route.getGeoByAddress(b.getString("src"));GeoPoint destGp = route.getGeoByAddress(b.getString("dest"));route.findRoute(srcGp, destGp, Color.BLUE, mapView);( p p p )

    }if(resultCode==RESULT_CANCELED){//do nothing}

    }

    43

    } }

    建構路徑上Point之Class建構路徑上Point之Class

    路徑上的 我們加入下列中幾個成員• 路徑上的Point我們加入下列中幾個成員– GeoPoint用來辨認Point的經緯度用來辨認 的經緯度– Type用來便是此點的型態

    Event儲存此Point的事件描述– Event儲存此Point的事件描述• 由以上幾點可建構出下面的Class

    public class MapPoint {private GeoPointpoint;private int type; private String event;public MapPoint(int type String event double latitude double longitude) {public MapPoint(int type , String event ,double latitude, double longitude) {

    this.type = type;this.event = event;point = new GeoPoint((int)(latitude*1e6),(int)(longitude*1e6));

    }}

    44

    }

  • 建構路徑上Point之Class建構路徑上Point之Class

    建立下列幾個方法以便未來取得 上• 建立下列幾個方法以便未來取得MapPoint上資料

    – getPoint()取得此Point的經緯度getEvent()取得此Point的事件描述– getEvent()取得此Point的事件描述

    – getType()取得此Point的型態public GeoPoint getPoint() {

    return point;}public String getEvent() {

    return event;return event;}public int getType() {

    return type;}

    45

    地名轉經緯度地名轉經緯度

    使用者輸入為地址資料• 使用者輸入為地址資料• Google Maps 認知為經緯度資料Google Maps 認知為經緯度資料• 必須將地址轉換成經緯度• Google 提供API Geocoder• 利用Geocoder將地址轉換成經緯度• 利用Geocoder將地址轉換成經緯度• 在Route Class中加入getGeoByAddress()、

    findRoute()

    46

  • 地名轉經緯度-Geocoder Class介紹

    新增一個 物件• 新增一個Geocoder物件– vns為呼叫新增此物件之Activity為呼叫新增此物件之 y– Locale.getDefault()為查詢時所指定的區域。可使用getDefault()默認區域,或者指定地區。使用getDefault()默認區域,或者指定地區

    Geocoder mGeocoder = new Geocoder(vns, Locale.getDefault());

    47

    地名轉經緯度-利用Geocoder轉換地址

    利用 中 方法• 利用Geocoder中getFromLocationName()方法取得轉換後資料

    – strSearchAddress為輸入地址資料1表示回傳的資料個數– 1表示回傳的資料個數

    List lstAddress = mGeocoder.getFromLocationName(strSearchAddress, 1);

    48

  • 地名轉經緯度 取得經緯度地名轉經緯度-取得經緯度

    宣告• 宣告Address Class• 取得Geocoder轉換後List中第0筆資料取得Geocoder轉換後List中第0筆資料• 利用Address Class中,getLatitude()及

    L i d ()方法取得經緯度getLongitude()方法取得經緯度• 將取得之經緯度加入新增之GeoPoint中將取得之經緯度加入新增之G 中

    Address adsLocation = lstAddress.get(0);double geoLatitude = adsLocation.getLatitude()*1E6;double geoLongitude = adsLocation getLongitude()*1E6;double geoLongitude = adsLocation.getLongitude()*1E6;gp = new GeoPoint((int) geoLatitude, (int) geoLongitude);

    49

    地址轉經緯度Code地址轉經緯度Code

    為一段地址• Input為一段地址• Reture 一個GeoPoint (內含地址之經緯度)Reture 一個GeoPoint (內含地址之經緯度)• 加入至Route Class中

    public GeoPoint getGeoByAddress(String strSearchAddress){GeoPoint gp = null;Geocoder mGeocoder = new Geocoder(vns, Locale.getDefault());Li Add l Add G d F L i N ( S hAdd 1)List lstAddress = mGeocoder.getFromLocationName(strSearchAddress, 1);if (!lstAddress.isEmpty()){

    Address adsLocation = lstAddress.get(0);double geoLatitude = adsLocation.getLatitude()*1E6;double geoLongitude = adsLocation.getLongitude()*1E6;gp = new GeoPoint((int) geoLatitude, (int) geoLongitude);

    }return gp;

    }

    50

  • 取得起點至終點路徑取得起點至終點路徑

    手機 必無內建找尋路徑功能• 手機Google Maps必無內建找尋路徑功能• 必須利用網路上Google Maps路徑找尋功能必須利用網路上Google Maps路徑找尋功能• 利用URL連上網路• 取得回傳KML檔• 自KML檔中取得路徑資料• 自KML檔中取得路徑資料

    51

    取得起點至終點路徑 利用URL取得起點至終點路徑-利用URL

    新增一 用來儲存網址• 新增一String用來儲存網址• 仿照下圖中urlString append()依序將往只填仿照下圖中urlString.append()依序將往只填入String中下圖紅色框線 上者為起點 下者為終點• 下圖紅色框線,上者為起點、下者為終點StringBuilder urlString = new StringBuilder();urlString append("http://maps google com/maps?f=d&hl=en");urlString.append("http://maps.google.com/maps?f=d&hl=en");urlString.append("&saddr=");urlString.append( Double.toString((double)src.getLatitudeE6()/1.0E6 ));urlString.append(",");urlString.append( Double.toString((double)src.getLongitudeE6()/1.0E6 ));urlString.append("&daddr=");urlString.append( Double.toString((double)dest.getLatitudeE6()/1.0E6 ));urlString.append(",");urlString.append( Double.toString((double)dest.getLongitudeE6()/1.0E6 ));urlString.append("&ie=UTF8&0&om=0&output=kml");

    52

    g pp ( p );

  • 練習時間練習時間

    練習• 練習1– 在onAvtivityResult()中將轉換後的經緯度顯示在 y ()中將轉換後的經緯度顯示出來

    • 練習2• 練習2– 下圖中網址輸入經緯度後,利用瀏覽器取得

    KML檔KML檔– 交大:緯度=24.789735,經度=120.999792– 清大:緯度=24.794624 ,經度=120.99256

    http://maps.google.com/maps?f=d&hl=en&saddr=緯度,經度&daddr=緯度,經度&ie=UTF8&0&om=0&output=kml

    53

    取得起點至終點路徑 宣告取得起點至終點路徑-宣告

    宣告• 宣告– Document用來處存URL回傳的KML檔用來處存 回傳的 檔– HttpURLConnection 用來與Internet連接

    URL 用來儲存URL網址– URL 用來儲存URL網址Document doc = null;HttpURLConnection urlConnection= null;HttpURLConnection urlConnection null;URL url = null;

    54

  • 取得起點至終點路徑-Connect Internet & Get KML File

    • 新增一URL並將網址存入URL中新增一URL並將網址存入URL中• 設定urlConnerction並與Internet連線• 將回傳的KML檔存入doc中

    url = new URL(urlString toString());url new URL(urlString.toString());urlConnection=(HttpURLConnection)url.openConnection();urlConnection.setRequestMethod("GET");urlConnection.setDoOutput(true);urlConnection.setDoInput(true);

    lC ti t()urlConnection.connect();DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();DocumentBuilder db = dbf.newDocumentBuilder();doc = db.parse(urlConnection.getInputStream());

    55

    取得起點至終點路徑-網址&.kml檔中所需內容

    完整 網址• 完整URL網址http://maps.google.com/maps?f=d&hl=en&saddr=24.789735,120.999792&daddr=24.794624,120.99256&ie=UTF8&0

    • 回傳KML檔中的路徑資料p p g g p , ,

    &om=0&output=kml

    120.999720,24.789550,0.000000 121.000130,24.789410,0.000000 , , , ,121.001500,24.789330,0.000000 121.002680,24.788910,0.000000 121.003070,24.788820,0.000000 121.003240,24.788840,0.000000 121.003350,24.788880,0.000000 121.004410,24.790210,0.000000 121.004460,24.790420,0.000000 121.004410,24.790720,0.000000 121.004430,24.791060,0.000000 121.004480,24.791150,0.000000 121.004480,24.791150,0.000000 121.000020,24.794240,0.000000 120 997080 24 796750 0 000000 120 997080 24 796750 0 000000 120 996600 24 796220 0 000000120.997080,24.796750,0.000000 120.997080,24.796750,0.000000 120.996600,24.796220,0.000000 120.996600,24.796220,0.000000 120.996660,24.796180,0.000000 120.996660,24.796180,0.000000 120.995330,24.794630,0.000000 120.993550,24.794040,0.000000 120.992950,24.793700,0.000000 120.992730,24.793490,0.000000 120.992730,24.793490,0.000000 120.992640,24.794630,0.000000

    56

  • 取得起點至終點路徑-自KML檔中取得經緯度

    取得經緯度字串 可利用下列方式• 取得經緯度字串,可利用下列方式– getElementByTagName表示取得特定的Tagg y g 表示取得特定的 g– Item()表示取得KML中第幾個特定的Tag

    getFirstChild()表示取得下一層Tag 但若第一次– getFirstChild()表示取得下一層Tag,但若第一次呼叫則回傳的是自己

    d l ()取得 中的資料– getNodeValue()取得Tag中的資料

    String path=doc.getElementsByTagName("GeometryCollection").item(0).getFirstChild().getFirstChild().getFirstChild().getNodeValue() ;String path doc.getElementsByTagName( GeometryCollection ).item(0).getFirstChild().getFirstChild().getFirstChild().getNodeValue() ;

    57

    取得起點至終點路徑取得起點至終點路徑

    getElementsByTagName("GeometryCollection").item(0).getFirstChild()

    getFirstChild()

    getFirstChild()

    getNodeValue()

    58

  • 取得起點至終點路徑-在MapViewer中加入方法

    將經緯度加入至 中• 將經緯度加入至List中– 第一個參數代表此點之型態第一個參數代表此點之型態– 第二個參數為此點之事件描述第三 第四為經緯度值– 第三、第四為經緯度值

    – routePath為所宣告知Listprivate List routePath;public void addRoutePath(int type , String eventDescriptor , double lat , double lng) {

    routePath.add(new MapPoint(type ,eventDescriptor, lat , lng));}

    59

    取得起點至終點路徑-在MapViewer中加入方法

    取得儲存此路徑上之點的• 取得儲存此路徑上之點的Listbli Li M P i R P h() {public List getRoutePath() {return routePath;

    }

    • 將儲存路徑上點之List中資料清空bli id l R P h() {public void clearRoutePath() {routePath.clear();

    }

    60

  • 取得起點至終點路徑-加入至routePath中

    將一連串的經緯度資料 拆開• 將一連串的經緯度資料(Path)拆開• 利用addRoutePath()將拆開的經緯度資料放利用addRoutePath()將拆開的經緯度資料放入routePath中未來畫路徑將依據 P h中資料• 未來畫路徑將依據routePath中資料String [] pairs = path.split(" ");for(int i=0;i

  • 取得KML檔中的路徑指示取得KML檔中的路徑指示

    路徑上的指示描述

    此路徑描述的經緯度位置

    63

    取得KML檔中的路徑指示取得KML檔中的路徑指示

    可以利用之前取得路徑經緯度的方法來取• 可以利用之前取得路徑經緯度的方法來取得路徑指示描述

    取得此指示描述的經緯度

    String length = doc.getElementsByTagName("name").item(k).getFirstChild().getNodeValue();

    • 取得此指示描述的經緯度String lngLat = doc.getElementsByTagName("coordinates").item(k).getFirstChild().getNodeValue().split(",");

    64

  • Draw Route In The MapDraw Route In The Map

    在 修改 方法畫路徑• 在MapOverLay修改draw()方法畫路徑• URL回傳為多個經緯度值URL回傳為多個經緯度值• 儲存這多個經緯度值至List中• 利用此List將點標至地圖上• 將點與點之間連線• 將點與點之間連線

    65

    Draw Route In The Map -畫出路徑(1)

    宣告一個 用來將地圖上之點投影• 宣告一個Projection用來將地圖上之點投影至手機上

    宣告 用來控制

    private Projection projection;projection = mapView.getProjection();

    • 宣告Iterator用來控制ListIterator iterator;Iterator iterator;iterator = mapViwer.getRoutePath().iterator();

    66

  • Draw Route In The Map -畫出路徑(2)

    設定 物件顏色 未來畫圖時會根據設• 設定Paint物件顏色,未來畫圖時會根據設定的顏色

    設定畫圖的範圍 參數的設定表示畫圖位

    paint.setColor(Color.BLUE);

    • 設定畫圖的範圍,參數的設定表示畫圖位置及範圍相對於p1

    • 利用drawOval()畫上圓型在上述兩個設定參RectF oval1 = new RectF(p1.x - 10, p1.y - 10,p1.x + 10, p1.y + 10);

    利用drawOval()畫上圓型在上述兩個設定參數

    canvas.drawOval(oval1, paint);

    67

    Draw Route In The Map -畫出路徑(3)

    當第一次讀取到此 時 會先判斷 是• 當第一次讀取到此List時,會先判斷List是否有資料

    • 若List中有資料,則新增一Point(p1)並取得第一筆資料第一筆資料

    • 將第一筆資料利用Projection投影至手機地圖上,並標上起點圖式if (iterator.hasNexiteratort()) {

    MapPoint startPoint = iterator next();MapPoint startPoint iterator.next();Point p1 = new Point();projection.toPixels(startPoint.getPoint(), p1);paint.setColor(Color.BLUE);RectF oval1 = new RectF(p1.x - 10, p1.y - 10,p1.x + 10, p1.y + 10);

    d O l( l1 i t)

    68

    canvas.drawOval(oval1, paint);….

  • Draw Route In The Map -畫出路徑(4)

    繼續往下尋找下一筆資料• 繼續往下尋找下一筆資料• 當此List還有資料,則在新增一Point(iP1),當此List還有資料 則在新增一Point(iP1)再將所讀到下一筆資料投影至手機地圖上

    將兩點做連線• 將兩點做連線while (iterator.hasNext()) {{

    MapPoint iPoint = iterator.next();Point iP1 = new Point();projection.toPixels(iPoint.getPoint(), iP1);paint.setColor(Color.BLUE);paint.setStrokeWidth(5);paint.setAlpha(250);canvas.drawLine(p1.x, p1.y, iP1.x, iP1.y, paint); ….

    }

    69

    }

    Draw Route In The Map -畫出路徑(5)

    判斷 是否還有資料 若無資料 則將剛• 判斷List是否還有資料,若無資料,則將剛剛新增那一點標上結束圖式

    • 若List還有資料,則標上路徑中圖式並將第二個Point (ip1)Assign至第一個Point (p1)二個Point (ip1)Assign至第一個Point (p1)RectF oval2=new RectF(iP1.x - mRadius,iP1.y - mRadius,iP1.x + mRadius,iP1.y + mRadius);if (!iterator.hasNext()) {

    i l ( l )paint.setColor(Color.GREEN); paint.setAlpha(255);

    canvas.drawOval(oval2, paint);} else {

    paint.setColor(Color.BLACK); p ( );paint.setAlpha(255);canvas.drawOval(oval2, paint);p1 = iP1;

    }

    70

  • Draw Route In The Map加入至MapOverlay Class

    @Override@public boolean draw(Canvas canvas, MapView mapView, boolean shadow, long when){

    projection = mapView.getProjection();if (shadow == false){

    Iterator iterator;iterator = mapViewer.getRoutePath().iterator();p g () ();if (iterator.hasNext()) {

    MapPoint startPoint = iterator.next();paint.setAntiAlias(true);Point p1 = new Point();projection.toPixels(startPoint.getPoint(), p1);paint.setColor(Color.BLUE);RectF oval1 = new RectF(p1.x - mRadius, p1.y - mRadius,(p , p y ,p1.x + mRadius, p1.y + mRadius);canvas.drawOval(oval1, paint);while (iterator.hasNext()) {

    MapPoint iPoint = iterator.next();Point iP1 = new Point();projection.toPixels(iPoint.getPoint(), iP1);paint.setColor(Color.GRAY);p ( )paint.setStrokeWidth(5);paint.setAlpha(120);canvas.drawLine(p1.x, p1.y, iP1.x, iP1.y, paint);RectF oval2=new RectF(iP1.x - mRadius,iP1.y - mRadius,iP1.x + mRadius,iP1.y + mRadius);if (!iterator.hasNext()) {

    paint.setColor(Color.GREEN); paint.setAlpha(255);p p ( )canvas.drawOval(oval2, paint);

    } else {paint.setColor(Color.BLACK); paint.setAlpha(255);canvas.drawOval(oval2, paint);p1 = iP1;

    }

    71

    }}

    } return super.draw(canvas, mapView, shadow, when);

    }

    練習時間練習時間

    練習• 練習– 在地圖上畫出起點至終點路徑在地圖上畫出起點至終點路徑

    72

  • 路徑對話框設計路徑對話框設計

    首先需要設計點道路徑上點時所跳出之對• 首先需要設計點道路徑上點時所跳出之對話框

    • 當Touch道路徑上所標示的點,則跳出對話框框

    73

    路徑對話框設計路徑對話框設計

    必須在 中實作或修改下列• 必須在MapOverlay Class中實作或修改下列方法

    – onTap()方法,當我們觸碰到螢幕時所發生的事件件

    – draw()方法,畫出對話方框新增 Hi M P i () 用來確認觸碰的點是否– 新增getHitMapPoint(),用來確認觸碰的點是否在路徑上

    74

  • 路徑對話框設計-設計對話框

    宣告一 儲存 銀幕上位置• 宣告一Point,儲存Touch銀幕上位置– selectedMapPoint表示觸碰在Google Maps上的p 表示觸碰在 g p 上的

    Point– selDestinationOffset表示在螢幕上的一PointselDestinationOffset表示在螢幕上的一Point– 將selectedMapPoint投影在螢幕上的一Point

    selDestinationOffsetselDestinationOffset

    Point selDestinationOffset = new Point();mapView.getProjection().toPixels(selectedMapPoint.getPoint(), selDestinationOffset);

    75

    路徑對話框設計路徑對話框設計

    宣告對話框之長寬度 並新增對話框• 宣告對話框之長寬度,並新增對話框– INFO WINDOW WIDTH 設定方框的寬度_ _ 設定方框的寬度– INFO_WINDOW_HEIGHT設定方框的高度利用上述宣告的參數宣告一個方框– 利用上述宣告的參數宣告一個方框

    int INFO_WINDOW_WIDTH = 200;int INFO_WINDOW_HEIGHT = 20;RectF infoWindowRect = new RectF(0,0,INFO_WINDOW_WIDTH,INFO_WINDOW_HEIGHT);

    76

  • 路徑對話框設計路徑對話框設計

    宣告當 銀幕時 設定對話框會在• 宣告當Touch銀幕時,設定對話框會在Touch螢幕上點的位置

    利用 將對話框畫在螢幕上

    int infoWindowOffsetX = selDestinationOffset.x-INFO_WINDOW_WIDTH/2;int infoWindowOffsetY = selDestinationOffset.y-INFO_WINDOW_HEIGHT-bubbleIcon.getHeight();infoWindowRect.offset(infoWindowOffsetX,infoWindowOffsetY);

    • 利用canvas將對話框畫在螢幕上– infoWindowRect為上面所設定之參數– 2、3個參數為設定方框左右的圓弧度

    getInnerPaint為額外自訂的Paint物件– getInnerPaint為額外自訂的Paint物件canvas.drawRoundRect(infoWindowRect, 5, 5, getInnerPaint());

    77

    路徑對話框設計路徑對話框設計

    設定對話框中可加入文字位置• 設定對話框中可加入文字位置• 取出路徑上點中的事件取出路徑上點中的事件• 利用canvas.drawText()將事件寫入對話框中• getTexPaint為自訂的Paint物件

    i TEXT OFFSET X 10int TEXT_OFFSET_X = 10;int TEXT_OFFSET_Y = 15;String showEvent = selectedMapPoint.getEvent();canvas.drawText(showEvent,infoWindowOffsetX+TEXT_OFFSET_X,infoWindowOffsetY+TEXT_OFFSET_Y,getTextPaint());

    78

  • 路徑對話框設計路徑對話框設計

    自訂的 物件• 自訂的Paint物件– 在此函式中新增一個Paint()物件在此函式中新增一個 ()物件– 利用setARGB()設定顏色

    setAntiAlisa()設定反鋸齒– setAntiAlisa()設定反鋸齒

    public Paint getTextPaint() {if ( textPaint == null) {

    textPaint = new Paint();textPaint.setARGB(255, 255, 255, 255);textPaint.setAntiAlias(true);

    }}return textPaint;

    }

    79

    路徑對話框設計路徑對話框設計

    private void drawInfoWindow(Canvas canvas, MapViewmapView, boolean shadow) {if ( selectedMapPoint != null) {

    if (shadow) {if (shadow) {} else {

    Point selDestinationOffset = new Point();mapView.getProjection().toPixels(selectedMapPoint.getPoint(), selDestinationOffset);int INFO_WINDOW_WIDTH = 200;i O O G 20int INFO_WINDOW_HEIGHT = 20;RectF infoWindowRect = new RectF(0,0,INFO_WINDOW_WIDTH,INFO_WINDOW_HEIGHT);int infoWindowOffsetX = selDestinationOffset.x-INFO_WINDOW_WIDTH/2;int infoWindowOffsetY = selDestinationOffset.y-INFO_WINDOW_HEIGHT-bubbleIcon.getHeight();infoWindowRect.offset(infoWindowOffsetX,infoWindowOffsetY);( , );canvas.drawRoundRect(infoWindowRect, 5, 5, getInnerPaint());canvas.drawRoundRect(infoWindowRect, 5, 5, getBorderPaint());int TEXT_OFFSET_X = 10;int TEXT_OFFSET_Y = 15;String showEvent = selectedMapPoint getEvent();String showEvent selectedMapPoint.getEvent();canvas.drawText(showEvent,infoWindowOffsetX+TEXT_OFFSET_X,infoWindowOffsetY+TEXT_OFFSET_Y,getTextPaint());

    }}

    }

    80

  • onTap()事件onTap()事件• 設定當手觸碰到螢幕時事件• 設定當手觸碰到螢幕時事件• 首先,當手觸碰到螢幕時會去看selectPoint是否為 並將結果存入是否為Null,並將結果存入isRemovePriorPopup中p p

    private MapPoint selectedMapPoint;@Override@public boolean onTap(GeoPoint p, MapView mapView) {

    boolean isRemovePriorPopup = (selectedMapPoint != null);….

    }

    81

    onTap()事件onTap()事件

    呼叫 確認觸碰的點是否是• 呼叫getHitMapPoint()確認觸碰的點是否是路徑上的Point,若是則回傳觸碰到Point

    測試i R P i P OR

    selectedMapPoint = getHitMapPoint(mapView,p);

    • 測試isRemovePriorPopup OR (selectedMapPoint != null),若回傳True則重畫畫面if ( isRemovePriorPopup || selectedMapPoint != null) {if ( isRemovePriorPopup || selectedMapPoint ! null) {

    mapView.invalidate();}

    82

  • onTap()事件onTap()事件

    回傳 畫對• 回傳(return selectedMapPoint != null),畫對話方框時會依據這回傳的值private MapPoint selectedMapPoint;@Overridepublic boolean onTap(GeoPoint p, MapView mapView) {

    boolean isRemovePriorPopup = (selectedMapPoint != null); selectedMapPoint = getHitMapPoint(mapView,p);if ( isRemovePriorPopup || selectedMapPoint != null) {

    mapView.invalidate();}}return selectedMapPoint != null;

    }

    83

    確認觸碰點是否在路徑上確認觸碰點是否在路徑上

    宣告一 表示未來要回傳的• 宣告一MapPoint表示未來要回傳的Point• 宣告一方框表示觸控的區域宣告一方框表示觸控的區域• 宣告一Point-screenCoords用來投影routePath上的P i上的Point

    • 宣告一Point-touchCoords用來投影處空到螢宣告一 C 用來投影處空到螢幕上的PointMapPoint hitMapLocation = null;MapPoint hitMapLocation null;RectF hitTestRecr = new RectF();Point screenCoords = new Point();Point touchCoords = new Point();

    84

  • 確認觸碰點是否在路徑上確認觸碰點是否在路徑上

    在 迴圈中• 在While迴圈中– 將路徑上的Point投影至宣告的screenCoords將路徑上的 投影至宣告的

    設定觸控的範圍

    mapView.getProjection().toPixels(testLocation.getPoint(), screenCoords);

    – 設定觸控的範圍hitTestRecr.set(-mRadius,-mRadius*2,mRadius,0);hitTestRecr.offset(screenCoords.x,screenCoords.y);

    85

    確認觸碰點是否在路徑上確認觸碰點是否在路徑上

    將觸控的 投影至– 將觸控的Point投影至touchCoordsmapView.getProjection().toPixels(tapPoint, touchCoords );

    – 若觸控的Point在設定的方框內,則break跳出迴圈,並將此Point assign至hitMapLocation中圈 並將此Point assign至hitMapLocation中

    if (hitTestRecr.contains(touchCoords .x, touchCoords .y)) {hitMapLocation = testLocation;break;

    – 回傳hitMapLocation

    break;}

    tapPoint = nullreturn hitMapLocation;

    86

  • 確認觸碰點是否在路徑上確認觸碰點是否在路徑上

    確定觸碰的 是否在 上• 確定觸碰的Point是否在routePath上private MapPoint getHitMapPoint(MapViewmapView, GeoPoint tapPoint) {

    MapPoint hitMapLocation = null;MapPoint hitMapLocation null;RectF hitTestRecr = new RectF();Point screenCoords = new Point();Point touchCoords = new Point();Iterator iterator = this.getRoutePath().iterator();

    hil (it t h N t()) {while(iterator.hasNext()) {MapPoint testLocation = iterator.next();mapView.getProjection().toPixels(testLocation.getPoint(), screenCoords);hitTestRecr.set(-mRadius,-mRadius*2,mRadius,0);hitTestRecr.offset(screenCoords.x,screenCoords.y);mapView.getProjection().toPixels(tapPoint, touchCoords );if (hitTestRecr.contains(touchCoords .x, touchCoords .y)) {

    hitMapLocation = testLocation;break;

    }}}tapPoint = nullreturn hitMapLocation;

    }

    87

    練習時間練習時間

    練習時間• 練習時間– 將Route中路徑改為事件路徑將 中路徑改為事件路徑– 加上觸碰對話框

    88

  • 設計泡泡對話框(1)設計泡泡對話框(1)

    外部這一個水平Linear Layout

    是 個是一個Image View

    內部是一個垂直的Linear Layout

    89

    設計泡泡對話框(2)設計泡泡對話框(2)

    外部整個 設定• 外部整個Linearlayout設定

    …..

    90

  • 設計泡泡對話框(3)設計泡泡對話框(3)

    設定當按到 時 的變化• 設定當按到Bulloon時Bullon的變化android:background="@drawable/balloon overlay bg selector"

    • drawable/balloon_overlay_bg_selector為一個XML檔內容如下

    android:background= @drawable/balloon_overlay_bg_selector

    XML檔內容如下

    91

    設計泡泡對話框(4)設計泡泡對話框(4)

    內部垂直• 內部垂直Linearlayout

    92

  • 設計泡泡對話框(5)設計泡泡對話框(5)

    設定• ImageView設定

    93