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